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