"fmt"
"io/ioutil"
"os"
+ "regexp"
+ "sort"
+ "strings"
"syscall"
"testing"
"time"
)
-func TempUnixVolume(t *testing.T, serialize 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 MakeUnixVolume(d, serialize)
+ return &TestableUnixVolume{
+ UnixVolume: UnixVolume{
+ root: d,
+ serialize: serialize,
+ readonly: readonly,
+ },
+ t: t,
+ }
}
-func _teardown(v UnixVolume) {
- if v.queue != nil {
- close(v.queue)
+// 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)
}
- os.RemoveAll(v.root)
}
-// store writes a Keep block directly into a UnixVolume, for testing
-// UnixVolume methods.
-//
-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 TestUnixVolumeWithGenericTests(t *testing.T) {
+ DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
+ return NewTestableUnixVolume(t, false, false)
+ })
+}
+
func TestGet(t *testing.T) {
- v := TempUnixVolume(t, 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 {
}
func TestGetNotFound(t *testing.T) {
- v := TempUnixVolume(t, 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 {
}
}
+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 := TempUnixVolume(t, false)
- defer _teardown(v)
+ v := NewTestableUnixVolume(t, false, false)
+ defer v.Teardown()
err := v.Put(TEST_HASH, TEST_BLOCK)
if err != nil {
}
func TestPutBadVolume(t *testing.T) {
- v := TempUnixVolume(t, false)
- defer _teardown(v)
+ v := NewTestableUnixVolume(t, false, false)
+ defer v.Teardown()
os.Chmod(v.root, 000)
err := v.Put(TEST_HASH, TEST_BLOCK)
}
}
+func TestUnixVolumeReadonly(t *testing.T) {
+ v := NewTestableUnixVolume(t, false, true)
+ 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 != MethodDisabledError {
+ t.Errorf("got err %v, expected MethodDisabledError", err)
+ }
+
+ err = v.Touch(TEST_HASH)
+ if err != MethodDisabledError {
+ t.Errorf("got err %v, expected MethodDisabledError", err)
+ }
+
+ err = v.Delete(TEST_HASH)
+ if err != MethodDisabledError {
+ t.Errorf("got err %v, expected MethodDisabledError", err)
+ }
+}
+
// 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 := TempUnixVolume(t, 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)
// 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.
}
// 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)
}
}
//
func TestGetSerialized(t *testing.T) {
// Create a volume with I/O serialization enabled.
- v := TempUnixVolume(t, true)
- 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) {
func TestPutSerialized(t *testing.T) {
// Create a volume with I/O serialization enabled.
- v := TempUnixVolume(t, true)
- defer _teardown(v)
+ v := NewTestableUnixVolume(t, true, false)
+ defer v.Teardown()
sem := make(chan int)
go func(sem chan int) {
}(sem)
// Wait for all goroutines to finish
- for done := 0; done < 2; {
+ for done := 0; done < 3; {
done += <-sem
}
}
func TestIsFull(t *testing.T) {
- v := TempUnixVolume(t, false)
- defer _teardown(v)
+ v := NewTestableUnixVolume(t, false, false)
+ defer v.Teardown()
full_path := v.root + "/full"
now := fmt.Sprintf("%d", time.Now().Unix())
t.Errorf("%s: should no longer be full", v)
}
}
+
+func TestNodeStatus(t *testing.T) {
+ v := NewTestableUnixVolume(t, false, false)
+ defer v.Teardown()
+
+ // Get node status and make a basic sanity check.
+ volinfo := v.Status()
+ if volinfo.MountPoint != v.root {
+ t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
+ }
+ if volinfo.DeviceNum == 0 {
+ t.Errorf("uninitialized device_num in %v", volinfo)
+ }
+ if volinfo.BytesFree == 0 {
+ t.Errorf("uninitialized bytes_free in %v", volinfo)
+ }
+ if volinfo.BytesUsed == 0 {
+ t.Errorf("uninitialized bytes_used in %v", volinfo)
+ }
+}