[ARVADOS] created: 8dd54802b2ada6da4fd6faac6e4fba2e82d2fb11

git at public.curoverse.com git at public.curoverse.com
Tue Sep 8 00:08:30 EDT 2015


        at  8dd54802b2ada6da4fd6faac6e4fba2e82d2fb11 (commit)


commit 8dd54802b2ada6da4fd6faac6e4fba2e82d2fb11
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Sep 8 00:08:14 2015 -0400

    7179: Add TestableVolume interface, make UnixVolume tests a bit more reusable.

diff --git a/services/keepstore/volume_test.go b/services/keepstore/volume_test.go
index c5a7491..34dcbdc 100644
--- a/services/keepstore/volume_test.go
+++ b/services/keepstore/volume_test.go
@@ -10,6 +10,21 @@ import (
 	"time"
 )
 
+// A TestableVolume allows test suites to manipulate the state of an
+// underlying Volume, in order to test behavior in cases that are
+// impractical to achieve with a sequence of normal Volume operations.
+type TestableVolume interface {
+	Volume
+	// [Over]write content for a locator with the given data,
+	// bypassing all constraints like readonly and serialize.
+	PutRaw(locator string, data []byte)
+	// Specify the value Mtime() should return, until the next
+	// call to Touch, TouchWithDate, or Put.
+	TouchWithDate(locator string, lastPut time.Time)
+	// Clean up, delete temporary files.
+	Teardown()
+}
+
 // MockVolumes are test doubles for Volumes, used to test handlers.
 type MockVolume struct {
 	Store      map[string][]byte
diff --git a/services/keepstore/volume_unix_test.go b/services/keepstore/volume_unix_test.go
index ebb8421..83df9ce 100644
--- a/services/keepstore/volume_unix_test.go
+++ b/services/keepstore/volume_unix_test.go
@@ -13,44 +13,56 @@ import (
 	"time"
 )
 
-func TempUnixVolume(t *testing.T, serialize bool, readonly bool) *UnixVolume {
+type TestableUnixVolume struct {
+	UnixVolume
+	t *testing.T
+}
+
+func NewTestableUnixVolume(t *testing.T, serialize bool, readonly bool) *TestableUnixVolume {
 	d, err := ioutil.TempDir("", "volume_test")
 	if err != nil {
 		t.Fatal(err)
 	}
-	return &UnixVolume{
-		root:      d,
-		serialize: serialize,
-		readonly:  readonly,
+	return &TestableUnixVolume{
+		UnixVolume: UnixVolume{
+			root:      d,
+			serialize: serialize,
+			readonly:  readonly,
+		},
+		t: t,
 	}
 }
 
-func _teardown(v *UnixVolume) {
-	os.RemoveAll(v.root)
+// PutRaw writes a Keep block directly into a UnixVolume, even if
+// the volume is readonly.
+func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
+	defer func(orig bool) {
+		v.readonly = orig
+	}(v.readonly)
+	v.readonly = false
+	err := v.Put(locator, data)
+	if err != nil {
+		v.t.Fatal(err)
+	}
 }
 
-// _store writes a Keep block directly into a UnixVolume, bypassing
-// the overhead and safeguards of Put(). Useful for storing bogus data
-// and isolating unit tests from Put() behavior.
-func _store(t *testing.T, vol *UnixVolume, filename string, block []byte) {
-	blockdir := fmt.Sprintf("%s/%s", vol.root, filename[:3])
-	if err := os.MkdirAll(blockdir, 0755); err != nil {
-		t.Fatal(err)
+func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
+	err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
+	if err != nil {
+		v.t.Fatal(err)
 	}
+}
 
-	blockpath := fmt.Sprintf("%s/%s", blockdir, filename)
-	if f, err := os.Create(blockpath); err == nil {
-		f.Write(block)
-		f.Close()
-	} else {
-		t.Fatal(err)
+func (v *TestableUnixVolume) Teardown() {
+	if err := os.RemoveAll(v.root); err != nil {
+		v.t.Fatal(err)
 	}
 }
 
 func TestGet(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
-	_store(t, v, TEST_HASH, TEST_BLOCK)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
+	v.Put(TEST_HASH, TEST_BLOCK)
 
 	buf, err := v.Get(TEST_HASH)
 	if err != nil {
@@ -62,9 +74,9 @@ func TestGet(t *testing.T) {
 }
 
 func TestGetNotFound(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
-	_store(t, v, TEST_HASH, TEST_BLOCK)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
+	v.Put(TEST_HASH, TEST_BLOCK)
 
 	buf, err := v.Get(TEST_HASH_2)
 	switch {
@@ -78,12 +90,12 @@ func TestGetNotFound(t *testing.T) {
 }
 
 func TestIndexTo(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
-	_store(t, v, TEST_HASH, TEST_BLOCK)
-	_store(t, v, TEST_HASH_2, TEST_BLOCK_2)
-	_store(t, v, TEST_HASH_3, TEST_BLOCK_3)
+	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)
@@ -114,8 +126,8 @@ func TestIndexTo(t *testing.T) {
 }
 
 func TestPut(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
 	err := v.Put(TEST_HASH, TEST_BLOCK)
 	if err != nil {
@@ -131,8 +143,8 @@ func TestPut(t *testing.T) {
 }
 
 func TestPutBadVolume(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
 	os.Chmod(v.root, 000)
 	err := v.Put(TEST_HASH, TEST_BLOCK)
@@ -142,35 +154,62 @@ func TestPutBadVolume(t *testing.T) {
 }
 
 func TestUnixVolumeReadonly(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, true)
+	defer v.Teardown()
 
-	// First write something before marking readonly
-	err := v.Put(TEST_HASH, TEST_BLOCK)
-	if err != nil {
-		t.Error("got err %v, expected nil", err)
-	}
-
-	v.readonly = true
+	v.PutRaw(TEST_HASH, TEST_BLOCK)
 
-	_, err = v.Get(TEST_HASH)
+	_, err := v.Get(TEST_HASH)
 	if err != nil {
-		t.Error("got err %v, expected nil", err)
+		t.Errorf("got err %v, expected nil", err)
 	}
 
 	err = v.Put(TEST_HASH, TEST_BLOCK)
 	if err != MethodDisabledError {
-		t.Error("got err %v, expected MethodDisabledError", err)
+		t.Errorf("got err %v, expected MethodDisabledError", err)
 	}
 
 	err = v.Touch(TEST_HASH)
 	if err != MethodDisabledError {
-		t.Error("got err %v, expected MethodDisabledError", err)
+		t.Errorf("got err %v, expected MethodDisabledError", err)
 	}
 
 	err = v.Delete(TEST_HASH)
 	if err != MethodDisabledError {
-		t.Error("got err %v, expected MethodDisabledError", err)
+		t.Errorf("got err %v, expected MethodDisabledError", err)
+	}
+}
+
+func TestUnixVolumeDeleteNewBlock(t *testing.T) {
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
+
+	v.Put(TEST_HASH, TEST_BLOCK)
+
+	if err := v.Delete(TEST_HASH); err != nil {
+		t.Error(err)
+	}
+	// This isn't reported as an error, but the block should not
+	// have been deleted: it's newer than blob_signature_ttl.
+	if data, err := v.Get(TEST_HASH); err != nil {
+		t.Error(err)
+	} else if bytes.Compare(data, TEST_BLOCK) != 0 {
+		t.Error("Content incorrect after delete failed")
+	}
+}
+
+func TestUnixVolumeDeleteOldBlock(t *testing.T) {
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
+
+	v.Put(TEST_HASH, TEST_BLOCK)
+	v.TouchWithDate(TEST_HASH, time.Now().Add(-2*blob_signature_ttl*time.Second))
+
+	if err := v.Delete(TEST_HASH); err != nil {
+		t.Error(err)
+	}
+	if _, err := v.Get(TEST_HASH); !os.IsNotExist(err) {
+		t.Errorf("os.IsNotExist(%v) should have been true", err.Error())
 	}
 }
 
@@ -178,8 +217,8 @@ func TestUnixVolumeReadonly(t *testing.T) {
 //     Test that when applying PUT to a block that already exists,
 //     the block's modification time is updated.
 func TestPutTouch(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
 	if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
 		t.Error(err)
@@ -193,17 +232,11 @@ func TestPutTouch(t *testing.T) {
 	// 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".
-	{
-		oldtime := time.Now().Add(-20 * time.Second).Unix()
-		if err := syscall.Utime(v.blockPath(TEST_HASH),
-			&syscall.Utimbuf{oldtime, oldtime}); err != nil {
-			t.Error(err)
-		}
+	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)
-		}
+	// 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.
@@ -212,13 +245,10 @@ func TestPutTouch(t *testing.T) {
 	}
 
 	// Verify threshold < t1
-	t1, err := v.Mtime(TEST_HASH)
-	if err != nil {
+	if t1, err := v.Mtime(TEST_HASH); err != nil {
 		t.Error(err)
-	}
-	if t1.Before(threshold) {
-		t.Errorf("t1 %v must be >= threshold %v after v.Put ",
-			t1, threshold)
+	} else if t1.Before(threshold) {
+		t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
 	}
 }
 
@@ -238,12 +268,12 @@ func TestPutTouch(t *testing.T) {
 //
 func TestGetSerialized(t *testing.T) {
 	// Create a volume with I/O serialization enabled.
-	v := TempUnixVolume(t, true, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, true, false)
+	defer v.Teardown()
 
-	_store(t, v, TEST_HASH, TEST_BLOCK)
-	_store(t, v, TEST_HASH_2, TEST_BLOCK_2)
-	_store(t, v, TEST_HASH_3, TEST_BLOCK_3)
+	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) {
@@ -287,8 +317,8 @@ func TestGetSerialized(t *testing.T) {
 
 func TestPutSerialized(t *testing.T) {
 	// Create a volume with I/O serialization enabled.
-	v := TempUnixVolume(t, true, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, true, false)
+	defer v.Teardown()
 
 	sem := make(chan int)
 	go func(sem chan int) {
@@ -347,8 +377,8 @@ func TestPutSerialized(t *testing.T) {
 }
 
 func TestIsFull(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
 	full_path := v.root + "/full"
 	now := fmt.Sprintf("%d", time.Now().Unix())
@@ -367,8 +397,8 @@ func TestIsFull(t *testing.T) {
 }
 
 func TestNodeStatus(t *testing.T) {
-	v := TempUnixVolume(t, false, false)
-	defer _teardown(v)
+	v := NewTestableUnixVolume(t, false, false)
+	defer v.Teardown()
 
 	// Get node status and make a basic sanity check.
 	volinfo := v.Status()

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list