[ARVADOS] updated: 241f0bcdacbf83b587bff9ff45985e720bde9f0b
git at public.curoverse.com
git at public.curoverse.com
Fri Sep 11 12:00:19 EDT 2015
Summary of changes:
services/keepstore/handlers.go | 7 -
services/keepstore/volume.go | 14 +-
services/keepstore/volume_generic_test.go | 564 +++++++++++++++++++++++++++---
services/keepstore/volume_unix_test.go | 217 +-----------
4 files changed, 531 insertions(+), 271 deletions(-)
via 241f0bcdacbf83b587bff9ff45985e720bde9f0b (commit)
from fbeca60ad61f0ef846a026b288a3bc5104383da7 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 241f0bcdacbf83b587bff9ff45985e720bde9f0b
Author: radhika <radhika at curoverse.com>
Date: Fri Sep 11 11:59:05 2015 -0400
7179: add generic volume test; as part of this exercise, moved some "generic" tests from volume_unix_test.go
into volume_generic_test.go so that we do not repeat these in all storage device related testing.
diff --git a/services/keepstore/handlers.go b/services/keepstore/handlers.go
index e6129a7..3dcc6e1 100644
--- a/services/keepstore/handlers.go
+++ b/services/keepstore/handlers.go
@@ -177,13 +177,6 @@ func IndexHandler(resp http.ResponseWriter, req *http.Request) {
// * device_num (an integer identifying the underlying filesystem)
// * bytes_free
// * bytes_used
-//
-type VolumeStatus struct {
- MountPoint string `json:"mount_point"`
- DeviceNum uint64 `json:"device_num"`
- BytesFree uint64 `json:"bytes_free"`
- BytesUsed uint64 `json:"bytes_used"`
-}
type PoolStatus struct {
Alloc uint64 `json:"BytesAllocated"`
diff --git a/services/keepstore/volume.go b/services/keepstore/volume.go
index f57df24..e5964e2 100644
--- a/services/keepstore/volume.go
+++ b/services/keepstore/volume.go
@@ -122,7 +122,7 @@ type Volume interface {
//
// - size is the number of bytes of content, given as a
// decimal number with one or more digits
- //
+ //
// - timestamp is the timestamp stored for the locator,
// given as a decimal number of seconds after January 1,
// 1970 UTC.
@@ -251,3 +251,15 @@ func (vm *RRVolumeManager) NextWritable() Volume {
func (vm *RRVolumeManager) Close() {
}
+
+// VolumeStatus
+// * mount_point
+// * device_num (an integer identifying the underlying storage system)
+// * bytes_free
+// * bytes_used
+type VolumeStatus struct {
+ MountPoint string `json:"mount_point"`
+ DeviceNum uint64 `json:"device_num"`
+ BytesFree uint64 `json:"bytes_free"`
+ BytesUsed uint64 `json:"bytes_used"`
+}
diff --git a/services/keepstore/volume_generic_test.go b/services/keepstore/volume_generic_test.go
index 24ebc35..f1b27be 100644
--- a/services/keepstore/volume_generic_test.go
+++ b/services/keepstore/volume_generic_test.go
@@ -3,6 +3,9 @@ package main
import (
"bytes"
"os"
+ "regexp"
+ "sort"
+ "strings"
"testing"
"time"
)
@@ -13,64 +16,313 @@ import (
type TestableVolumeFactory func(t *testing.T) TestableVolume
// DoGenericVolumeTests runs a set of tests that every TestableVolume
-// is expected to pass. It calls factory to create a new
+// is expected to pass. It calls factory to create a new writable
// TestableVolume for each test case, to avoid leaking state between
// tests.
func DoGenericVolumeTests(t *testing.T, factory TestableVolumeFactory) {
- /*
- testGetBlock(t, factory)
- testGetNoSuchBlock(t, factory)
- testGetSystemError(t, factory)
-
- testCompareSameContent(t, factory)
- testCompareWithCollisionError(t, factory)
- testCompareWithCorruptError(t, factory)
- testCompareSystemError(t, factory)
-
- testPutBlock(t, factory)
- testPutMultipleBlocks(t, factory)
- testPutBlockWithSameContent(t, factory)
- testPutBlockWithDifferentContent(t, factory)
- testPutBlockSystemError(t, factory)
-
- testTouch(t, factory)
- testTouchNoSuchBlock(t, factory)
- testTouchSystemError(t, factory)
-
- testMtime(t, factory)
- testMtimeNoSuchBlock(t, factory)
- testMtimeSystemError(t, factory)
-
- testIndexToWithNoPrefix(t, factory)
- testIndexToWithPrefix(t, factory)
- testIndexToWithNoSuchPrefix(t, factory)
- testIndexToOnEmptyVolume(t, factory)
- testIndexToSystemError(t, factory)
-
- testDeleteNewBlock(t, factory)
- testDeleteOldWithOnlyBlockInVol(t, factory)
- testDeleteOldWithOtherBlocksInVol(t, factory)
- testDeleteNoSuchBlock(t, factory)
- testDeleteSystemError(t, factory)
-
- testStatus(t, factory)
- testStatusWithError(t, factory)
- testStatusSystemError(t, factory)
-
- testString(t, factory)
- testStringSystemError(t, factory)
-
- testWritableTrue(t, factory)
- testWritableFalse(t, factory)
- testWritableSystemError(t, factory)
- */
+ testGet(t, factory)
+ testGetNoSuchBlock(t, factory)
+
+ testCompareSameContent(t, factory)
+ testCompareWithDifferentContent(t, factory)
+ testCompareWithBadData(t, factory)
+
+ testPutBlockWithSameContent(t, factory)
+ testPutBlockWithDifferentContent(t, factory)
+ testPutMultipleBlocks(t, factory)
+
+ testPutAndTouch(t, factory)
+ testTouchNoSuchBlock(t, factory)
+
+ testMtimeNoSuchBlock(t, factory)
+
+ testIndexTo(t, factory)
testDeleteNewBlock(t, factory)
testDeleteOldBlock(t, factory)
+ testDeleteNoSuchBlock(t, factory)
+
+ testStatus(t, factory)
+
+ testString(t, factory)
+
+ testWritableTrue(t, factory)
+
+ testGetSerialized(t, factory)
+ testPutSerialized(t, factory)
+}
+
+// DoGenericReadOnlyVolumeTests runs a set of tests that every
+// read-only TestableVolume is expected to pass. It calls factory
+// to create a new read-only TestableVolume for each test case,
+// to avoid leaking state between tests.
+func DoGenericReadOnlyVolumeTests(t *testing.T, factory TestableVolumeFactory) {
+ testWritableFalse(t, factory)
+ testUpdateReadOnly(t, factory)
+}
+
+// Put a test block, get it and verify content
+func testGet(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+ v.Put(TEST_HASH, TEST_BLOCK)
+
+ buf, err := v.Get(TEST_HASH)
+ if err != nil {
+ t.Error(err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK) != 0 {
+ t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
+ }
+}
+
+// Invoke get on a block that does not exist in volume; should result in error
+func testGetNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+ v.Put(TEST_HASH, TEST_BLOCK)
+
+ if _, err := v.Get(TEST_HASH_2); err == nil {
+ t.Errorf("Expected error while getting non-existing block %v", TEST_HASH_2)
+ }
+}
+
+// Put a test block and compare the locator with same content
+func testCompareSameContent(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.Put(TEST_HASH, TEST_BLOCK)
+
+ // Compare the block locator with same content
+ err := v.Compare(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("Got err %q, expected nil", err)
+ }
+}
+
+// Put a test block and compare the locator with a different content
+// Expect error due to collision
+func testCompareWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.Put(TEST_HASH, TEST_BLOCK)
+
+ // Compare the block locator with different content; collision
+ err := v.Compare(TEST_HASH, []byte("baddata"))
+ if err == nil {
+ t.Errorf("Expected error due to collision")
+ }
+}
+
+// Put a test block with bad data (hash does not match, but Put does not verify)
+// Compare the locator with good data whose has matches with locator
+// Expect error due to corruption.
+func testCompareWithBadData(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.Put(TEST_HASH, []byte("baddata"))
+
+ err := v.Compare(TEST_HASH, TEST_BLOCK)
+ if err == nil {
+ t.Errorf("Expected error due to corruption")
+ }
+}
+
+// Put a block and put again with same content
+func testPutBlockWithSameContent(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ err := v.Put(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
+ }
+
+ err = v.Put(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("Got err putting block second time %q: %q, expected nil", TEST_BLOCK, err)
+ }
+}
+
+// Put a block and put again with different content
+func testPutBlockWithDifferentContent(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ err := v.Put(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
+ }
+
+ // Whether Put with the same loc with different content fails or succeeds
+ // is implementation dependent. So, just check loc exists after overwriting.
+ // We also do not want to see if loc has block1 or block2, for the same reason.
+ if err = v.Put(TEST_HASH, TEST_BLOCK_2); err != nil {
+ t.Errorf("Got err putting block with different content %q: %q, expected nil", TEST_BLOCK, err)
+ }
+ if _, err := v.Get(TEST_HASH); err != nil {
+ t.Errorf("Got err getting block %q: %q, expected nil", TEST_BLOCK, err)
+ }
+}
+
+// Put and get multiple blocks
+func testPutMultipleBlocks(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ err := v.Put(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK, err)
+ }
+
+ err = v.Put(TEST_HASH_2, TEST_BLOCK_2)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_2, err)
+ }
+
+ err = v.Put(TEST_HASH_3, TEST_BLOCK_3)
+ if err != nil {
+ t.Errorf("Got err putting block %q: %q, expected nil", TEST_BLOCK_3, err)
+ }
+
+ if data, err := v.Get(TEST_HASH); err != nil {
+ t.Error(err)
+ } else if bytes.Compare(data, TEST_BLOCK) != 0 {
+ t.Errorf("Block present, but content is incorrect: Expected: %v Found: %v", data, TEST_BLOCK)
+ }
+
+ if data, err := v.Get(TEST_HASH_2); err != nil {
+ t.Error(err)
+ } else if bytes.Compare(data, TEST_BLOCK_2) != 0 {
+ t.Errorf("Block present, but content is incorrect: Expected: %v Found: %v", data, TEST_BLOCK_2)
+ }
+
+ if data, err := v.Get(TEST_HASH_3); err != nil {
+ t.Error(err)
+ } else if bytes.Compare(data, TEST_BLOCK_3) != 0 {
+ t.Errorf("Block present, but content is incorrect: Expected: %v Found: %v", data, TEST_BLOCK_3)
+ }
+}
+
+// testPutAndTouch
+// Test that when applying PUT to a block that already exists,
+// the block's modification time is updated.
+func testPutAndTouch(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
+ t.Error(err)
+ }
+
+ // We'll verify { t0 < threshold < t1 }, where t0 is the
+ // existing block's timestamp on disk before Put() and t1 is
+ // its timestamp after Put().
+ threshold := time.Now().Add(-time.Second)
+
+ // Set the stored block's mtime far enough in the past that we
+ // can see the difference between "timestamp didn't change"
+ // and "timestamp granularity is too low".
+ v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
+
+ // Make sure v.Mtime() agrees the above Utime really worked.
+ if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
+ t.Errorf("Setting mtime failed: %v, %v", t0, err)
+ }
+
+ // Write the same block again.
+ if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
+ t.Error(err)
+ }
+
+ // Verify threshold < t1
+ if t1, err := v.Mtime(TEST_HASH); err != nil {
+ t.Error(err)
+ } else if t1.Before(threshold) {
+ t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
+ }
+}
+
+// Touching a non-existing block should result in error.
+func testTouchNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
+ t.Error(err)
+ }
+
+ if err := v.Touch(TEST_HASH); err != nil {
+ t.Error("Expected error when attempted to touch a non-existing block")
+ }
+}
+
+// Invoking Mtime on a non-existing block should result in error.
+func testMtimeNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if _, err := v.Mtime("12345678901234567890123456789012"); err == nil {
+ t.Error("Expected error when updating Mtime on a non-existing block")
+ }
}
-// Calling Delete() for a block immediately after writing it should
-// neither delete the data nor return an error.
+// Put a few blocks and invoke IndexTo with:
+// * no prefix
+// * with a prefix
+// * with no such prefix
+func testIndexTo(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.Put(TEST_HASH, TEST_BLOCK)
+ v.Put(TEST_HASH_2, TEST_BLOCK_2)
+ v.Put(TEST_HASH_3, TEST_BLOCK_3)
+
+ buf := new(bytes.Buffer)
+ v.IndexTo("", buf)
+ index_rows := strings.Split(string(buf.Bytes()), "\n")
+ sort.Strings(index_rows)
+ sorted_index := strings.Join(index_rows, "\n")
+ m, err := regexp.MatchString(
+ `^\n`+TEST_HASH+`\+\d+ \d+\n`+
+ TEST_HASH_3+`\+\d+ \d+\n`+
+ TEST_HASH_2+`\+\d+ \d+$`,
+ sorted_index)
+ if err != nil {
+ t.Error(err)
+ } else if !m {
+ t.Errorf("Got index %q for empty prefix", sorted_index)
+ }
+
+ for _, prefix := range []string{"f", "f15", "f15ac"} {
+ buf = new(bytes.Buffer)
+ v.IndexTo(prefix, buf)
+
+ m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
+ if err != nil {
+ t.Error(err)
+ } else if !m {
+ t.Errorf("Got index %q for prefix %s", string(buf.Bytes()), prefix)
+ }
+ }
+
+ for _, prefix := range []string{"zero", "zip", "zilch"} {
+ buf = new(bytes.Buffer)
+ v.IndexTo(prefix, buf)
+ if err != nil {
+ t.Errorf("Got error on IndexTo with no such prefix %v", err.Error())
+ } else if buf.Len() != 0 {
+ t.Errorf("Expected empty list for IndexTo with no such prefix %s", prefix)
+ }
+ }
+}
+
+// Calling Delete() for a block immediately after writing it (not old enough)
+// should neither delete the data nor return an error.
func testDeleteNewBlock(t *testing.T, factory TestableVolumeFactory) {
v := factory(t)
defer v.Teardown()
@@ -101,3 +353,215 @@ func testDeleteOldBlock(t *testing.T, factory TestableVolumeFactory) {
t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
}
}
+
+// Calling Delete() for a block that does not exist should result in error.
+func testDeleteNoSuchBlock(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+ v.Put(TEST_HASH, TEST_BLOCK)
+
+ if err := v.Delete(TEST_HASH_2); err == nil {
+ t.Errorf("Expected error when attempting to delete a non-existing block")
+ }
+}
+
+// Invoke Status and verify that VolumeStatus is returned
+func testStatus(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ // Get node status and make a basic sanity check.
+ status := v.Status()
+ if status.DeviceNum == 0 {
+ t.Errorf("uninitialized device_num in %v", status)
+ }
+
+ if status.BytesFree == 0 {
+ t.Errorf("uninitialized bytes_free in %v", status)
+ }
+
+ if status.BytesUsed == 0 {
+ t.Errorf("uninitialized bytes_used in %v", status)
+ }
+}
+
+// Invoke String for the volume; expect non-empty result
+func testString(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if id := v.String(); len(id) == 0 {
+ t.Error("Got empty string for v.String()")
+ }
+}
+
+// Verify Writable is true on a writable volume
+func testWritableTrue(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if v.Writable() == false {
+ t.Errorf("Expected writable to be true on a writable volume")
+ }
+}
+
+// Verify Writable is false on a read-only volume
+func testWritableFalse(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ if v.Writable() != false {
+ t.Errorf("Expected writable to be false on a read-only volume")
+ }
+}
+
+// Updating, touching, and deleting blocks from a read-only volume result in error.
+func testUpdateReadOnly(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.PutRaw(TEST_HASH, TEST_BLOCK)
+
+ _, err := v.Get(TEST_HASH)
+ if err != nil {
+ t.Errorf("got err %v, expected nil", err)
+ }
+
+ err = v.Put(TEST_HASH, TEST_BLOCK)
+ if err == nil {
+ t.Errorf("Expected error when putting block in a read-only volume")
+ }
+
+ err = v.Touch(TEST_HASH)
+ if err == nil {
+ t.Errorf("Expected error when touching block in a read-only volume")
+ }
+
+ err = v.Delete(TEST_HASH)
+ if err == nil {
+ t.Errorf("Expected error when deleting block from a read-only volume")
+ }
+}
+
+// Serialization tests: launch a bunch of concurrent
+//
+// TODO(twp): show that the underlying Read/Write operations executed
+// serially and not concurrently. The easiest way to do this is
+// probably to activate verbose or debug logging, capture log output
+// and examine it to confirm that Reads and Writes did not overlap.
+//
+// TODO(twp): a proper test of I/O serialization requires that a
+// second request start while the first one is still underway.
+// Guaranteeing that the test behaves this way requires some tricky
+// synchronization and mocking. For now we'll just launch a bunch of
+// requests simultaenously in goroutines and demonstrate that they
+// return accurate results.
+//
+
+func testGetSerialized(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ v.Put(TEST_HASH, TEST_BLOCK)
+ v.Put(TEST_HASH_2, TEST_BLOCK_2)
+ v.Put(TEST_HASH_3, TEST_BLOCK_3)
+
+ sem := make(chan int)
+ go func(sem chan int) {
+ buf, err := v.Get(TEST_HASH)
+ if err != nil {
+ t.Errorf("err1: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK) != 0 {
+ t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
+ }
+ sem <- 1
+ }(sem)
+
+ go func(sem chan int) {
+ buf, err := v.Get(TEST_HASH_2)
+ if err != nil {
+ t.Errorf("err2: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
+ t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
+ }
+ sem <- 1
+ }(sem)
+
+ go func(sem chan int) {
+ buf, err := v.Get(TEST_HASH_3)
+ if err != nil {
+ t.Errorf("err3: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
+ t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
+ }
+ sem <- 1
+ }(sem)
+
+ // Wait for all goroutines to finish
+ for done := 0; done < 3; {
+ done += <-sem
+ }
+}
+
+func testPutSerialized(t *testing.T, factory TestableVolumeFactory) {
+ v := factory(t)
+ defer v.Teardown()
+
+ sem := make(chan int)
+ go func(sem chan int) {
+ err := v.Put(TEST_HASH, TEST_BLOCK)
+ if err != nil {
+ t.Errorf("err1: %v", err)
+ }
+ sem <- 1
+ }(sem)
+
+ go func(sem chan int) {
+ err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
+ if err != nil {
+ t.Errorf("err2: %v", err)
+ }
+ sem <- 1
+ }(sem)
+
+ go func(sem chan int) {
+ err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
+ if err != nil {
+ t.Errorf("err3: %v", err)
+ }
+ sem <- 1
+ }(sem)
+
+ // Wait for all goroutines to finish
+ for done := 0; done < 3; {
+ done += <-sem
+ }
+
+ // Double check that we actually wrote the blocks we expected to write.
+ buf, err := v.Get(TEST_HASH)
+ if err != nil {
+ t.Errorf("Get #1: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK) != 0 {
+ t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
+ }
+
+ buf, err = v.Get(TEST_HASH_2)
+ if err != nil {
+ t.Errorf("Get #2: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
+ t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
+ }
+
+ buf, err = v.Get(TEST_HASH_3)
+ if err != nil {
+ t.Errorf("Get #3: %v", err)
+ }
+ if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
+ t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
+ }
+}
diff --git a/services/keepstore/volume_unix_test.go b/services/keepstore/volume_unix_test.go
index 9f37042..7fea560 100644
--- a/services/keepstore/volume_unix_test.go
+++ b/services/keepstore/volume_unix_test.go
@@ -7,8 +7,6 @@ import (
"io"
"io/ioutil"
"os"
- "regexp"
- "sort"
"strings"
"sync"
"syscall"
@@ -72,18 +70,10 @@ func TestUnixVolumeWithGenericTests(t *testing.T) {
})
}
-func TestGet(t *testing.T) {
- v := NewTestableUnixVolume(t, false, false)
- defer v.Teardown()
- v.Put(TEST_HASH, TEST_BLOCK)
-
- buf, err := v.Get(TEST_HASH)
- if err != nil {
- t.Error(err)
- }
- if bytes.Compare(buf, TEST_BLOCK) != 0 {
- t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
- }
+func TestUnixReadOnlyVolumeWithGenericTests(t *testing.T) {
+ DoGenericReadOnlyVolumeTests(t, func(t *testing.T) TestableVolume {
+ return NewTestableUnixVolume(t, false, true)
+ })
}
func TestGetNotFound(t *testing.T) {
@@ -102,42 +92,6 @@ func TestGetNotFound(t *testing.T) {
}
}
-func TestIndexTo(t *testing.T) {
- v := NewTestableUnixVolume(t, false, false)
- defer v.Teardown()
-
- v.Put(TEST_HASH, TEST_BLOCK)
- v.Put(TEST_HASH_2, TEST_BLOCK_2)
- v.Put(TEST_HASH_3, TEST_BLOCK_3)
-
- buf := new(bytes.Buffer)
- v.IndexTo("", buf)
- index_rows := strings.Split(string(buf.Bytes()), "\n")
- sort.Strings(index_rows)
- sorted_index := strings.Join(index_rows, "\n")
- m, err := regexp.MatchString(
- `^\n`+TEST_HASH+`\+\d+ \d+\n`+
- TEST_HASH_3+`\+\d+ \d+\n`+
- TEST_HASH_2+`\+\d+ \d+$`,
- sorted_index)
- if err != nil {
- t.Error(err)
- } else if !m {
- t.Errorf("Got index %q for empty prefix", sorted_index)
- }
-
- for _, prefix := range []string{"f", "f15", "f15ac"} {
- buf = new(bytes.Buffer)
- v.IndexTo(prefix, buf)
- m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
- if err != nil {
- t.Error(err)
- } else if !m {
- t.Errorf("Got index %q for prefix %q", string(buf.Bytes()), prefix)
- }
- }
-}
-
func TestPut(t *testing.T) {
v := NewTestableUnixVolume(t, false, false)
defer v.Teardown()
@@ -193,169 +147,6 @@ func TestUnixVolumeReadonly(t *testing.T) {
}
}
-// TestPutTouch
-// Test that when applying PUT to a block that already exists,
-// the block's modification time is updated.
-func TestPutTouch(t *testing.T) {
- v := NewTestableUnixVolume(t, false, false)
- defer v.Teardown()
-
- if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
- t.Error(err)
- }
-
- // We'll verify { t0 < threshold < t1 }, where t0 is the
- // existing block's timestamp on disk before Put() and t1 is
- // its timestamp after Put().
- threshold := time.Now().Add(-time.Second)
-
- // Set the stored block's mtime far enough in the past that we
- // can see the difference between "timestamp didn't change"
- // and "timestamp granularity is too low".
- v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
-
- // Make sure v.Mtime() agrees the above Utime really worked.
- if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
- t.Errorf("Setting mtime failed: %v, %v", t0, err)
- }
-
- // Write the same block again.
- if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
- t.Error(err)
- }
-
- // Verify threshold < t1
- if t1, err := v.Mtime(TEST_HASH); err != nil {
- t.Error(err)
- } else if t1.Before(threshold) {
- t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
- }
-}
-
-// Serialization tests: launch a bunch of concurrent
-//
-// TODO(twp): show that the underlying Read/Write operations executed
-// serially and not concurrently. The easiest way to do this is
-// probably to activate verbose or debug logging, capture log output
-// and examine it to confirm that Reads and Writes did not overlap.
-//
-// TODO(twp): a proper test of I/O serialization requires that a
-// second request start while the first one is still underway.
-// Guaranteeing that the test behaves this way requires some tricky
-// synchronization and mocking. For now we'll just launch a bunch of
-// requests simultaenously in goroutines and demonstrate that they
-// return accurate results.
-//
-func TestGetSerialized(t *testing.T) {
- // Create a volume with I/O serialization enabled.
- v := NewTestableUnixVolume(t, true, false)
- defer v.Teardown()
-
- v.Put(TEST_HASH, TEST_BLOCK)
- v.Put(TEST_HASH_2, TEST_BLOCK_2)
- v.Put(TEST_HASH_3, TEST_BLOCK_3)
-
- sem := make(chan int)
- go func(sem chan int) {
- buf, err := v.Get(TEST_HASH)
- if err != nil {
- t.Errorf("err1: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK) != 0 {
- t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
- }
- sem <- 1
- }(sem)
-
- go func(sem chan int) {
- buf, err := v.Get(TEST_HASH_2)
- if err != nil {
- t.Errorf("err2: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
- t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
- }
- sem <- 1
- }(sem)
-
- go func(sem chan int) {
- buf, err := v.Get(TEST_HASH_3)
- if err != nil {
- t.Errorf("err3: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
- t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
- }
- sem <- 1
- }(sem)
-
- // Wait for all goroutines to finish
- for done := 0; done < 3; {
- done += <-sem
- }
-}
-
-func TestPutSerialized(t *testing.T) {
- // Create a volume with I/O serialization enabled.
- v := NewTestableUnixVolume(t, true, false)
- defer v.Teardown()
-
- sem := make(chan int)
- go func(sem chan int) {
- err := v.Put(TEST_HASH, TEST_BLOCK)
- if err != nil {
- t.Errorf("err1: %v", err)
- }
- sem <- 1
- }(sem)
-
- go func(sem chan int) {
- err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
- if err != nil {
- t.Errorf("err2: %v", err)
- }
- sem <- 1
- }(sem)
-
- go func(sem chan int) {
- err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
- if err != nil {
- t.Errorf("err3: %v", err)
- }
- sem <- 1
- }(sem)
-
- // Wait for all goroutines to finish
- for done := 0; done < 3; {
- done += <-sem
- }
-
- // Double check that we actually wrote the blocks we expected to write.
- buf, err := v.Get(TEST_HASH)
- if err != nil {
- t.Errorf("Get #1: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK) != 0 {
- t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
- }
-
- buf, err = v.Get(TEST_HASH_2)
- if err != nil {
- t.Errorf("Get #2: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
- t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
- }
-
- buf, err = v.Get(TEST_HASH_3)
- if err != nil {
- t.Errorf("Get #3: %v", err)
- }
- if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
- t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
- }
-}
-
func TestIsFull(t *testing.T) {
v := NewTestableUnixVolume(t, false, false)
defer v.Teardown()
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list