blob: 4e96ef97dee2cb9581cd714f228196812cf52219 [file] [log] [blame]
Ankur18663bd2014-11-11 18:39:47 -08001package filelocker
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "os"
Ankur1d8f3c52014-11-19 15:39:49 -08009 "syscall"
Ankur18663bd2014-11-11 18:39:47 -080010 "testing"
11 "time"
12
Jiri Simsa764efb72014-12-25 20:57:03 -080013 "v.io/core/veyron/lib/expect"
14 "v.io/core/veyron/lib/modules"
Ankur18663bd2014-11-11 18:39:47 -080015)
16
17func init() {
Ankur1d8f3c52014-11-19 15:39:49 -080018 modules.RegisterChild("testLockChild", "", testLockChild)
Ankur18663bd2014-11-11 18:39:47 -080019}
20
21func TestHelperProcess(t *testing.T) {
22 modules.DispatchInTest()
23}
24
25func newFile() string {
26 file, err := ioutil.TempFile("", "test_lock_file")
27 if err != nil {
28 panic(err)
29 }
30 defer file.Close()
31 return file.Name()
32}
33
34func grabbedLock(lock <-chan bool) bool {
35 select {
36 case <-lock:
37 return true
38 case <-time.After(100 * time.Millisecond):
39 return false
40 }
41}
42
Ankur1d8f3c52014-11-19 15:39:49 -080043func testLockChild(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
Ankur18663bd2014-11-11 18:39:47 -080044 // Lock the file
45 unlocker, err := Lock(args[1])
46 if err != nil {
47 return fmt.Errorf("Lock failed: %v", err)
48 }
49 fmt.Fprintf(stdout, "locked\n")
50
51 // Wait for message from parent to unlock the file.
52 scanner := bufio.NewScanner(stdin)
53 if scanned := scanner.Scan(); !scanned || (scanned && scanner.Text() != "unlock") {
54 unlocker.Unlock()
55 return fmt.Errorf("unexpected message read from stdout, expected %v", "unlock")
56 }
57 unlocker.Unlock()
58 fmt.Fprintf(stdout, "unlocked\n")
59 return nil
60}
61
Ankur1d8f3c52014-11-19 15:39:49 -080062func TestLockInterProcess(t *testing.T) {
Ankur18663bd2014-11-11 18:39:47 -080063 filepath := newFile()
64 defer os.Remove(filepath)
65
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -080066 sh, err := modules.NewShell(nil)
67 if err != nil {
68 t.Fatalf("unexpected error: %s", err)
69 }
Ankur18663bd2014-11-11 18:39:47 -080070 defer sh.Cleanup(os.Stderr, os.Stderr)
Ankur1d8f3c52014-11-19 15:39:49 -080071 h, err := sh.Start("testLockChild", nil, filepath)
Ankur18663bd2014-11-11 18:39:47 -080072 if err != nil {
73 t.Fatalf("sh.Start failed: %v", err)
74 }
75 s := expect.NewSession(t, h.Stdout(), time.Minute)
76
77 // Test that child grabbed the lock.
78 s.Expect("locked")
79
80 // Test that parent cannot grab the lock, and then send a message
81 // to the child to release the lock.
82 lock := make(chan bool)
83 go func() {
84 unlocker, err := Lock(filepath)
85 if err != nil {
86 t.Fatalf("Lock failed: %v", err)
87 }
88 close(lock)
89 unlocker.Unlock()
90 }()
91 if grabbedLock(lock) {
92 t.Fatal("Parent process unexpectedly grabbed lock before child released it")
93 }
94
95 // Test that the parent can grab the lock after the child has released it.
96 h.Stdin().Write([]byte("unlock\n"))
97 s.Expect("unlocked")
98 if !grabbedLock(lock) {
99 t.Fatal("Parent process failed to grab the lock after child released it")
100 }
101 s.ExpectEOF()
102}
103
Ankur1d8f3c52014-11-19 15:39:49 -0800104func TestLockIntraProcess(t *testing.T) {
Ankur18663bd2014-11-11 18:39:47 -0800105 filepath := newFile()
106 defer os.Remove(filepath)
107
108 // Grab the lock within this goroutine and test that
109 // another goroutine blocks when trying to grab the lock.
110 unlocker, err := Lock(filepath)
111 if err != nil {
112 t.Fatalf("Lock failed: %v", err)
113 }
114 lock := make(chan bool)
115 go func() {
116 unlocker, err := Lock(filepath)
117 if err != nil {
118 t.Fatalf("Lock failed: %v", err)
119 }
120 close(lock)
121 unlocker.Unlock()
122 }()
123 if grabbedLock(lock) {
124 unlocker.Unlock()
125 t.Fatal("Another goroutine unexpectedly grabbed lock before this goroutine released it")
126 }
127
128 // Release the lock and test that the goroutine can grab it.
129 unlocker.Unlock()
130 if !grabbedLock(lock) {
131 t.Fatal("Another goroutine failed to grab the lock after this goroutine released it")
132 }
133}
Ankur1d8f3c52014-11-19 15:39:49 -0800134
135func TestTryLock(t *testing.T) {
136 filepath := newFile()
137 defer os.Remove(filepath)
138
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800139 sh, err := modules.NewShell(nil)
140 if err != nil {
141 t.Fatalf("unexpected error: %s", err)
142 }
Ankur1d8f3c52014-11-19 15:39:49 -0800143 defer sh.Cleanup(os.Stderr, os.Stderr)
144 h, err := sh.Start("testLockChild", nil, filepath)
145 if err != nil {
146 t.Fatalf("sh.Start failed: %v", err)
147 }
148 s := expect.NewSession(t, h.Stdout(), time.Minute)
149
150 // Test that child grabbed the lock.
151 s.Expect("locked")
152
153 // Test that parent cannot grab the lock, and then send a message
154 // to the child to release the lock.
155 if _, err := TryLock(filepath); err != syscall.EWOULDBLOCK {
156 t.Fatal("TryLock returned error: %v, want: %v", err, syscall.EWOULDBLOCK)
157 }
158
159 // Test that the parent can grab the lock after the child has released it.
160 h.Stdin().Write([]byte("unlock\n"))
161 s.Expect("unlocked")
162 if _, err = TryLock(filepath); err != nil {
163 t.Fatalf("TryLock failed: %v", err)
164 }
165 s.ExpectEOF()
166}