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
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
22 func DoGenericVolumeTests(t *testing.T, factory TestableVolumeFactory) {
24 testGetNoSuchBlock(t, factory)
26 testCompareSameContent(t, factory)
27 testCompareWithDifferentContent(t, factory)
28 testCompareWithBadData(t, factory)
30 testPutBlockWithSameContent(t, factory)
31 testPutBlockWithDifferentContent(t, factory)
32 testPutMultipleBlocks(t, factory)
34 testPutAndTouch(t, factory)
35 testTouchNoSuchBlock(t, factory)
37 testMtimeNoSuchBlock(t, factory)
39 testIndexTo(t, factory)
41 testDeleteNewBlock(t, factory)
42 testDeleteOldBlock(t, factory)
43 testDeleteNoSuchBlock(t, factory)
45 testStatus(t, factory)
47 testString(t, factory)
49 testWritableTrue(t, factory)
51 testGetSerialized(t, factory)
52 testPutSerialized(t, factory)
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)
64 // Put a test block, get it and verify content
65 func testGet(t *testing.T, factory TestableVolumeFactory) {
68 v.Put(TEST_HASH, TEST_BLOCK)
70 buf, err := v.Get(TEST_HASH)
74 if bytes.Compare(buf, TEST_BLOCK) != 0 {
75 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
79 // Invoke get on a block that does not exist in volume; should result in error
80 func testGetNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
83 v.Put(TEST_HASH, TEST_BLOCK)
85 if _, err := v.Get(TEST_HASH_2); err == nil {
86 t.Errorf("Expected error while getting non-existing block %v", TEST_HASH_2)
90 // Put a test block and compare the locator with same content
91 func testCompareSameContent(t *testing.T, factory TestableVolumeFactory) {
95 v.Put(TEST_HASH, TEST_BLOCK)
97 // Compare the block locator with same content
98 err := v.Compare(TEST_HASH, TEST_BLOCK)
100 t.Errorf("Got err %q, expected nil", err)
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) {
110 v.Put(TEST_HASH, TEST_BLOCK)
112 // Compare the block locator with different content; collision
113 err := v.Compare(TEST_HASH, []byte("baddata"))
115 t.Errorf("Expected error due to collision")
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) {
126 v.Put(TEST_HASH, []byte("baddata"))
128 err := v.Compare(TEST_HASH, TEST_BLOCK)
130 t.Errorf("Expected error due to corruption")
134 // Put a block and put again with same content
135 func testPutBlockWithSameContent(t *testing.T, factory TestableVolumeFactory) {
139 err := v.Put(TEST_HASH, TEST_BLOCK)
141 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
144 err = v.Put(TEST_HASH, TEST_BLOCK)
146 t.Errorf("Got err putting block second time %q: %q, expected nil", TEST_BLOCK, err)
150 // Put a block and put again with different content
151 func testPutBlockWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
155 err := v.Put(TEST_HASH, TEST_BLOCK)
157 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
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)
166 if _, err := v.Get(TEST_HASH); err != nil {
167 t.Errorf("Got err getting block %q: %q, expected nil", TEST_BLOCK, err)
171 // Put and get multiple blocks
172 func testPutMultipleBlocks(t *testing.T, factory TestableVolumeFactory) {
176 err := v.Put(TEST_HASH, TEST_BLOCK)
178 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
181 err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
183 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_2, err)
186 err = v.Put(TEST_HASH_3, TEST_BLOCK_3)
188 t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_3, err)
191 if data, err := v.Get(TEST_HASH); err != nil {
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)
197 if data, err := v.Get(TEST_HASH_2); err != nil {
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)
203 if data, err := v.Get(TEST_HASH_3); err != nil {
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)
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) {
217 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
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)
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))
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)
236 // Write the same block again.
237 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
241 // Verify threshold < t1
242 if t1, err := v.Mtime(TEST_HASH); err != nil {
244 } else if t1.Before(threshold) {
245 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
249 // Touching a non-existing block should result in error.
250 func testTouchNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
254 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
258 if err := v.Touch(TEST_HASH); err != nil {
259 t.Error("Expected error when attempted to touch a non-existing block")
263 // Invoking Mtime on a non-existing block should result in error.
264 func testMtimeNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
268 if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
269 t.Error("Expected error when updating Mtime on a non-existing block")
273 // Put a few blocks and invoke IndexTo with:
276 // * with no such prefix
277 func testIndexTo(t *testing.T, factory TestableVolumeFactory) {
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)
285 buf := new(bytes.Buffer)
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+$`,
298 t.Errorf("Got index %q for empty prefix", sorted_index)
301 for _, prefix := range []string{"f", "f15", "f15ac"} {
302 buf = new(bytes.Buffer)
303 v.IndexTo(prefix, buf)
305 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
309 t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
313 for _, prefix := range []string{"zero", "zip", "zilch"} {
314 buf = new(bytes.Buffer)
315 v.IndexTo(prefix, buf)
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)
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) {
329 v.Put(TEST_HASH, TEST_BLOCK)
331 if err := v.Delete(TEST_HASH); err != nil {
334 if data, err := v.Get(TEST_HASH); err != nil {
336 } else if bytes.Compare(data, TEST_BLOCK) != 0 {
337 t.Error("Block still present, but content is incorrect: %+v != %+v", data, TEST_BLOCK)
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) {
346 v.Put(TEST_HASH, TEST_BLOCK)
347 v.TouchWithDate(TEST_HASH, time.Now().Add(-2*blob_signature_ttl*time.Second))
349 if err := v.Delete(TEST_HASH); err != nil {
352 if _, err := v.Get(TEST_HASH); err == nil || !os.IsNotExist(err) {
353 t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
357 // Calling Delete() for a block that does not exist should result in error.
358 func testDeleteNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
361 v.Put(TEST_HASH, TEST_BLOCK)
363 if err := v.Delete(TEST_HASH_2); err == nil {
364 t.Errorf("Expected error when attempting to delete a non-existing block")
368 // Invoke Status and verify that VolumeStatus is returned
369 func testStatus(t *testing.T, factory TestableVolumeFactory) {
373 // Get node status and make a basic sanity check.
375 if status.DeviceNum == 0 {
376 t.Errorf("uninitialized device_num in %v", status)
379 if status.BytesFree == 0 {
380 t.Errorf("uninitialized bytes_free in %v", status)
383 if status.BytesUsed == 0 {
384 t.Errorf("uninitialized bytes_used in %v", status)
388 // Invoke String for the volume; expect non-empty result
389 func testString(t *testing.T, factory TestableVolumeFactory) {
393 if id := v.String(); len(id) == 0 {
394 t.Error("Got empty string for v.String()")
398 // Verify Writable is true on a writable volume
399 func testWritableTrue(t *testing.T, factory TestableVolumeFactory) {
403 if v.Writable() == false {
404 t.Errorf("Expected writable to be true on a writable volume")
408 // Verify Writable is false on a read-only volume
409 func testWritableFalse(t *testing.T, factory TestableVolumeFactory) {
413 if v.Writable() != false {
414 t.Errorf("Expected writable to be false on a read-only volume")
418 // Updating, touching, and deleting blocks from a read-only volume result in error.
419 func testUpdateReadOnly(t *testing.T, factory TestableVolumeFactory) {
423 v.PutRaw(TEST_HASH, TEST_BLOCK)
425 _, err := v.Get(TEST_HASH)
427 t.Errorf("got err %v, expected nil", err)
430 err = v.Put(TEST_HASH, TEST_BLOCK)
432 t.Errorf("Expected error when putting block in a read-only volume")
435 err = v.Touch(TEST_HASH)
437 t.Errorf("Expected error when touching block in a read-only volume")
440 err = v.Delete(TEST_HASH)
442 t.Errorf("Expected error when deleting block from a read-only volume")
446 // Serialization tests: launch a bunch of concurrent
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.
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.
461 func testGetSerialized(t *testing.T, factory TestableVolumeFactory) {
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)
469 sem := make(chan int)
470 go func(sem chan int) {
471 buf, err := v.Get(TEST_HASH)
473 t.Errorf("err1: %v", err)
475 if bytes.Compare(buf, TEST_BLOCK) != 0 {
476 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
481 go func(sem chan int) {
482 buf, err := v.Get(TEST_HASH_2)
484 t.Errorf("err2: %v", err)
486 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
487 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
492 go func(sem chan int) {
493 buf, err := v.Get(TEST_HASH_3)
495 t.Errorf("err3: %v", err)
497 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
498 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
503 // Wait for all goroutines to finish
504 for done := 0; done < 3; {
509 func testPutSerialized(t *testing.T, factory TestableVolumeFactory) {
513 sem := make(chan int)
514 go func(sem chan int) {
515 err := v.Put(TEST_HASH, TEST_BLOCK)
517 t.Errorf("err1: %v", err)
522 go func(sem chan int) {
523 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
525 t.Errorf("err2: %v", err)
530 go func(sem chan int) {
531 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
533 t.Errorf("err3: %v", err)
538 // Wait for all goroutines to finish
539 for done := 0; done < 3; {
543 // Double check that we actually wrote the blocks we expected to write.
544 buf, err := v.Get(TEST_HASH)
546 t.Errorf("Get #1: %v", err)
548 if bytes.Compare(buf, TEST_BLOCK) != 0 {
549 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
552 buf, err = v.Get(TEST_HASH_2)
554 t.Errorf("Get #2: %v", err)
556 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
557 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
560 buf, err = v.Get(TEST_HASH_3)
562 t.Errorf("Get #3: %v", err)
564 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
565 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))