7179: A few golint suggested updates. There are still a lot of golint complaints.
[arvados.git] / services / keepstore / volume_generic_test.go
1 package main
2
3 import (
4         "bytes"
5         "os"
6         "regexp"
7         "sort"
8         "strings"
9         "testing"
10         "time"
11 )
12
13 // A TestableVolumeFactory returns a new TestableVolume. The factory
14 // function, and the TestableVolume it returns, can use t to write
15 // logs, fail the current test, etc.
16 type TestableVolumeFactory func(t *testing.T) TestableVolume
17
18 // DoGenericVolumeTests runs a set of tests that every TestableVolume
19 // is expected to pass. It calls factory to create a new writable
20 // TestableVolume for each test case, to avoid leaking state between
21 // tests.
22 func DoGenericVolumeTests(t *testing.T, factory TestableVolumeFactory) {
23         testGet(t, factory)
24         testGetNoSuchBlock(t, factory)
25
26         testCompareSameContent(t, factory)
27         testCompareWithDifferentContent(t, factory)
28         testCompareWithBadData(t, factory)
29
30         testPutBlockWithSameContent(t, factory)
31         testPutBlockWithDifferentContent(t, factory)
32         testPutMultipleBlocks(t, factory)
33
34         testPutAndTouch(t, factory)
35         testTouchNoSuchBlock(t, factory)
36
37         testMtimeNoSuchBlock(t, factory)
38
39         testIndexTo(t, factory)
40
41         testDeleteNewBlock(t, factory)
42         testDeleteOldBlock(t, factory)
43         testDeleteNoSuchBlock(t, factory)
44
45         testStatus(t, factory)
46
47         testString(t, factory)
48
49         testWritableTrue(t, factory)
50
51         testGetConcurrent(t, factory)
52         testPutConcurrent(t, factory)
53 }
54
55 // DoGenericReadOnlyVolumeTests runs a set of tests that every
56 // read-only TestableVolume is expected to pass. It calls factory
57 // to create a new read-only TestableVolume for each test case,
58 // to avoid leaking state between tests.
59 func DoGenericReadOnlyVolumeTests(t *testing.T, factory TestableVolumeFactory) {
60         testWritableFalse(t, factory)
61         testUpdateReadOnly(t, factory)
62 }
63
64 // Put a test block, get it and verify content
65 func testGet(t *testing.T, factory TestableVolumeFactory) {
66         v := factory(t)
67         defer v.Teardown()
68         v.Put(TEST_HASH, TEST_BLOCK)
69
70         buf, err := v.Get(TEST_HASH)
71         if err != nil {
72                 t.Error(err)
73         }
74
75         bufs.Put(buf)
76
77         if bytes.Compare(buf, TEST_BLOCK) != 0 {
78                 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
79         }
80 }
81
82 // Invoke get on a block that does not exist in volume; should result in error
83 func testGetNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
84         v := factory(t)
85         defer v.Teardown()
86         v.Put(TEST_HASH, TEST_BLOCK)
87
88         if _, err := v.Get(TEST_HASH_2); err == nil {
89                 t.Errorf("Expected error while getting non-existing block %v", TEST_HASH_2)
90         }
91 }
92
93 // Put a test block and compare the locator with same content
94 func testCompareSameContent(t *testing.T, factory TestableVolumeFactory) {
95         v := factory(t)
96         defer v.Teardown()
97
98         v.Put(TEST_HASH, TEST_BLOCK)
99
100         // Compare the block locator with same content
101         err := v.Compare(TEST_HASH, TEST_BLOCK)
102         if err != nil {
103                 t.Errorf("Got err %q, expected nil", err)
104         }
105 }
106
107 // Put a test block and compare the locator with a different content
108 // Expect error due to collision
109 func testCompareWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
110         v := factory(t)
111         defer v.Teardown()
112
113         v.Put(TEST_HASH, TEST_BLOCK)
114
115         // Compare the block locator with different content; collision
116         err := v.Compare(TEST_HASH, []byte("baddata"))
117         if err == nil {
118                 t.Errorf("Expected error due to collision")
119         }
120 }
121
122 // Put a test block with bad data (hash does not match, but Put does not verify)
123 // Compare the locator with good data whose has matches with locator
124 // Expect error due to corruption.
125 func testCompareWithBadData(t *testing.T, factory TestableVolumeFactory) {
126         v := factory(t)
127         defer v.Teardown()
128
129         v.Put(TEST_HASH, []byte("baddata"))
130
131         err := v.Compare(TEST_HASH, TEST_BLOCK)
132         if err == nil {
133                 t.Errorf("Expected error due to corruption")
134         }
135 }
136
137 // Put a block and put again with same content
138 func testPutBlockWithSameContent(t *testing.T, factory TestableVolumeFactory) {
139         v := factory(t)
140         defer v.Teardown()
141
142         err := v.Put(TEST_HASH, TEST_BLOCK)
143         if err != nil {
144                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
145         }
146
147         err = v.Put(TEST_HASH, TEST_BLOCK)
148         if err != nil {
149                 t.Errorf("Got err putting block second time %q: %q, expected nil", TEST_BLOCK, err)
150         }
151 }
152
153 // Put a block and put again with different content
154 func testPutBlockWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
155         v := factory(t)
156         defer v.Teardown()
157
158         err := v.Put(TEST_HASH, TEST_BLOCK)
159         if err != nil {
160                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
161         }
162
163         putErr := v.Put(TEST_HASH, TEST_BLOCK_2)
164         buf, getErr := v.Get(TEST_HASH)
165         if putErr == nil {
166                 // Put must not return a nil error unless it has
167                 // overwritten the existing data.
168                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
169                         t.Errorf("Put succeeded but Get returned %+v, expected %+v", buf, TEST_BLOCK_2)
170                 }
171         } else {
172                 // It is permissible for Put to fail, but it must
173                 // leave us with either the original data, the new
174                 // data, or nothing at all.
175                 if getErr == nil && bytes.Compare(buf, TEST_BLOCK) != 0 && bytes.Compare(buf, TEST_BLOCK_2) != 0 {
176                         t.Errorf("Put failed but Get returned %+v, which is neither %+v nor %+v", buf, TEST_BLOCK, TEST_BLOCK_2)
177                 }
178         }
179         if getErr == nil {
180                 bufs.Put(buf)
181         }
182 }
183
184 // Put and get multiple blocks
185 func testPutMultipleBlocks(t *testing.T, factory TestableVolumeFactory) {
186         v := factory(t)
187         defer v.Teardown()
188
189         err := v.Put(TEST_HASH, TEST_BLOCK)
190         if err != nil {
191                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
192         }
193
194         err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
195         if err != nil {
196                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_2, err)
197         }
198
199         err = v.Put(TEST_HASH_3, TEST_BLOCK_3)
200         if err != nil {
201                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_3, err)
202         }
203
204         data, err := v.Get(TEST_HASH)
205         if err != nil {
206                 t.Error(err)
207         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
208                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK)
209         }
210         bufs.Put(data)
211
212         data, err = v.Get(TEST_HASH_2)
213         if err != nil {
214                 t.Error(err)
215         } else if bytes.Compare(data, TEST_BLOCK_2) != 0 {
216                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_2)
217         }
218         bufs.Put(data)
219
220         data, err = v.Get(TEST_HASH_3)
221         if err != nil {
222                 t.Error(err)
223         } else if bytes.Compare(data, TEST_BLOCK_3) != 0 {
224                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_3)
225         }
226         bufs.Put(data)
227 }
228
229 // testPutAndTouch
230 //   Test that when applying PUT to a block that already exists,
231 //   the block's modification time is updated.
232 func testPutAndTouch(t *testing.T, factory TestableVolumeFactory) {
233         v := factory(t)
234         defer v.Teardown()
235
236         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
237                 t.Error(err)
238         }
239
240         // We'll verify { t0 < threshold < t1 }, where t0 is the
241         // existing block's timestamp on disk before Put() and t1 is
242         // its timestamp after Put().
243         threshold := time.Now().Add(-time.Second)
244
245         // Set the stored block's mtime far enough in the past that we
246         // can see the difference between "timestamp didn't change"
247         // and "timestamp granularity is too low".
248         v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
249
250         // Make sure v.Mtime() agrees the above Utime really worked.
251         if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
252                 t.Errorf("Setting mtime failed: %v, %v", t0, err)
253         }
254
255         // Write the same block again.
256         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
257                 t.Error(err)
258         }
259
260         // Verify threshold < t1
261         if t1, err := v.Mtime(TEST_HASH); err != nil {
262                 t.Error(err)
263         } else if t1.Before(threshold) {
264                 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
265         }
266 }
267
268 // Touching a non-existing block should result in error.
269 func testTouchNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
270         v := factory(t)
271         defer v.Teardown()
272
273         if err := v.Touch(TEST_HASH); err == nil {
274                 t.Error("Expected error when attempted to touch a non-existing block")
275         }
276 }
277
278 // Invoking Mtime on a non-existing block should result in error.
279 func testMtimeNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
280         v := factory(t)
281         defer v.Teardown()
282
283         if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
284                 t.Error("Expected error when updating Mtime on a non-existing block")
285         }
286 }
287
288 // Put a few blocks and invoke IndexTo with:
289 // * no prefix
290 // * with a prefix
291 // * with no such prefix
292 func testIndexTo(t *testing.T, factory TestableVolumeFactory) {
293         v := factory(t)
294         defer v.Teardown()
295
296         v.Put(TEST_HASH, TEST_BLOCK)
297         v.Put(TEST_HASH_2, TEST_BLOCK_2)
298         v.Put(TEST_HASH_3, TEST_BLOCK_3)
299
300         buf := new(bytes.Buffer)
301         v.IndexTo("", buf)
302         indexRows := strings.Split(string(buf.Bytes()), "\n")
303         sort.Strings(indexRows)
304         sortedIndex := strings.Join(indexRows, "\n")
305         m, err := regexp.MatchString(
306                 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
307                         TEST_HASH_3+`\+\d+ \d+\n`+
308                         TEST_HASH_2+`\+\d+ \d+$`,
309                 sortedIndex)
310         if err != nil {
311                 t.Error(err)
312         } else if !m {
313                 t.Errorf("Got index %q for empty prefix", sortedIndex)
314         }
315
316         for _, prefix := range []string{"f", "f15", "f15ac"} {
317                 buf = new(bytes.Buffer)
318                 v.IndexTo(prefix, buf)
319
320                 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
321                 if err != nil {
322                         t.Error(err)
323                 } else if !m {
324                         t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
325                 }
326         }
327
328         for _, prefix := range []string{"zero", "zip", "zilch"} {
329                 buf = new(bytes.Buffer)
330                 v.IndexTo(prefix, buf)
331                 if err != nil {
332                         t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
333                 } else if buf.Len() != 0 {
334                         t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
335                 }
336         }
337 }
338
339 // Calling Delete() for a block immediately after writing it (not old enough)
340 // should neither delete the data nor return an error.
341 func testDeleteNewBlock(t *testing.T, factory TestableVolumeFactory) {
342         v := factory(t)
343         defer v.Teardown()
344         v.Put(TEST_HASH, TEST_BLOCK)
345
346         if err := v.Delete(TEST_HASH); err != nil {
347                 t.Error(err)
348         }
349         data, err := v.Get(TEST_HASH)
350         if err != nil {
351                 t.Error(err)
352         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
353                 t.Error("Block still present, but content is incorrect: %+v != %+v", data, TEST_BLOCK)
354         }
355         bufs.Put(data)
356 }
357
358 // Calling Delete() for a block with a timestamp older than
359 // blob_signature_ttl seconds in the past should delete the data.
360 func testDeleteOldBlock(t *testing.T, factory TestableVolumeFactory) {
361         v := factory(t)
362         defer v.Teardown()
363         v.Put(TEST_HASH, TEST_BLOCK)
364         v.TouchWithDate(TEST_HASH, time.Now().Add(-2*blob_signature_ttl*time.Second))
365
366         if err := v.Delete(TEST_HASH); err != nil {
367                 t.Error(err)
368         }
369         if _, err := v.Get(TEST_HASH); err == nil || !os.IsNotExist(err) {
370                 t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
371         }
372 }
373
374 // Calling Delete() for a block that does not exist should result in error.
375 func testDeleteNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
376         v := factory(t)
377         defer v.Teardown()
378         v.Put(TEST_HASH, TEST_BLOCK)
379
380         if err := v.Delete(TEST_HASH_2); err == nil {
381                 t.Errorf("Expected error when attempting to delete a non-existing block")
382         }
383 }
384
385 // Invoke Status and verify that VolumeStatus is returned
386 func testStatus(t *testing.T, factory TestableVolumeFactory) {
387         v := factory(t)
388         defer v.Teardown()
389
390         // Get node status and make a basic sanity check.
391         status := v.Status()
392         if status.DeviceNum == 0 {
393                 t.Errorf("uninitialized device_num in %v", status)
394         }
395
396         if status.BytesFree == 0 {
397                 t.Errorf("uninitialized bytes_free in %v", status)
398         }
399
400         if status.BytesUsed == 0 {
401                 t.Errorf("uninitialized bytes_used in %v", status)
402         }
403 }
404
405 // Invoke String for the volume; expect non-empty result
406 func testString(t *testing.T, factory TestableVolumeFactory) {
407         v := factory(t)
408         defer v.Teardown()
409
410         if id := v.String(); len(id) == 0 {
411                 t.Error("Got empty string for v.String()")
412         }
413 }
414
415 // Verify Writable is true on a writable volume
416 func testWritableTrue(t *testing.T, factory TestableVolumeFactory) {
417         v := factory(t)
418         defer v.Teardown()
419
420         if v.Writable() == false {
421                 t.Errorf("Expected writable to be true on a writable volume")
422         }
423 }
424
425 // Verify Writable is false on a read-only volume
426 func testWritableFalse(t *testing.T, factory TestableVolumeFactory) {
427         v := factory(t)
428         defer v.Teardown()
429
430         if v.Writable() != false {
431                 t.Errorf("Expected writable to be false on a read-only volume")
432         }
433 }
434
435 // Updating, touching, and deleting blocks from a read-only volume result in error.
436 func testUpdateReadOnly(t *testing.T, factory TestableVolumeFactory) {
437         v := factory(t)
438         defer v.Teardown()
439
440         v.PutRaw(TEST_HASH, TEST_BLOCK)
441
442         _, err := v.Get(TEST_HASH)
443         if err != nil {
444                 t.Errorf("got err %v, expected nil", err)
445         }
446
447         err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
448         if err == nil {
449                 t.Errorf("Expected error when putting block in a read-only volume")
450         }
451         _, err = v.Get(TEST_HASH_2)
452         if err == nil {
453                 t.Errorf("Expected error when getting block whose put in read-only volume failed")
454         }
455
456         err = v.Touch(TEST_HASH)
457         if err == nil {
458                 t.Errorf("Expected error when touching block in a read-only volume")
459         }
460
461         err = v.Delete(TEST_HASH)
462         if err == nil {
463                 t.Errorf("Expected error when deleting block from a read-only volume")
464         }
465 }
466
467 // Launch concurrent Gets
468 func testGetConcurrent(t *testing.T, factory TestableVolumeFactory) {
469         v := factory(t)
470         defer v.Teardown()
471
472         v.Put(TEST_HASH, TEST_BLOCK)
473         v.Put(TEST_HASH_2, TEST_BLOCK_2)
474         v.Put(TEST_HASH_3, TEST_BLOCK_3)
475
476         sem := make(chan int)
477         go func(sem chan int) {
478                 buf, err := v.Get(TEST_HASH)
479                 if err != nil {
480                         t.Errorf("err1: %v", err)
481                 }
482                 bufs.Put(buf)
483                 if bytes.Compare(buf, TEST_BLOCK) != 0 {
484                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
485                 }
486                 sem <- 1
487         }(sem)
488
489         go func(sem chan int) {
490                 buf, err := v.Get(TEST_HASH_2)
491                 if err != nil {
492                         t.Errorf("err2: %v", err)
493                 }
494                 bufs.Put(buf)
495                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
496                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
497                 }
498                 sem <- 1
499         }(sem)
500
501         go func(sem chan int) {
502                 buf, err := v.Get(TEST_HASH_3)
503                 if err != nil {
504                         t.Errorf("err3: %v", err)
505                 }
506                 bufs.Put(buf)
507                 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
508                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
509                 }
510                 sem <- 1
511         }(sem)
512
513         // Wait for all goroutines to finish
514         for done := 0; done < 3; {
515                 done += <-sem
516         }
517 }
518
519 // Launch concurrent Puts
520 func testPutConcurrent(t *testing.T, factory TestableVolumeFactory) {
521         v := factory(t)
522         defer v.Teardown()
523
524         sem := make(chan int)
525         go func(sem chan int) {
526                 err := v.Put(TEST_HASH, TEST_BLOCK)
527                 if err != nil {
528                         t.Errorf("err1: %v", err)
529                 }
530                 sem <- 1
531         }(sem)
532
533         go func(sem chan int) {
534                 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
535                 if err != nil {
536                         t.Errorf("err2: %v", err)
537                 }
538                 sem <- 1
539         }(sem)
540
541         go func(sem chan int) {
542                 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
543                 if err != nil {
544                         t.Errorf("err3: %v", err)
545                 }
546                 sem <- 1
547         }(sem)
548
549         // Wait for all goroutines to finish
550         for done := 0; done < 3; {
551                 done += <-sem
552         }
553
554         // Double check that we actually wrote the blocks we expected to write.
555         buf, err := v.Get(TEST_HASH)
556         if err != nil {
557                 t.Errorf("Get #1: %v", err)
558         }
559         bufs.Put(buf)
560         if bytes.Compare(buf, TEST_BLOCK) != 0 {
561                 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
562         }
563
564         buf, err = v.Get(TEST_HASH_2)
565         if err != nil {
566                 t.Errorf("Get #2: %v", err)
567         }
568         bufs.Put(buf)
569         if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
570                 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
571         }
572
573         buf, err = v.Get(TEST_HASH_3)
574         if err != nil {
575                 t.Errorf("Get #3: %v", err)
576         }
577         bufs.Put(buf)
578         if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
579                 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
580         }
581 }