7329: add generic volume based keepstore tests to have better functional test coverage.
[arvados.git] / services / keepstore / volume_unix_test.go
index f23a9c9c8ee450981c373c167823733ea15306fc..4fd6a30e63b9181266b7deb298a313a54ec48653 100644 (file)
@@ -64,36 +64,51 @@ func (v *TestableUnixVolume) Teardown() {
        }
 }
 
+// serialize = false; readonly = false
 func TestUnixVolumeWithGenericTests(t *testing.T) {
        DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
                return NewTestableUnixVolume(t, false, false)
        })
 }
 
-func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
+// serialize = false; readonly = true
+func TestUnixVolumeWithGenericTestsReadOnly(t *testing.T) {
        DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
-               return NewTestableUnixVolume(t, true, false)
+               return NewTestableUnixVolume(t, false, true)
        })
 }
 
-func TestUnixReadOnlyVolumeWithGenericTests(t *testing.T) {
-       DoGenericReadOnlyVolumeTests(t, func(t *testing.T) TestableVolume {
-               return NewTestableUnixVolume(t, false, true)
+// serialize = true; readonly = false
+func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
+       DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
+               return NewTestableUnixVolume(t, true, false)
        })
 }
 
-func TestUnixReadOnlyVolumeWithGenericTestsSerialized(t *testing.T) {
-       DoGenericReadOnlyVolumeTests(t, func(t *testing.T) TestableVolume {
-               return NewTestableUnixVolume(t, true, true)
+// serialize = false; readonly = false
+func TestUnixVolumeManagerWithGenericTests(t *testing.T) {
+       DoGenericVolumeFunctionalTests(t, func(t *testing.T) []TestableVolume {
+               vols := make([]Volume, 2)
+               testableUnixVols := make([]TestableVolume, 2)
+
+               for i := range vols {
+                       v := NewTestableUnixVolume(t, false, false)
+                       vols[i] = v
+                       testableUnixVols[i] = v
+               }
+
+               KeepVM = MakeRRVolumeManager(vols)
+
+               return testableUnixVols
        })
 }
 
 func TestGetNotFound(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
-       v.Put(TEST_HASH, TEST_BLOCK)
+       v.Put(TestHash, TestBlock)
 
-       buf, err := v.Get(TEST_HASH_2)
+       buf, err := v.Get(TestHash2)
        switch {
        case os.IsNotExist(err):
                break
@@ -108,16 +123,16 @@ func TestPut(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
 
-       err := v.Put(TEST_HASH, TEST_BLOCK)
+       err := v.Put(TestHash, TestBlock)
        if err != nil {
                t.Error(err)
        }
-       p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
+       p := fmt.Sprintf("%s/%s/%s", v.root, TestHash[:3], TestHash)
        if buf, err := ioutil.ReadFile(p); err != nil {
                t.Error(err)
-       } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
+       } else if bytes.Compare(buf, TestBlock) != 0 {
                t.Errorf("Write should have stored %s, did store %s",
-                       string(TEST_BLOCK), string(buf))
+                       string(TestBlock), string(buf))
        }
 }
 
@@ -126,7 +141,7 @@ func TestPutBadVolume(t *testing.T) {
        defer v.Teardown()
 
        os.Chmod(v.root, 000)
-       err := v.Put(TEST_HASH, TEST_BLOCK)
+       err := v.Put(TestHash, TestBlock)
        if err == nil {
                t.Error("Write should have failed")
        }
@@ -136,24 +151,24 @@ func TestUnixVolumeReadonly(t *testing.T) {
        v := NewTestableUnixVolume(t, false, true)
        defer v.Teardown()
 
-       v.PutRaw(TEST_HASH, TEST_BLOCK)
+       v.PutRaw(TestHash, TestBlock)
 
-       _, err := v.Get(TEST_HASH)
+       _, err := v.Get(TestHash)
        if err != nil {
                t.Errorf("got err %v, expected nil", err)
        }
 
-       err = v.Put(TEST_HASH, TEST_BLOCK)
+       err = v.Put(TestHash, TestBlock)
        if err != MethodDisabledError {
                t.Errorf("got err %v, expected MethodDisabledError", err)
        }
 
-       err = v.Touch(TEST_HASH)
+       err = v.Touch(TestHash)
        if err != MethodDisabledError {
                t.Errorf("got err %v, expected MethodDisabledError", err)
        }
 
-       err = v.Delete(TEST_HASH)
+       err = v.Delete(TestHash)
        if err != MethodDisabledError {
                t.Errorf("got err %v, expected MethodDisabledError", err)
        }
@@ -163,17 +178,17 @@ func TestIsFull(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
 
-       full_path := v.root + "/full"
+       fullPath := v.root + "/full"
        now := fmt.Sprintf("%d", time.Now().Unix())
-       os.Symlink(now, full_path)
+       os.Symlink(now, fullPath)
        if !v.IsFull() {
                t.Errorf("%s: claims not to be full", v)
        }
-       os.Remove(full_path)
+       os.Remove(fullPath)
 
        // Test with an expired /full link.
        expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
-       os.Symlink(expired, full_path)
+       os.Symlink(expired, fullPath)
        if v.IsFull() {
                t.Errorf("%s: should no longer be full", v)
        }
@@ -203,9 +218,9 @@ func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
 
-       v.Put(TEST_HASH, TEST_BLOCK)
+       v.Put(TestHash, TestBlock)
        mockErr := errors.New("Mock error")
-       err := v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
+       err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
                return mockErr
        })
        if err != mockErr {
@@ -218,7 +233,7 @@ func TestUnixVolumeGetFuncFileError(t *testing.T) {
        defer v.Teardown()
 
        funcCalled := false
-       err := v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
+       err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
                funcCalled = true
                return nil
        })
@@ -234,13 +249,13 @@ func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
 
-       v.Put(TEST_HASH, TEST_BLOCK)
+       v.Put(TestHash, TestBlock)
 
        mtx := NewMockMutex()
        v.locker = mtx
 
        funcCalled := make(chan struct{})
-       go v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
+       go v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
                funcCalled <- struct{}{}
                return nil
        })
@@ -269,31 +284,90 @@ func TestUnixVolumeCompare(t *testing.T) {
        v := NewTestableUnixVolume(t, false, false)
        defer v.Teardown()
 
-       v.Put(TEST_HASH, TEST_BLOCK)
-       err := v.Compare(TEST_HASH, TEST_BLOCK)
+       v.Put(TestHash, TestBlock)
+       err := v.Compare(TestHash, TestBlock)
        if err != nil {
                t.Errorf("Got err %q, expected nil", err)
        }
 
-       err = v.Compare(TEST_HASH, []byte("baddata"))
+       err = v.Compare(TestHash, []byte("baddata"))
        if err != CollisionError {
                t.Errorf("Got err %q, expected %q", err, CollisionError)
        }
 
-       v.Put(TEST_HASH, []byte("baddata"))
-       err = v.Compare(TEST_HASH, TEST_BLOCK)
+       v.Put(TestHash, []byte("baddata"))
+       err = v.Compare(TestHash, TestBlock)
        if err != DiskHashError {
                t.Errorf("Got err %q, expected %q", err, DiskHashError)
        }
 
-       p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
+       p := fmt.Sprintf("%s/%s/%s", v.root, TestHash[:3], TestHash)
        os.Chmod(p, 000)
-       err = v.Compare(TEST_HASH, TEST_BLOCK)
+       err = v.Compare(TestHash, TestBlock)
        if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
                t.Errorf("Got err %q, expected %q", err, "permission denied")
        }
 }
 
+// Put an EmptyBlock and get and compare for EmptyHash
+// With #7329 unresolved, Compare falls in infinite loop
+func TestGetAndCompareEmptyBlock(t *testing.T) {
+       v := NewTestableUnixVolume(t, false, false)
+       defer v.Teardown()
+
+       v.Put(EmptyHash, EmptyBlock)
+
+       buf, err := v.Get(EmptyHash)
+       if err != nil {
+               t.Errorf("Error during Get for %q: %s", EmptyHash, err)
+       }
+
+       err = v.Compare(EmptyHash, buf)
+       if err != nil {
+               t.Errorf("Error during Compare for %q: %s", EmptyHash, err)
+       }
+}
+
+// Put baddata for EmptyHash. Get will succeed, but Compare will raise DiskHashError
+func TestGetAndCompareEmptyHashWithDiskHashError(t *testing.T) {
+       v := NewTestableUnixVolume(t, false, false)
+       defer v.Teardown()
+
+       v.PutRaw(EmptyHash, []byte("baddata"))
+
+       _, err := v.Get(EmptyHash)
+       if err != nil {
+               t.Errorf("Unexpected error after PutRaw EmptyHash with baddata")
+       }
+
+       err = v.Compare(EmptyHash, EmptyBlock)
+       if err != DiskHashError {
+               t.Errorf("Expected DiskHashError when comparing EmptyHash with bad data. Got %s", err)
+       }
+}
+
+// Get non existing empty block; should fail with file not found error.
+func TestGetEmptyBlockNonExisting(t *testing.T) {
+       v := NewTestableUnixVolume(t, false, false)
+       defer v.Teardown()
+
+       buf, err := v.Get(EmptyHash)
+       if err == nil {
+               t.Errorf("Get non existing empty hash should have failed, instead got %q", buf)
+       }
+}
+
+// Compare non existing empty hash should fail with file not found error.
+func TestCompareEmptyHashNonExisting(t *testing.T) {
+       v := NewTestableUnixVolume(t, false, false)
+       defer v.Teardown()
+
+       err := v.Compare(EmptyHash, EmptyBlock)
+       if err == nil {
+               t.Errorf("Expected error no such file. But got no error.")
+       }
+}
+
 // 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