8554: Add EmptyTrash api to Volume and implementation in volume_unix. Add emptyTrash...
[arvados.git] / services / keepstore / volume_generic_test.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/md5"
6         "fmt"
7         "os"
8         "regexp"
9         "sort"
10         "strings"
11         "time"
12
13         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
14 )
15
16 type TB interface {
17         Error(args ...interface{})
18         Errorf(format string, args ...interface{})
19         Fail()
20         FailNow()
21         Failed() bool
22         Fatal(args ...interface{})
23         Fatalf(format string, args ...interface{})
24         Log(args ...interface{})
25         Logf(format string, args ...interface{})
26 }
27
28 // A TestableVolumeFactory returns a new TestableVolume. The factory
29 // function, and the TestableVolume it returns, can use "t" to write
30 // logs, fail the current test, etc.
31 type TestableVolumeFactory func(t TB) TestableVolume
32
33 // DoGenericVolumeTests runs a set of tests that every TestableVolume
34 // is expected to pass. It calls factory to create a new TestableVolume
35 // for each test case, to avoid leaking state between tests.
36 func DoGenericVolumeTests(t TB, factory TestableVolumeFactory) {
37         testGet(t, factory)
38         testGetNoSuchBlock(t, factory)
39
40         testCompareNonexistent(t, factory)
41         testCompareSameContent(t, factory, TestHash, TestBlock)
42         testCompareSameContent(t, factory, EmptyHash, EmptyBlock)
43         testCompareWithCollision(t, factory, TestHash, TestBlock, []byte("baddata"))
44         testCompareWithCollision(t, factory, TestHash, TestBlock, EmptyBlock)
45         testCompareWithCollision(t, factory, EmptyHash, EmptyBlock, TestBlock)
46         testCompareWithCorruptStoredData(t, factory, TestHash, TestBlock, []byte("baddata"))
47         testCompareWithCorruptStoredData(t, factory, TestHash, TestBlock, EmptyBlock)
48         testCompareWithCorruptStoredData(t, factory, EmptyHash, EmptyBlock, []byte("baddata"))
49
50         testPutBlockWithSameContent(t, factory, TestHash, TestBlock)
51         testPutBlockWithSameContent(t, factory, EmptyHash, EmptyBlock)
52         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], arvadostest.MD5CollisionData[1])
53         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, EmptyBlock, arvadostest.MD5CollisionData[0])
54         testPutBlockWithDifferentContent(t, factory, arvadostest.MD5CollisionMD5, arvadostest.MD5CollisionData[0], EmptyBlock)
55         testPutBlockWithDifferentContent(t, factory, EmptyHash, EmptyBlock, arvadostest.MD5CollisionData[0])
56         testPutMultipleBlocks(t, factory)
57
58         testPutAndTouch(t, factory)
59         testTouchNoSuchBlock(t, factory)
60
61         testMtimeNoSuchBlock(t, factory)
62
63         testIndexTo(t, factory)
64
65         testDeleteNewBlock(t, factory)
66         testDeleteOldBlock(t, factory)
67         testDeleteNoSuchBlock(t, factory)
68
69         testStatus(t, factory)
70
71         testString(t, factory)
72
73         testUpdateReadOnly(t, factory)
74
75         testGetConcurrent(t, factory)
76         testPutConcurrent(t, factory)
77
78         testPutFullBlock(t, factory)
79
80         testTrashUntrash(t, factory)
81         testEmptyTrashTrashLifetime0s(t, factory)
82         testEmptyTrashTrashLifetime3600s(t, factory)
83         testEmptyTrashTrashLifetime1s(t, factory)
84 }
85
86 // Put a test block, get it and verify content
87 // Test should pass for both writable and read-only volumes
88 func testGet(t TB, factory TestableVolumeFactory) {
89         v := factory(t)
90         defer v.Teardown()
91
92         v.PutRaw(TestHash, TestBlock)
93
94         buf, err := v.Get(TestHash)
95         if err != nil {
96                 t.Fatal(err)
97         }
98
99         bufs.Put(buf)
100
101         if bytes.Compare(buf, TestBlock) != 0 {
102                 t.Errorf("expected %s, got %s", string(TestBlock), string(buf))
103         }
104 }
105
106 // Invoke get on a block that does not exist in volume; should result in error
107 // Test should pass for both writable and read-only volumes
108 func testGetNoSuchBlock(t TB, factory TestableVolumeFactory) {
109         v := factory(t)
110         defer v.Teardown()
111
112         if _, err := v.Get(TestHash2); err == nil {
113                 t.Errorf("Expected error while getting non-existing block %v", TestHash2)
114         }
115 }
116
117 // Compare() should return os.ErrNotExist if the block does not exist.
118 // Otherwise, writing new data causes CompareAndTouch() to generate
119 // error logs even though everything is working fine.
120 func testCompareNonexistent(t TB, factory TestableVolumeFactory) {
121         v := factory(t)
122         defer v.Teardown()
123
124         err := v.Compare(TestHash, TestBlock)
125         if err != os.ErrNotExist {
126                 t.Errorf("Got err %T %q, expected os.ErrNotExist", err, err)
127         }
128 }
129
130 // Put a test block and compare the locator with same content
131 // Test should pass for both writable and read-only volumes
132 func testCompareSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
133         v := factory(t)
134         defer v.Teardown()
135
136         v.PutRaw(testHash, testData)
137
138         // Compare the block locator with same content
139         err := v.Compare(testHash, testData)
140         if err != nil {
141                 t.Errorf("Got err %q, expected nil", err)
142         }
143 }
144
145 // Test behavior of Compare() when stored data matches expected
146 // checksum but differs from new data we need to store. Requires
147 // testHash = md5(testDataA).
148 //
149 // Test should pass for both writable and read-only volumes
150 func testCompareWithCollision(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
151         v := factory(t)
152         defer v.Teardown()
153
154         v.PutRaw(testHash, testDataA)
155
156         // Compare the block locator with different content; collision
157         err := v.Compare(TestHash, testDataB)
158         if err == nil {
159                 t.Errorf("Got err nil, expected error due to collision")
160         }
161 }
162
163 // Test behavior of Compare() when stored data has become
164 // corrupted. Requires testHash = md5(testDataA) != md5(testDataB).
165 //
166 // Test should pass for both writable and read-only volumes
167 func testCompareWithCorruptStoredData(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
168         v := factory(t)
169         defer v.Teardown()
170
171         v.PutRaw(TestHash, testDataB)
172
173         err := v.Compare(testHash, testDataA)
174         if err == nil || err == CollisionError {
175                 t.Errorf("Got err %+v, expected non-collision error", err)
176         }
177 }
178
179 // Put a block and put again with same content
180 // Test is intended for only writable volumes
181 func testPutBlockWithSameContent(t TB, factory TestableVolumeFactory, testHash string, testData []byte) {
182         v := factory(t)
183         defer v.Teardown()
184
185         if v.Writable() == false {
186                 return
187         }
188
189         err := v.Put(testHash, testData)
190         if err != nil {
191                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
192         }
193
194         err = v.Put(testHash, testData)
195         if err != nil {
196                 t.Errorf("Got err putting block second time %q: %q, expected nil", TestBlock, err)
197         }
198 }
199
200 // Put a block and put again with different content
201 // Test is intended for only writable volumes
202 func testPutBlockWithDifferentContent(t TB, factory TestableVolumeFactory, testHash string, testDataA, testDataB []byte) {
203         v := factory(t)
204         defer v.Teardown()
205
206         if v.Writable() == false {
207                 return
208         }
209
210         v.PutRaw(testHash, testDataA)
211
212         putErr := v.Put(testHash, testDataB)
213         buf, getErr := v.Get(testHash)
214         if putErr == nil {
215                 // Put must not return a nil error unless it has
216                 // overwritten the existing data.
217                 if bytes.Compare(buf, testDataB) != 0 {
218                         t.Errorf("Put succeeded but Get returned %+q, expected %+q", buf, testDataB)
219                 }
220         } else {
221                 // It is permissible for Put to fail, but it must
222                 // leave us with either the original data, the new
223                 // data, or nothing at all.
224                 if getErr == nil && bytes.Compare(buf, testDataA) != 0 && bytes.Compare(buf, testDataB) != 0 {
225                         t.Errorf("Put failed but Get returned %+q, which is neither %+q nor %+q", buf, testDataA, testDataB)
226                 }
227         }
228         if getErr == nil {
229                 bufs.Put(buf)
230         }
231 }
232
233 // Put and get multiple blocks
234 // Test is intended for only writable volumes
235 func testPutMultipleBlocks(t TB, factory TestableVolumeFactory) {
236         v := factory(t)
237         defer v.Teardown()
238
239         if v.Writable() == false {
240                 return
241         }
242
243         err := v.Put(TestHash, TestBlock)
244         if err != nil {
245                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock, err)
246         }
247
248         err = v.Put(TestHash2, TestBlock2)
249         if err != nil {
250                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock2, err)
251         }
252
253         err = v.Put(TestHash3, TestBlock3)
254         if err != nil {
255                 t.Errorf("Got err putting block %q: %q, expected nil", TestBlock3, err)
256         }
257
258         data, err := v.Get(TestHash)
259         if err != nil {
260                 t.Error(err)
261         } else {
262                 if bytes.Compare(data, TestBlock) != 0 {
263                         t.Errorf("Block present, but got %+q, expected %+q", data, TestBlock)
264                 }
265                 bufs.Put(data)
266         }
267
268         data, err = v.Get(TestHash2)
269         if err != nil {
270                 t.Error(err)
271         } else {
272                 if bytes.Compare(data, TestBlock2) != 0 {
273                         t.Errorf("Block present, but got %+q, expected %+q", data, TestBlock2)
274                 }
275                 bufs.Put(data)
276         }
277
278         data, err = v.Get(TestHash3)
279         if err != nil {
280                 t.Error(err)
281         } else {
282                 if bytes.Compare(data, TestBlock3) != 0 {
283                         t.Errorf("Block present, but to %+q, expected %+q", data, TestBlock3)
284                 }
285                 bufs.Put(data)
286         }
287 }
288
289 // testPutAndTouch
290 //   Test that when applying PUT to a block that already exists,
291 //   the block's modification time is updated.
292 // Test is intended for only writable volumes
293 func testPutAndTouch(t TB, factory TestableVolumeFactory) {
294         v := factory(t)
295         defer v.Teardown()
296
297         if v.Writable() == false {
298                 return
299         }
300
301         if err := v.Put(TestHash, TestBlock); err != nil {
302                 t.Error(err)
303         }
304
305         // We'll verify { t0 < threshold < t1 }, where t0 is the
306         // existing block's timestamp on disk before Put() and t1 is
307         // its timestamp after Put().
308         threshold := time.Now().Add(-time.Second)
309
310         // Set the stored block's mtime far enough in the past that we
311         // can see the difference between "timestamp didn't change"
312         // and "timestamp granularity is too low".
313         v.TouchWithDate(TestHash, time.Now().Add(-20*time.Second))
314
315         // Make sure v.Mtime() agrees the above Utime really worked.
316         if t0, err := v.Mtime(TestHash); err != nil || t0.IsZero() || !t0.Before(threshold) {
317                 t.Errorf("Setting mtime failed: %v, %v", t0, err)
318         }
319
320         // Write the same block again.
321         if err := v.Put(TestHash, TestBlock); err != nil {
322                 t.Error(err)
323         }
324
325         // Verify threshold < t1
326         if t1, err := v.Mtime(TestHash); err != nil {
327                 t.Error(err)
328         } else if t1.Before(threshold) {
329                 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
330         }
331 }
332
333 // Touching a non-existing block should result in error.
334 // Test should pass for both writable and read-only volumes
335 func testTouchNoSuchBlock(t TB, factory TestableVolumeFactory) {
336         v := factory(t)
337         defer v.Teardown()
338
339         if err := v.Touch(TestHash); err == nil {
340                 t.Error("Expected error when attempted to touch a non-existing block")
341         }
342 }
343
344 // Invoking Mtime on a non-existing block should result in error.
345 // Test should pass for both writable and read-only volumes
346 func testMtimeNoSuchBlock(t TB, factory TestableVolumeFactory) {
347         v := factory(t)
348         defer v.Teardown()
349
350         if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
351                 t.Error("Expected error when updating Mtime on a non-existing block")
352         }
353 }
354
355 // Put a few blocks and invoke IndexTo with:
356 // * no prefix
357 // * with a prefix
358 // * with no such prefix
359 // Test should pass for both writable and read-only volumes
360 func testIndexTo(t TB, factory TestableVolumeFactory) {
361         v := factory(t)
362         defer v.Teardown()
363
364         v.PutRaw(TestHash, TestBlock)
365         v.PutRaw(TestHash2, TestBlock2)
366         v.PutRaw(TestHash3, TestBlock3)
367
368         // Blocks whose names aren't Keep hashes should be omitted from
369         // index
370         v.PutRaw("fffffffffnotreallyahashfffffffff", nil)
371         v.PutRaw("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", nil)
372         v.PutRaw("f0000000000000000000000000000000f", nil)
373         v.PutRaw("f00", nil)
374
375         buf := new(bytes.Buffer)
376         v.IndexTo("", buf)
377         indexRows := strings.Split(string(buf.Bytes()), "\n")
378         sort.Strings(indexRows)
379         sortedIndex := strings.Join(indexRows, "\n")
380         m, err := regexp.MatchString(
381                 `^\n`+TestHash+`\+\d+ \d+\n`+
382                         TestHash3+`\+\d+ \d+\n`+
383                         TestHash2+`\+\d+ \d+$`,
384                 sortedIndex)
385         if err != nil {
386                 t.Error(err)
387         } else if !m {
388                 t.Errorf("Got index %q for empty prefix", sortedIndex)
389         }
390
391         for _, prefix := range []string{"f", "f15", "f15ac"} {
392                 buf = new(bytes.Buffer)
393                 v.IndexTo(prefix, buf)
394
395                 m, err := regexp.MatchString(`^`+TestHash2+`\+\d+ \d+\n$`, string(buf.Bytes()))
396                 if err != nil {
397                         t.Error(err)
398                 } else if !m {
399                         t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
400                 }
401         }
402
403         for _, prefix := range []string{"zero", "zip", "zilch"} {
404                 buf = new(bytes.Buffer)
405                 v.IndexTo(prefix, buf)
406                 if err != nil {
407                         t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
408                 } else if buf.Len() != 0 {
409                         t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
410                 }
411         }
412 }
413
414 // Calling Delete() for a block immediately after writing it (not old enough)
415 // should neither delete the data nor return an error.
416 // Test is intended for only writable volumes
417 func testDeleteNewBlock(t TB, factory TestableVolumeFactory) {
418         v := factory(t)
419         defer v.Teardown()
420         blobSignatureTTL = 300 * time.Second
421
422         if v.Writable() == false {
423                 return
424         }
425
426         v.Put(TestHash, TestBlock)
427
428         if err := v.Trash(TestHash); err != nil {
429                 t.Error(err)
430         }
431         data, err := v.Get(TestHash)
432         if err != nil {
433                 t.Error(err)
434         } else {
435                 if bytes.Compare(data, TestBlock) != 0 {
436                         t.Errorf("Got data %+q, expected %+q", data, TestBlock)
437                 }
438                 bufs.Put(data)
439         }
440 }
441
442 // Calling Delete() for a block with a timestamp older than
443 // blobSignatureTTL seconds in the past should delete the data.
444 // Test is intended for only writable volumes
445 func testDeleteOldBlock(t TB, factory TestableVolumeFactory) {
446         v := factory(t)
447         defer v.Teardown()
448         blobSignatureTTL = 300 * time.Second
449
450         if v.Writable() == false {
451                 return
452         }
453
454         v.Put(TestHash, TestBlock)
455         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
456
457         if err := v.Trash(TestHash); err != nil {
458                 t.Error(err)
459         }
460         if _, err := v.Get(TestHash); err == nil || !os.IsNotExist(err) {
461                 t.Errorf("os.IsNotExist(%v) should have been true", err)
462         }
463 }
464
465 // Calling Delete() for a block that does not exist should result in error.
466 // Test should pass for both writable and read-only volumes
467 func testDeleteNoSuchBlock(t TB, factory TestableVolumeFactory) {
468         v := factory(t)
469         defer v.Teardown()
470
471         if err := v.Trash(TestHash2); err == nil {
472                 t.Errorf("Expected error when attempting to delete a non-existing block")
473         }
474 }
475
476 // Invoke Status and verify that VolumeStatus is returned
477 // Test should pass for both writable and read-only volumes
478 func testStatus(t TB, factory TestableVolumeFactory) {
479         v := factory(t)
480         defer v.Teardown()
481
482         // Get node status and make a basic sanity check.
483         status := v.Status()
484         if status.DeviceNum == 0 {
485                 t.Errorf("uninitialized device_num in %v", status)
486         }
487
488         if status.BytesFree == 0 {
489                 t.Errorf("uninitialized bytes_free in %v", status)
490         }
491
492         if status.BytesUsed == 0 {
493                 t.Errorf("uninitialized bytes_used in %v", status)
494         }
495 }
496
497 // Invoke String for the volume; expect non-empty result
498 // Test should pass for both writable and read-only volumes
499 func testString(t TB, factory TestableVolumeFactory) {
500         v := factory(t)
501         defer v.Teardown()
502
503         if id := v.String(); len(id) == 0 {
504                 t.Error("Got empty string for v.String()")
505         }
506 }
507
508 // Putting, updating, touching, and deleting blocks from a read-only volume result in error.
509 // Test is intended for only read-only volumes
510 func testUpdateReadOnly(t TB, factory TestableVolumeFactory) {
511         v := factory(t)
512         defer v.Teardown()
513
514         if v.Writable() == true {
515                 return
516         }
517
518         v.PutRaw(TestHash, TestBlock)
519
520         // Get from read-only volume should succeed
521         _, err := v.Get(TestHash)
522         if err != nil {
523                 t.Errorf("got err %v, expected nil", err)
524         }
525
526         // Put a new block to read-only volume should result in error
527         err = v.Put(TestHash2, TestBlock2)
528         if err == nil {
529                 t.Errorf("Expected error when putting block in a read-only volume")
530         }
531         _, err = v.Get(TestHash2)
532         if err == nil {
533                 t.Errorf("Expected error when getting block whose put in read-only volume failed")
534         }
535
536         // Touch a block in read-only volume should result in error
537         err = v.Touch(TestHash)
538         if err == nil {
539                 t.Errorf("Expected error when touching block in a read-only volume")
540         }
541
542         // Delete a block from a read-only volume should result in error
543         err = v.Trash(TestHash)
544         if err == nil {
545                 t.Errorf("Expected error when deleting block from a read-only volume")
546         }
547
548         // Overwriting an existing block in read-only volume should result in error
549         err = v.Put(TestHash, TestBlock)
550         if err == nil {
551                 t.Errorf("Expected error when putting block in a read-only volume")
552         }
553 }
554
555 // Launch concurrent Gets
556 // Test should pass for both writable and read-only volumes
557 func testGetConcurrent(t TB, factory TestableVolumeFactory) {
558         v := factory(t)
559         defer v.Teardown()
560
561         v.PutRaw(TestHash, TestBlock)
562         v.PutRaw(TestHash2, TestBlock2)
563         v.PutRaw(TestHash3, TestBlock3)
564
565         sem := make(chan int)
566         go func(sem chan int) {
567                 buf, err := v.Get(TestHash)
568                 if err != nil {
569                         t.Errorf("err1: %v", err)
570                 }
571                 bufs.Put(buf)
572                 if bytes.Compare(buf, TestBlock) != 0 {
573                         t.Errorf("buf should be %s, is %s", string(TestBlock), string(buf))
574                 }
575                 sem <- 1
576         }(sem)
577
578         go func(sem chan int) {
579                 buf, err := v.Get(TestHash2)
580                 if err != nil {
581                         t.Errorf("err2: %v", err)
582                 }
583                 bufs.Put(buf)
584                 if bytes.Compare(buf, TestBlock2) != 0 {
585                         t.Errorf("buf should be %s, is %s", string(TestBlock2), string(buf))
586                 }
587                 sem <- 1
588         }(sem)
589
590         go func(sem chan int) {
591                 buf, err := v.Get(TestHash3)
592                 if err != nil {
593                         t.Errorf("err3: %v", err)
594                 }
595                 bufs.Put(buf)
596                 if bytes.Compare(buf, TestBlock3) != 0 {
597                         t.Errorf("buf should be %s, is %s", string(TestBlock3), string(buf))
598                 }
599                 sem <- 1
600         }(sem)
601
602         // Wait for all goroutines to finish
603         for done := 0; done < 3; {
604                 done += <-sem
605         }
606 }
607
608 // Launch concurrent Puts
609 // Test is intended for only writable volumes
610 func testPutConcurrent(t TB, factory TestableVolumeFactory) {
611         v := factory(t)
612         defer v.Teardown()
613
614         if v.Writable() == false {
615                 return
616         }
617
618         sem := make(chan int)
619         go func(sem chan int) {
620                 err := v.Put(TestHash, TestBlock)
621                 if err != nil {
622                         t.Errorf("err1: %v", err)
623                 }
624                 sem <- 1
625         }(sem)
626
627         go func(sem chan int) {
628                 err := v.Put(TestHash2, TestBlock2)
629                 if err != nil {
630                         t.Errorf("err2: %v", err)
631                 }
632                 sem <- 1
633         }(sem)
634
635         go func(sem chan int) {
636                 err := v.Put(TestHash3, TestBlock3)
637                 if err != nil {
638                         t.Errorf("err3: %v", err)
639                 }
640                 sem <- 1
641         }(sem)
642
643         // Wait for all goroutines to finish
644         for done := 0; done < 3; {
645                 done += <-sem
646         }
647
648         // Double check that we actually wrote the blocks we expected to write.
649         buf, err := v.Get(TestHash)
650         if err != nil {
651                 t.Errorf("Get #1: %v", err)
652         }
653         bufs.Put(buf)
654         if bytes.Compare(buf, TestBlock) != 0 {
655                 t.Errorf("Get #1: expected %s, got %s", string(TestBlock), string(buf))
656         }
657
658         buf, err = v.Get(TestHash2)
659         if err != nil {
660                 t.Errorf("Get #2: %v", err)
661         }
662         bufs.Put(buf)
663         if bytes.Compare(buf, TestBlock2) != 0 {
664                 t.Errorf("Get #2: expected %s, got %s", string(TestBlock2), string(buf))
665         }
666
667         buf, err = v.Get(TestHash3)
668         if err != nil {
669                 t.Errorf("Get #3: %v", err)
670         }
671         bufs.Put(buf)
672         if bytes.Compare(buf, TestBlock3) != 0 {
673                 t.Errorf("Get #3: expected %s, got %s", string(TestBlock3), string(buf))
674         }
675 }
676
677 // Write and read back a full size block
678 func testPutFullBlock(t TB, factory TestableVolumeFactory) {
679         v := factory(t)
680         defer v.Teardown()
681
682         if !v.Writable() {
683                 return
684         }
685
686         wdata := make([]byte, BlockSize)
687         wdata[0] = 'a'
688         wdata[BlockSize-1] = 'z'
689         hash := fmt.Sprintf("%x", md5.Sum(wdata))
690         err := v.Put(hash, wdata)
691         if err != nil {
692                 t.Fatal(err)
693         }
694         rdata, err := v.Get(hash)
695         if err != nil {
696                 t.Error(err)
697         } else {
698                 defer bufs.Put(rdata)
699         }
700         if bytes.Compare(rdata, wdata) != 0 {
701                 t.Error("rdata != wdata")
702         }
703 }
704
705 // With trashLifetime != 0, perform:
706 // Trash an old block - which either raises ErrNotImplemented or succeeds
707 // Untrash -  which either raises ErrNotImplemented or succeeds
708 // Get - which must succeed
709 func testTrashUntrash(t TB, factory TestableVolumeFactory) {
710         v := factory(t)
711         defer v.Teardown()
712         defer func() {
713                 trashLifetime = 0
714         }()
715
716         trashLifetime = 3600
717
718         // put block and backdate it
719         v.PutRaw(TestHash, TestBlock)
720         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
721
722         buf, err := v.Get(TestHash)
723         if err != nil {
724                 t.Fatal(err)
725         }
726         if bytes.Compare(buf, TestBlock) != 0 {
727                 t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
728         }
729         bufs.Put(buf)
730
731         // Trash
732         err = v.Trash(TestHash)
733         if v.Writable() == false {
734                 if err != MethodDisabledError {
735                         t.Error(err)
736                 }
737         } else if err != nil {
738                 if err != ErrNotImplemented {
739                         t.Error(err)
740                 }
741         } else {
742                 _, err = v.Get(TestHash)
743                 if err == nil || !os.IsNotExist(err) {
744                         t.Errorf("os.IsNotExist(%v) should have been true", err)
745                 }
746
747                 // Untrash
748                 err = v.Untrash(TestHash)
749                 if err != nil {
750                         t.Fatal(err)
751                 }
752         }
753
754         // Get the block - after trash and untrash sequence
755         buf, err = v.Get(TestHash)
756         if err != nil {
757                 t.Fatal(err)
758         }
759         if bytes.Compare(buf, TestBlock) != 0 {
760                 t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
761         }
762         bufs.Put(buf)
763 }
764
765 // With trashLifetime == 0, perform:
766 // Trash an old block - which either raises ErrNotImplemented or succeeds to delete it
767 // Untrash - which either raises ErrNotImplemented or is a no-op for the deleted block
768 // Get - which must fail to find the block, since it was deleted and hence not untrashed
769 func testEmptyTrashTrashLifetime0s(t TB, factory TestableVolumeFactory) {
770         v := factory(t)
771         defer v.Teardown()
772         defer func() {
773                 trashLifetime = 0
774                 doneEmptyingTrash <- true
775         }()
776
777         trashLifetime = 0
778         trashCheckInterval = 1
779
780         go emptyTrash(trashCheckInterval)
781
782         // Trash old block; since trashLifetime = 0, Trash actually deletes the block
783         err := trashUntrashOldBlock(t, v, 0)
784
785         // Get it; for writable volumes, this should not find the block since it was deleted
786         buf, err := v.Get(TestHash)
787         if err != nil {
788                 if !os.IsNotExist(err) {
789                         t.Errorf("os.IsNotExist(%v) should have been true", err)
790                 }
791         } else {
792                 if bytes.Compare(buf, TestBlock) != 0 {
793                         t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
794                 }
795                 bufs.Put(buf)
796         }
797 }
798
799 // With large trashLifetime, perform:
800 // Run emptyTrash goroutine with much smaller trashCheckInterval
801 // Trash an old block - which either raises ErrNotImplemented or succeeds
802 // Untrash - which either raises ErrNotImplemented or succeeds
803 // Get - which must find the block
804 func testEmptyTrashTrashLifetime3600s(t TB, factory TestableVolumeFactory) {
805         v := factory(t)
806         defer v.Teardown()
807         defer func() {
808                 trashLifetime = 0
809                 doneEmptyingTrash <- true
810         }()
811
812         trashLifetime = 3600
813         trashCheckInterval = 1
814
815         go emptyTrash(trashCheckInterval)
816
817         // Trash old block
818         err := trashUntrashOldBlock(t, v, 2)
819
820         // Get is expected to succeed after untrash before EmptyTrash
821         // It is still found on readonly volumes
822         buf, err := v.Get(TestHash)
823         if err != nil {
824                 if !os.IsNotExist(err) {
825                         t.Errorf("os.IsNotExist(%v) should have been true", err)
826                 }
827         } else {
828                 if bytes.Compare(buf, TestBlock) != 0 {
829                         t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
830                 }
831                 bufs.Put(buf)
832         }
833 }
834
835 // With trashLifetime = 1, perform:
836 // Run emptyTrash goroutine
837 // Trash an old block - which either raises ErrNotImplemented or succeeds
838 // Untrash - after emptyTrash goroutine ticks, and hence does not actually untrash
839 // Get - which must fail to find the block
840 func testEmptyTrashTrashLifetime1s(t TB, factory TestableVolumeFactory) {
841         v := factory(t)
842         defer v.Teardown()
843         defer func() {
844                 trashLifetime = 0
845                 doneEmptyingTrash <- true
846         }()
847
848         volumes = append(volumes, v)
849
850         trashLifetime = 1
851         trashCheckInterval = 1
852
853         go emptyTrash(trashCheckInterval)
854
855         // Trash old block and untrash a little after first trashCheckInterval
856         err := trashUntrashOldBlock(t, v, 3)
857
858         // Get is expected to fail due to EmptyTrash before Untrash
859         // It is still found on readonly volumes
860         buf, err := v.Get(TestHash)
861         if err != nil {
862                 if !os.IsNotExist(err) {
863                         t.Errorf("os.IsNotExist(%v) should have been true", err)
864                 }
865         } else {
866                 if bytes.Compare(buf, TestBlock) != 0 {
867                         t.Errorf("Got data %+q, expected %+q", buf, TestBlock)
868                 }
869                 bufs.Put(buf)
870         }
871 }
872
873 // Put a block, backdate it, trash it, untrash it after the untrashAfter seconds
874 func trashUntrashOldBlock(t TB, v TestableVolume, untrashAfter int) error {
875         // put block and backdate it
876         v.PutRaw(TestHash, TestBlock)
877         v.TouchWithDate(TestHash, time.Now().Add(-2*blobSignatureTTL))
878
879         buf, err := v.Get(TestHash)
880         if err != nil {
881                 t.Fatal(err)
882         }
883         if bytes.Compare(buf, TestBlock) != 0 {
884                 t.Fatalf("Got data %+q, expected %+q", buf, TestBlock)
885         }
886         bufs.Put(buf)
887
888         // Trash
889         err = v.Trash(TestHash)
890         if err != nil {
891                 if err != ErrNotImplemented && err != MethodDisabledError {
892                         t.Fatal(err)
893                 } else {
894                         // To test emptyTrash goroutine effectively, we need to give the
895                         // ticker a couple rounds, adding some sleep time to the test.
896                         // This delay is unnecessary for volumes that are currently
897                         // not yet supporting trashLifetime > 0 (this case is already
898                         // covered in the testTrashUntrash already)
899                         return err
900                 }
901         } else {
902                 _, err = v.Get(TestHash)
903                 if err == nil || !os.IsNotExist(err) {
904                         t.Fatalf("os.IsNotExist(%v) should have been true", err)
905                 }
906         }
907
908         // Untrash after give wait time
909         time.Sleep(time.Duration(untrashAfter) * time.Second)
910         err = v.Untrash(TestHash)
911         if err != nil {
912                 if err != ErrNotImplemented && err != MethodDisabledError {
913                         t.Fatal(err)
914                 }
915         }
916         return err
917 }