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