Merge branch 'master' into 7179-generic-volume-tests
[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         testGetSerialized(t, factory)
52         testPutSerialized(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         if bytes.Compare(buf, TEST_BLOCK) != 0 {
75                 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
76         }
77 }
78
79 // Invoke get on a block that does not exist in volume; should result in error
80 func testGetNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
81         v := factory(t)
82         defer v.Teardown()
83         v.Put(TEST_HASH, TEST_BLOCK)
84
85         if _, err := v.Get(TEST_HASH_2); err == nil {
86                 t.Errorf("Expected error while getting non-existing block %v", TEST_HASH_2)
87         }
88 }
89
90 // Put a test block and compare the locator with same content
91 func testCompareSameContent(t *testing.T, factory TestableVolumeFactory) {
92         v := factory(t)
93         defer v.Teardown()
94
95         v.Put(TEST_HASH, TEST_BLOCK)
96
97         // Compare the block locator with same content
98         err := v.Compare(TEST_HASH, TEST_BLOCK)
99         if err != nil {
100                 t.Errorf("Got err %q, expected nil", err)
101         }
102 }
103
104 // Put a test block and compare the locator with a different content
105 // Expect error due to collision
106 func testCompareWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
107         v := factory(t)
108         defer v.Teardown()
109
110         v.Put(TEST_HASH, TEST_BLOCK)
111
112         // Compare the block locator with different content; collision
113         err := v.Compare(TEST_HASH, []byte("baddata"))
114         if err == nil {
115                 t.Errorf("Expected error due to collision")
116         }
117 }
118
119 // Put a test block with bad data (hash does not match, but Put does not verify)
120 // Compare the locator with good data whose has matches with locator
121 // Expect error due to corruption.
122 func testCompareWithBadData(t *testing.T, factory TestableVolumeFactory) {
123         v := factory(t)
124         defer v.Teardown()
125
126         v.Put(TEST_HASH, []byte("baddata"))
127
128         err := v.Compare(TEST_HASH, TEST_BLOCK)
129         if err == nil {
130                 t.Errorf("Expected error due to corruption")
131         }
132 }
133
134 // Put a block and put again with same content
135 func testPutBlockWithSameContent(t *testing.T, factory TestableVolumeFactory) {
136         v := factory(t)
137         defer v.Teardown()
138
139         err := v.Put(TEST_HASH, TEST_BLOCK)
140         if err != nil {
141                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
142         }
143
144         err = v.Put(TEST_HASH, TEST_BLOCK)
145         if err != nil {
146                 t.Errorf("Got err putting block second time %q: %q, expected nil", TEST_BLOCK, err)
147         }
148 }
149
150 // Put a block and put again with different content
151 func testPutBlockWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
152         v := factory(t)
153         defer v.Teardown()
154
155         err := v.Put(TEST_HASH, TEST_BLOCK)
156         if err != nil {
157                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
158         }
159
160         // Whether Put with the same loc with different content fails or succeeds
161         // is implementation dependent. So, just check loc exists after overwriting.
162         // We also do not want to see if loc has block1 or block2, for the same reason.
163         if err = v.Put(TEST_HASH, TEST_BLOCK_2); err != nil {
164                 t.Errorf("Got err putting block with different content %q: %q, expected nil", TEST_BLOCK, err)
165         }
166         if _, err := v.Get(TEST_HASH); err != nil {
167                 t.Errorf("Got err getting block %q: %q, expected nil", TEST_BLOCK, err)
168         }
169 }
170
171 // Put and get multiple blocks
172 func testPutMultipleBlocks(t *testing.T, factory TestableVolumeFactory) {
173         v := factory(t)
174         defer v.Teardown()
175
176         err := v.Put(TEST_HASH, TEST_BLOCK)
177         if err != nil {
178                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
179         }
180
181         err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
182         if err != nil {
183                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_2, err)
184         }
185
186         err = v.Put(TEST_HASH_3, TEST_BLOCK_3)
187         if err != nil {
188                 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_3, err)
189         }
190
191         if data, err := v.Get(TEST_HASH); err != nil {
192                 t.Error(err)
193         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
194                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK)
195         }
196
197         if data, err := v.Get(TEST_HASH_2); err != nil {
198                 t.Error(err)
199         } else if bytes.Compare(data, TEST_BLOCK_2) != 0 {
200                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_2)
201         }
202
203         if data, err := v.Get(TEST_HASH_3); err != nil {
204                 t.Error(err)
205         } else if bytes.Compare(data, TEST_BLOCK_3) != 0 {
206                 t.Errorf("Block present, but content is incorrect: Expected: %v  Found: %v", data, TEST_BLOCK_3)
207         }
208 }
209
210 // testPutAndTouch
211 //   Test that when applying PUT to a block that already exists,
212 //   the block's modification time is updated.
213 func testPutAndTouch(t *testing.T, factory TestableVolumeFactory) {
214         v := factory(t)
215         defer v.Teardown()
216
217         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
218                 t.Error(err)
219         }
220
221         // We'll verify { t0 < threshold < t1 }, where t0 is the
222         // existing block's timestamp on disk before Put() and t1 is
223         // its timestamp after Put().
224         threshold := time.Now().Add(-time.Second)
225
226         // Set the stored block's mtime far enough in the past that we
227         // can see the difference between "timestamp didn't change"
228         // and "timestamp granularity is too low".
229         v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
230
231         // Make sure v.Mtime() agrees the above Utime really worked.
232         if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
233                 t.Errorf("Setting mtime failed: %v, %v", t0, err)
234         }
235
236         // Write the same block again.
237         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
238                 t.Error(err)
239         }
240
241         // Verify threshold < t1
242         if t1, err := v.Mtime(TEST_HASH); err != nil {
243                 t.Error(err)
244         } else if t1.Before(threshold) {
245                 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
246         }
247 }
248
249 // Touching a non-existing block should result in error.
250 func testTouchNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
251         v := factory(t)
252         defer v.Teardown()
253
254         if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
255                 t.Error(err)
256         }
257
258         if err := v.Touch(TEST_HASH); err != nil {
259                 t.Error("Expected error when attempted to touch a non-existing block")
260         }
261 }
262
263 // Invoking Mtime on a non-existing block should result in error.
264 func testMtimeNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
265         v := factory(t)
266         defer v.Teardown()
267
268         if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
269                 t.Error("Expected error when updating Mtime on a non-existing block")
270         }
271 }
272
273 // Put a few blocks and invoke IndexTo with:
274 // * no prefix
275 // * with a prefix
276 // * with no such prefix
277 func testIndexTo(t *testing.T, factory TestableVolumeFactory) {
278         v := factory(t)
279         defer v.Teardown()
280
281         v.Put(TEST_HASH, TEST_BLOCK)
282         v.Put(TEST_HASH_2, TEST_BLOCK_2)
283         v.Put(TEST_HASH_3, TEST_BLOCK_3)
284
285         buf := new(bytes.Buffer)
286         v.IndexTo("", buf)
287         index_rows := strings.Split(string(buf.Bytes()), "\n")
288         sort.Strings(index_rows)
289         sorted_index := strings.Join(index_rows, "\n")
290         m, err := regexp.MatchString(
291                 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
292                         TEST_HASH_3+`\+\d+ \d+\n`+
293                         TEST_HASH_2+`\+\d+ \d+$`,
294                 sorted_index)
295         if err != nil {
296                 t.Error(err)
297         } else if !m {
298                 t.Errorf("Got index %q for empty prefix", sorted_index)
299         }
300
301         for _, prefix := range []string{"f", "f15", "f15ac"} {
302                 buf = new(bytes.Buffer)
303                 v.IndexTo(prefix, buf)
304
305                 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
306                 if err != nil {
307                         t.Error(err)
308                 } else if !m {
309                         t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
310                 }
311         }
312
313         for _, prefix := range []string{"zero", "zip", "zilch"} {
314                 buf = new(bytes.Buffer)
315                 v.IndexTo(prefix, buf)
316                 if err != nil {
317                         t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
318                 } else if buf.Len() != 0 {
319                         t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
320                 }
321         }
322 }
323
324 // Calling Delete() for a block immediately after writing it (not old enough)
325 // should neither delete the data nor return an error.
326 func testDeleteNewBlock(t *testing.T, factory TestableVolumeFactory) {
327         v := factory(t)
328         defer v.Teardown()
329         v.Put(TEST_HASH, TEST_BLOCK)
330
331         if err := v.Delete(TEST_HASH); err != nil {
332                 t.Error(err)
333         }
334         if data, err := v.Get(TEST_HASH); err != nil {
335                 t.Error(err)
336         } else if bytes.Compare(data, TEST_BLOCK) != 0 {
337                 t.Error("Block still present, but content is incorrect: %+v != %+v", data, TEST_BLOCK)
338         }
339 }
340
341 // Calling Delete() for a block with a timestamp older than
342 // blob_signature_ttl seconds in the past should delete the data.
343 func testDeleteOldBlock(t *testing.T, factory TestableVolumeFactory) {
344         v := factory(t)
345         defer v.Teardown()
346         v.Put(TEST_HASH, TEST_BLOCK)
347         v.TouchWithDate(TEST_HASH, time.Now().Add(-2*blob_signature_ttl*time.Second))
348
349         if err := v.Delete(TEST_HASH); err != nil {
350                 t.Error(err)
351         }
352         if _, err := v.Get(TEST_HASH); err == nil || !os.IsNotExist(err) {
353                 t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
354         }
355 }
356
357 // Calling Delete() for a block that does not exist should result in error.
358 func testDeleteNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
359         v := factory(t)
360         defer v.Teardown()
361         v.Put(TEST_HASH, TEST_BLOCK)
362
363         if err := v.Delete(TEST_HASH_2); err == nil {
364                 t.Errorf("Expected error when attempting to delete a non-existing block")
365         }
366 }
367
368 // Invoke Status and verify that VolumeStatus is returned
369 func testStatus(t *testing.T, factory TestableVolumeFactory) {
370         v := factory(t)
371         defer v.Teardown()
372
373         // Get node status and make a basic sanity check.
374         status := v.Status()
375         if status.DeviceNum == 0 {
376                 t.Errorf("uninitialized device_num in %v", status)
377         }
378
379         if status.BytesFree == 0 {
380                 t.Errorf("uninitialized bytes_free in %v", status)
381         }
382
383         if status.BytesUsed == 0 {
384                 t.Errorf("uninitialized bytes_used in %v", status)
385         }
386 }
387
388 // Invoke String for the volume; expect non-empty result
389 func testString(t *testing.T, factory TestableVolumeFactory) {
390         v := factory(t)
391         defer v.Teardown()
392
393         if id := v.String(); len(id) == 0 {
394                 t.Error("Got empty string for v.String()")
395         }
396 }
397
398 // Verify Writable is true on a writable volume
399 func testWritableTrue(t *testing.T, factory TestableVolumeFactory) {
400         v := factory(t)
401         defer v.Teardown()
402
403         if v.Writable() == false {
404                 t.Errorf("Expected writable to be true on a writable volume")
405         }
406 }
407
408 // Verify Writable is false on a read-only volume
409 func testWritableFalse(t *testing.T, factory TestableVolumeFactory) {
410         v := factory(t)
411         defer v.Teardown()
412
413         if v.Writable() != false {
414                 t.Errorf("Expected writable to be false on a read-only volume")
415         }
416 }
417
418 // Updating, touching, and deleting blocks from a read-only volume result in error.
419 func testUpdateReadOnly(t *testing.T, factory TestableVolumeFactory) {
420         v := factory(t)
421         defer v.Teardown()
422
423         v.PutRaw(TEST_HASH, TEST_BLOCK)
424
425         _, err := v.Get(TEST_HASH)
426         if err != nil {
427                 t.Errorf("got err %v, expected nil", err)
428         }
429
430         err = v.Put(TEST_HASH, TEST_BLOCK)
431         if err == nil {
432                 t.Errorf("Expected error when putting block in a read-only volume")
433         }
434
435         err = v.Touch(TEST_HASH)
436         if err == nil {
437                 t.Errorf("Expected error when touching block in a read-only volume")
438         }
439
440         err = v.Delete(TEST_HASH)
441         if err == nil {
442                 t.Errorf("Expected error when deleting block from a read-only volume")
443         }
444 }
445
446 // Serialization tests: launch a bunch of concurrent
447 //
448 // TODO(twp): show that the underlying Read/Write operations executed
449 // serially and not concurrently. The easiest way to do this is
450 // probably to activate verbose or debug logging, capture log output
451 // and examine it to confirm that Reads and Writes did not overlap.
452 //
453 // TODO(twp): a proper test of I/O serialization requires that a
454 // second request start while the first one is still underway.
455 // Guaranteeing that the test behaves this way requires some tricky
456 // synchronization and mocking.  For now we'll just launch a bunch of
457 // requests simultaenously in goroutines and demonstrate that they
458 // return accurate results.
459 //
460
461 func testGetSerialized(t *testing.T, factory TestableVolumeFactory) {
462         v := factory(t)
463         defer v.Teardown()
464
465         v.Put(TEST_HASH, TEST_BLOCK)
466         v.Put(TEST_HASH_2, TEST_BLOCK_2)
467         v.Put(TEST_HASH_3, TEST_BLOCK_3)
468
469         sem := make(chan int)
470         go func(sem chan int) {
471                 buf, err := v.Get(TEST_HASH)
472                 if err != nil {
473                         t.Errorf("err1: %v", err)
474                 }
475                 if bytes.Compare(buf, TEST_BLOCK) != 0 {
476                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
477                 }
478                 sem <- 1
479         }(sem)
480
481         go func(sem chan int) {
482                 buf, err := v.Get(TEST_HASH_2)
483                 if err != nil {
484                         t.Errorf("err2: %v", err)
485                 }
486                 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
487                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
488                 }
489                 sem <- 1
490         }(sem)
491
492         go func(sem chan int) {
493                 buf, err := v.Get(TEST_HASH_3)
494                 if err != nil {
495                         t.Errorf("err3: %v", err)
496                 }
497                 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
498                         t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
499                 }
500                 sem <- 1
501         }(sem)
502
503         // Wait for all goroutines to finish
504         for done := 0; done < 3; {
505                 done += <-sem
506         }
507 }
508
509 func testPutSerialized(t *testing.T, factory TestableVolumeFactory) {
510         v := factory(t)
511         defer v.Teardown()
512
513         sem := make(chan int)
514         go func(sem chan int) {
515                 err := v.Put(TEST_HASH, TEST_BLOCK)
516                 if err != nil {
517                         t.Errorf("err1: %v", err)
518                 }
519                 sem <- 1
520         }(sem)
521
522         go func(sem chan int) {
523                 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
524                 if err != nil {
525                         t.Errorf("err2: %v", err)
526                 }
527                 sem <- 1
528         }(sem)
529
530         go func(sem chan int) {
531                 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
532                 if err != nil {
533                         t.Errorf("err3: %v", err)
534                 }
535                 sem <- 1
536         }(sem)
537
538         // Wait for all goroutines to finish
539         for done := 0; done < 3; {
540                 done += <-sem
541         }
542
543         // Double check that we actually wrote the blocks we expected to write.
544         buf, err := v.Get(TEST_HASH)
545         if err != nil {
546                 t.Errorf("Get #1: %v", err)
547         }
548         if bytes.Compare(buf, TEST_BLOCK) != 0 {
549                 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
550         }
551
552         buf, err = v.Get(TEST_HASH_2)
553         if err != nil {
554                 t.Errorf("Get #2: %v", err)
555         }
556         if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
557                 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
558         }
559
560         buf, err = v.Get(TEST_HASH_3)
561         if err != nil {
562                 t.Errorf("Get #3: %v", err)
563         }
564         if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
565                 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
566         }
567 }