13 func TempUnixVolume(t *testing.T, serialize bool) UnixVolume {
14 d, err := ioutil.TempDir("", "volume_test")
18 return MakeUnixVolume(d, serialize)
21 func _teardown(v UnixVolume) {
28 // store writes a Keep block directly into a UnixVolume, for testing
29 // UnixVolume methods.
31 func _store(t *testing.T, vol UnixVolume, filename string, block []byte) {
32 blockdir := fmt.Sprintf("%s/%s", vol.root, filename[:3])
33 if err := os.MkdirAll(blockdir, 0755); err != nil {
37 blockpath := fmt.Sprintf("%s/%s", blockdir, filename)
38 if f, err := os.Create(blockpath); err == nil {
46 func TestGet(t *testing.T) {
47 v := TempUnixVolume(t, false)
49 _store(t, v, TEST_HASH, TEST_BLOCK)
51 buf, err := v.Get(TEST_HASH)
55 if bytes.Compare(buf, TEST_BLOCK) != 0 {
56 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
60 func TestGetNotFound(t *testing.T) {
61 v := TempUnixVolume(t, false)
63 _store(t, v, TEST_HASH, TEST_BLOCK)
65 buf, err := v.Get(TEST_HASH_2)
67 case os.IsNotExist(err):
70 t.Errorf("Read should have failed, returned %s", string(buf))
72 t.Errorf("Read expected ErrNotExist, got: %s", err)
76 func TestPut(t *testing.T) {
77 v := TempUnixVolume(t, false)
80 err := v.Put(TEST_HASH, TEST_BLOCK)
84 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
85 if buf, err := ioutil.ReadFile(p); err != nil {
87 } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
88 t.Errorf("Write should have stored %s, did store %s",
89 string(TEST_BLOCK), string(buf))
93 func TestPutBadVolume(t *testing.T) {
94 v := TempUnixVolume(t, false)
98 err := v.Put(TEST_HASH, TEST_BLOCK)
100 t.Error("Write should have failed")
105 // Test that when applying PUT to a block that already exists,
106 // the block's modification time is updated.
107 func TestPutTouch(t *testing.T) {
108 v := TempUnixVolume(t, false)
111 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
115 // We'll verify { t0 < threshold < t1 }, where t0 is the
116 // existing block's timestamp on disk before Put() and t1 is
117 // its timestamp after Put().
118 threshold := time.Now().Add(-time.Second)
120 // Set the stored block's mtime far enough in the past that we
121 // can see the difference between "timestamp didn't change"
122 // and "timestamp granularity is too low".
124 oldtime := time.Now().Add(-20 * time.Second).Unix()
125 if err := syscall.Utime(v.blockPath(TEST_HASH),
126 &syscall.Utimbuf{oldtime, oldtime}); err != nil {
130 // Make sure v.Mtime() agrees the above Utime really worked.
131 if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
132 t.Errorf("Setting mtime failed: %v, %v", t0, err)
136 // Write the same block again.
137 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
141 // Verify threshold < t1
142 t1, err := v.Mtime(TEST_HASH)
146 if t1.Before(threshold) {
147 t.Errorf("t1 %v must be >= threshold %v after v.Put ",
152 // Serialization tests: launch a bunch of concurrent
154 // TODO(twp): show that the underlying Read/Write operations executed
155 // serially and not concurrently. The easiest way to do this is
156 // probably to activate verbose or debug logging, capture log output
157 // and examine it to confirm that Reads and Writes did not overlap.
159 // TODO(twp): a proper test of I/O serialization requires that a
160 // second request start while the first one is still underway.
161 // Guaranteeing that the test behaves this way requires some tricky
162 // synchronization and mocking. For now we'll just launch a bunch of
163 // requests simultaenously in goroutines and demonstrate that they
164 // return accurate results.
166 func TestGetSerialized(t *testing.T) {
167 // Create a volume with I/O serialization enabled.
168 v := TempUnixVolume(t, true)
171 _store(t, v, TEST_HASH, TEST_BLOCK)
172 _store(t, v, TEST_HASH_2, TEST_BLOCK_2)
173 _store(t, v, TEST_HASH_3, TEST_BLOCK_3)
175 sem := make(chan int)
176 go func(sem chan int) {
177 buf, err := v.Get(TEST_HASH)
179 t.Errorf("err1: %v", err)
181 if bytes.Compare(buf, TEST_BLOCK) != 0 {
182 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
187 go func(sem chan int) {
188 buf, err := v.Get(TEST_HASH_2)
190 t.Errorf("err2: %v", err)
192 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
193 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
198 go func(sem chan int) {
199 buf, err := v.Get(TEST_HASH_3)
201 t.Errorf("err3: %v", err)
203 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
204 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
209 // Wait for all goroutines to finish
210 for done := 0; done < 3; {
215 func TestPutSerialized(t *testing.T) {
216 // Create a volume with I/O serialization enabled.
217 v := TempUnixVolume(t, true)
220 sem := make(chan int)
221 go func(sem chan int) {
222 err := v.Put(TEST_HASH, TEST_BLOCK)
224 t.Errorf("err1: %v", err)
229 go func(sem chan int) {
230 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
232 t.Errorf("err2: %v", err)
237 go func(sem chan int) {
238 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
240 t.Errorf("err3: %v", err)
245 // Wait for all goroutines to finish
246 for done := 0; done < 2; {
250 // Double check that we actually wrote the blocks we expected to write.
251 buf, err := v.Get(TEST_HASH)
253 t.Errorf("Get #1: %v", err)
255 if bytes.Compare(buf, TEST_BLOCK) != 0 {
256 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
259 buf, err = v.Get(TEST_HASH_2)
261 t.Errorf("Get #2: %v", err)
263 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
264 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
267 buf, err = v.Get(TEST_HASH_3)
269 t.Errorf("Get #3: %v", err)
271 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
272 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
276 func TestIsFull(t *testing.T) {
277 v := TempUnixVolume(t, false)
280 full_path := v.root + "/full"
281 now := fmt.Sprintf("%d", time.Now().Unix())
282 os.Symlink(now, full_path)
284 t.Errorf("%s: claims not to be full", v)
288 // Test with an expired /full link.
289 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
290 os.Symlink(expired, full_path)
292 t.Errorf("%s: should no longer be full", v)