Fix repositories.get_all_permissions, add tests. closes #3546
[arvados.git] / services / keep / src / keep / volume_unix_test.go
1 package main
2
3 import (
4         "bytes"
5         "fmt"
6         "io/ioutil"
7         "os"
8         "testing"
9         "time"
10 )
11
12 func TempUnixVolume(t *testing.T, serialize bool) UnixVolume {
13         d, err := ioutil.TempDir("", "volume_test")
14         if err != nil {
15                 t.Fatal(err)
16         }
17         return MakeUnixVolume(d, serialize)
18 }
19
20 func _teardown(v UnixVolume) {
21         if v.queue != nil {
22                 close(v.queue)
23         }
24         os.RemoveAll(v.root)
25 }
26
27 // store writes a Keep block directly into a UnixVolume, for testing
28 // UnixVolume methods.
29 //
30 func _store(t *testing.T, vol UnixVolume, filename string, block []byte) {
31         blockdir := fmt.Sprintf("%s/%s", vol.root, filename[:3])
32         if err := os.MkdirAll(blockdir, 0755); err != nil {
33                 t.Fatal(err)
34         }
35
36         blockpath := fmt.Sprintf("%s/%s", blockdir, filename)
37         if f, err := os.Create(blockpath); err == nil {
38                 f.Write(block)
39                 f.Close()
40         } else {
41                 t.Fatal(err)
42         }
43 }
44
45 func TestGet(t *testing.T) {
46         v := TempUnixVolume(t, false)
47         defer _teardown(v)
48         _store(t, v, TEST_HASH, TEST_BLOCK)
49
50         buf, err := v.Get(TEST_HASH)
51         if err != nil {
52                 t.Error(err)
53         }
54         if bytes.Compare(buf, TEST_BLOCK) != 0 {
55                 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
56         }
57 }
58
59 func TestGetNotFound(t *testing.T) {
60         v := TempUnixVolume(t, false)
61         defer _teardown(v)
62         _store(t, v, TEST_HASH, TEST_BLOCK)
63
64         buf, err := v.Get(TEST_HASH_2)
65         switch {
66         case os.IsNotExist(err):
67                 break
68         case err == nil:
69                 t.Errorf("Read should have failed, returned %s", string(buf))
70         default:
71                 t.Errorf("Read expected ErrNotExist, got: %s", err)
72         }
73 }
74
75 func TestPut(t *testing.T) {
76         v := TempUnixVolume(t, false)
77         defer _teardown(v)
78
79         err := v.Put(TEST_HASH, TEST_BLOCK)
80         if err != nil {
81                 t.Error(err)
82         }
83         p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
84         if buf, err := ioutil.ReadFile(p); err != nil {
85                 t.Error(err)
86         } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
87                 t.Errorf("Write should have stored %s, did store %s",
88                         string(TEST_BLOCK), string(buf))
89         }
90 }
91
92 func TestPutBadVolume(t *testing.T) {
93         v := TempUnixVolume(t, false)
94         defer _teardown(v)
95
96         os.Chmod(v.root, 000)
97         err := v.Put(TEST_HASH, TEST_BLOCK)
98         if err == nil {
99                 t.Error("Write should have failed")
100         }
101 }
102
103 // Serialization tests: launch a bunch of concurrent
104 //
105 // TODO(twp): show that the underlying Read/Write operations executed
106 // serially and not concurrently. The easiest way to do this is
107 // probably to activate verbose or debug logging, capture log output
108 // and examine it to confirm that Reads and Writes did not overlap.
109 //
110 // TODO(twp): a proper test of I/O serialization requires that a
111 // second request start while the first one is still underway.
112 // Guaranteeing that the test behaves this way requires some tricky
113 // synchronization and mocking.  For now we'll just launch a bunch of
114 // requests simultaenously in goroutines and demonstrate that they
115 // return accurate results.
116 //
117 func TestGetSerialized(t *testing.T) {
118         // Create a volume with I/O serialization enabled.
119         v := TempUnixVolume(t, true)
120         defer _teardown(v)
121
122         _store(t, v, TEST_HASH, TEST_BLOCK)
123         _store(t, v, TEST_HASH_2, TEST_BLOCK_2)
124         _store(t, v, TEST_HASH_3, TEST_BLOCK_3)
125
126         sem := make(chan int)
127         go func(sem chan int) {
128                 buf, err := v.Get(TEST_HASH)
129                 if err != nil {
130                         t.Errorf("err1: %v", err)
131                 }
132                 if bytes.Compare(buf, TEST_BLOCK) != 0 {
133                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
134                 }
135                 sem <- 1
136         }(sem)
137
138         go func(sem chan int) {
139                 buf, err := v.Get(TEST_HASH_2)
140                 if err != nil {
141                         t.Errorf("err2: %v", err)
142                 }
143                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
144                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
145                 }
146                 sem <- 1
147         }(sem)
148
149         go func(sem chan int) {
150                 buf, err := v.Get(TEST_HASH_3)
151                 if err != nil {
152                         t.Errorf("err3: %v", err)
153                 }
154                 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
155                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
156                 }
157                 sem <- 1
158         }(sem)
159
160         // Wait for all goroutines to finish
161         for done := 0; done < 3; {
162                 done += <-sem
163         }
164 }
165
166 func TestPutSerialized(t *testing.T) {
167         // Create a volume with I/O serialization enabled.
168         v := TempUnixVolume(t, true)
169         defer _teardown(v)
170
171         sem := make(chan int)
172         go func(sem chan int) {
173                 err := v.Put(TEST_HASH, TEST_BLOCK)
174                 if err != nil {
175                         t.Errorf("err1: %v", err)
176                 }
177                 sem <- 1
178         }(sem)
179
180         go func(sem chan int) {
181                 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
182                 if err != nil {
183                         t.Errorf("err2: %v", err)
184                 }
185                 sem <- 1
186         }(sem)
187
188         go func(sem chan int) {
189                 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
190                 if err != nil {
191                         t.Errorf("err3: %v", err)
192                 }
193                 sem <- 1
194         }(sem)
195
196         // Wait for all goroutines to finish
197         for done := 0; done < 2; {
198                 done += <-sem
199         }
200
201         // Double check that we actually wrote the blocks we expected to write.
202         buf, err := v.Get(TEST_HASH)
203         if err != nil {
204                 t.Errorf("Get #1: %v", err)
205         }
206         if bytes.Compare(buf, TEST_BLOCK) != 0 {
207                 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
208         }
209
210         buf, err = v.Get(TEST_HASH_2)
211         if err != nil {
212                 t.Errorf("Get #2: %v", err)
213         }
214         if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
215                 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
216         }
217
218         buf, err = v.Get(TEST_HASH_3)
219         if err != nil {
220                 t.Errorf("Get #3: %v", err)
221         }
222         if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
223                 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
224         }
225 }
226
227 func TestIsFull(t *testing.T) {
228         v := TempUnixVolume(t, false)
229         defer _teardown(v)
230
231         full_path := v.root + "/full"
232         now := fmt.Sprintf("%d", time.Now().Unix())
233         os.Symlink(now, full_path)
234         if !v.IsFull() {
235                 t.Errorf("%s: claims not to be full", v)
236         }
237         os.Remove(full_path)
238
239         // Test with an expired /full link.
240         expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
241         os.Symlink(expired, full_path)
242         if v.IsFull() {
243                 t.Errorf("%s: should no longer be full", v)
244         }
245 }