}
}
-// PutBlockCollision
+// TestPutBlockCollision
// PutBlock returns a 400 Collision error when attempting to
// store a block that collides with another block on disk.
//
}
}
+// TestPutBlockTouchFails
+// When PutBlock is asked to PUT an existing block, but cannot
+// modify the timestamp, it should write a second block.
+//
+func TestPutBlockTouchFails(t *testing.T) {
+ defer teardown()
+
+ // Prepare two test Keep volumes.
+ KeepVM = MakeTestVolumeManager(2)
+ defer func() { KeepVM.Quit() }()
+ vols := KeepVM.Volumes()
+
+ // Store a block and then make the underlying volume bad,
+ // so a subsequent attempt to update the file timestamp
+ // will fail.
+ vols[0].Put(TEST_HASH, BAD_BLOCK)
+ old_mtime, err := vols[0].Mtime(TEST_HASH)
+ if err != nil {
+ t.Fatalf("vols[0].Mtime(%s): %s\n", TEST_HASH, err)
+ }
+
+ // Mark the volume bad and call PutBlock.
+ vols[0].(*MockVolume).Bad = true
+ if err := PutBlock(TEST_BLOCK, TEST_HASH); err != nil {
+ t.Fatalf("PutBlock: %v", err)
+ }
+ vols[0].(*MockVolume).Bad = false
+
+ // Now the mtime on the block on vols[0] should be unchanged, and
+ // there should be a copy of the block on vols[1].
+ new_mtime, err := vols[0].Mtime(TEST_HASH)
+ if err != nil {
+ t.Fatalf("vols[0].Mtime(%s): %s\n", TEST_HASH, err)
+ }
+ if !new_mtime.Equal(old_mtime) {
+ t.Errorf("bad block mtimes do not match:\nold_mtime = %v\nnew_mtime = %v\n",
+ old_mtime, new_mtime)
+ }
+ result, err := vols[1].Get(TEST_HASH)
+ if err != nil {
+ t.Fatalf("vols[1]: %v", err)
+ }
+ if bytes.Compare(result, TEST_BLOCK) != 0 {
+ t.Errorf("new block does not match test block\nnew block = %v\n", result)
+ }
+}
+
// ========================================
// FindKeepVolumes tests.
// ========================================
Get(loc string) ([]byte, error)
Put(loc string, block []byte) error
Touch(loc string) error
+ Mtime(loc string) (time.Time, error)
Index(prefix string) string
Delete(loc string) error
Status() *VolumeStatus
return nil
}
+func (v *MockVolume) Mtime(loc string) (time.Time, error) {
+ var mtime time.Time
+ var err error
+ if v.Bad {
+ err = errors.New("Bad volume")
+ } else if t, ok := v.Timestamps[loc]; ok {
+ mtime = t
+ } else {
+ err = os.ErrNotExist
+ }
+ return mtime, err
+}
+
func (v *MockVolume) Index(prefix string) string {
var result string
for loc, block := range v.Store {
return syscall.Utime(p, &utime)
}
+func (v *UnixVolume) Mtime(loc string) (time.Time, error) {
+ p := v.blockPath(loc)
+ if fi, err := os.Stat(p); err != nil {
+ return time.Time{}, err
+ } else {
+ return fi.ModTime(), nil
+ }
+}
+
// Read retrieves a block identified by the locator string "loc", and
// returns its contents as a byte slice.
//
if err != nil {
return err
}
- lockfile(f)
+ if e := lockfile(f); e != nil {
+ return e
+ }
defer unlockfile(f)
// If the block has been PUT more recently than -permission_ttl,
}
}
+// 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)
+
+ if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
+ t.Error(err)
+ }
+ old_mtime, err := v.Mtime(TEST_HASH)
+ if err != nil {
+ t.Error(err)
+ }
+ if old_mtime.IsZero() {
+ t.Errorf("v.Mtime(%s) returned a zero mtime\n", TEST_HASH)
+ }
+ // Sleep for 1s, then put the block again. The volume
+ // should report a more recent mtime.
+ // TODO(twp): this would be better handled with a mock Time object.
+ time.Sleep(time.Second)
+ if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
+ t.Error(err)
+ }
+ new_mtime, err := v.Mtime(TEST_HASH)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if !new_mtime.After(old_mtime) {
+ t.Errorf("v.Put did not update the block mtime:\nold_mtime = %v\nnew_mtime = %v\n",
+ old_mtime, new_mtime)
+ }
+}
+
// Serialization tests: launch a bunch of concurrent
//
// TODO(twp): show that the underlying Read/Write operations executed