19 type TestableUnixVolume struct {
24 func NewTestableUnixVolume(t *testing.T, serialize bool, readonly bool) *TestableUnixVolume {
25 d, err := ioutil.TempDir("", "volume_test")
29 var locker sync.Locker
31 locker = &sync.Mutex{}
33 return &TestableUnixVolume{
34 UnixVolume: UnixVolume{
43 // PutRaw writes a Keep block directly into a UnixVolume, even if
44 // the volume is readonly.
45 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
46 defer func(orig bool) {
50 err := v.Put(locator, data)
56 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
57 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
63 func (v *TestableUnixVolume) Teardown() {
64 if err := os.RemoveAll(v.root); err != nil {
69 func TestUnixVolumeWithGenericTests(t *testing.T) {
70 DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
71 return NewTestableUnixVolume(t, false, false)
75 func TestGet(t *testing.T) {
76 v := NewTestableUnixVolume(t, false, false)
78 v.Put(TEST_HASH, TEST_BLOCK)
80 buf, err := v.Get(TEST_HASH)
84 if bytes.Compare(buf, TEST_BLOCK) != 0 {
85 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
89 func TestGetNotFound(t *testing.T) {
90 v := NewTestableUnixVolume(t, false, false)
92 v.Put(TEST_HASH, TEST_BLOCK)
94 buf, err := v.Get(TEST_HASH_2)
96 case os.IsNotExist(err):
99 t.Errorf("Read should have failed, returned %s", string(buf))
101 t.Errorf("Read expected ErrNotExist, got: %s", err)
105 func TestIndexTo(t *testing.T) {
106 v := NewTestableUnixVolume(t, false, false)
109 v.Put(TEST_HASH, TEST_BLOCK)
110 v.Put(TEST_HASH_2, TEST_BLOCK_2)
111 v.Put(TEST_HASH_3, TEST_BLOCK_3)
113 buf := new(bytes.Buffer)
115 index_rows := strings.Split(string(buf.Bytes()), "\n")
116 sort.Strings(index_rows)
117 sorted_index := strings.Join(index_rows, "\n")
118 m, err := regexp.MatchString(
119 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
120 TEST_HASH_3+`\+\d+ \d+\n`+
121 TEST_HASH_2+`\+\d+ \d+$`,
126 t.Errorf("Got index %q for empty prefix", sorted_index)
129 for _, prefix := range []string{"f", "f15", "f15ac"} {
130 buf = new(bytes.Buffer)
131 v.IndexTo(prefix, buf)
132 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
136 t.Errorf("Got index %q for prefix %q", string(buf.Bytes()), prefix)
141 func TestPut(t *testing.T) {
142 v := NewTestableUnixVolume(t, false, false)
145 err := v.Put(TEST_HASH, TEST_BLOCK)
149 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
150 if buf, err := ioutil.ReadFile(p); err != nil {
152 } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
153 t.Errorf("Write should have stored %s, did store %s",
154 string(TEST_BLOCK), string(buf))
158 func TestPutBadVolume(t *testing.T) {
159 v := NewTestableUnixVolume(t, false, false)
162 os.Chmod(v.root, 000)
163 err := v.Put(TEST_HASH, TEST_BLOCK)
165 t.Error("Write should have failed")
169 func TestUnixVolumeReadonly(t *testing.T) {
170 v := NewTestableUnixVolume(t, false, true)
173 v.PutRaw(TEST_HASH, TEST_BLOCK)
175 _, err := v.Get(TEST_HASH)
177 t.Errorf("got err %v, expected nil", err)
180 err = v.Put(TEST_HASH, TEST_BLOCK)
181 if err != MethodDisabledError {
182 t.Errorf("got err %v, expected MethodDisabledError", err)
185 err = v.Touch(TEST_HASH)
186 if err != MethodDisabledError {
187 t.Errorf("got err %v, expected MethodDisabledError", err)
190 err = v.Delete(TEST_HASH)
191 if err != MethodDisabledError {
192 t.Errorf("got err %v, expected MethodDisabledError", err)
197 // Test that when applying PUT to a block that already exists,
198 // the block's modification time is updated.
199 func TestPutTouch(t *testing.T) {
200 v := NewTestableUnixVolume(t, false, false)
203 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
207 // We'll verify { t0 < threshold < t1 }, where t0 is the
208 // existing block's timestamp on disk before Put() and t1 is
209 // its timestamp after Put().
210 threshold := time.Now().Add(-time.Second)
212 // Set the stored block's mtime far enough in the past that we
213 // can see the difference between "timestamp didn't change"
214 // and "timestamp granularity is too low".
215 v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
217 // Make sure v.Mtime() agrees the above Utime really worked.
218 if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
219 t.Errorf("Setting mtime failed: %v, %v", t0, err)
222 // Write the same block again.
223 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
227 // Verify threshold < t1
228 if t1, err := v.Mtime(TEST_HASH); err != nil {
230 } else if t1.Before(threshold) {
231 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
235 // Serialization tests: launch a bunch of concurrent
237 // TODO(twp): show that the underlying Read/Write operations executed
238 // serially and not concurrently. The easiest way to do this is
239 // probably to activate verbose or debug logging, capture log output
240 // and examine it to confirm that Reads and Writes did not overlap.
242 // TODO(twp): a proper test of I/O serialization requires that a
243 // second request start while the first one is still underway.
244 // Guaranteeing that the test behaves this way requires some tricky
245 // synchronization and mocking. For now we'll just launch a bunch of
246 // requests simultaenously in goroutines and demonstrate that they
247 // return accurate results.
249 func TestGetSerialized(t *testing.T) {
250 // Create a volume with I/O serialization enabled.
251 v := NewTestableUnixVolume(t, true, false)
254 v.Put(TEST_HASH, TEST_BLOCK)
255 v.Put(TEST_HASH_2, TEST_BLOCK_2)
256 v.Put(TEST_HASH_3, TEST_BLOCK_3)
258 sem := make(chan int)
259 go func(sem chan int) {
260 buf, err := v.Get(TEST_HASH)
262 t.Errorf("err1: %v", err)
264 if bytes.Compare(buf, TEST_BLOCK) != 0 {
265 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
270 go func(sem chan int) {
271 buf, err := v.Get(TEST_HASH_2)
273 t.Errorf("err2: %v", err)
275 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
276 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
281 go func(sem chan int) {
282 buf, err := v.Get(TEST_HASH_3)
284 t.Errorf("err3: %v", err)
286 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
287 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
292 // Wait for all goroutines to finish
293 for done := 0; done < 3; {
298 func TestPutSerialized(t *testing.T) {
299 // Create a volume with I/O serialization enabled.
300 v := NewTestableUnixVolume(t, true, false)
303 sem := make(chan int)
304 go func(sem chan int) {
305 err := v.Put(TEST_HASH, TEST_BLOCK)
307 t.Errorf("err1: %v", err)
312 go func(sem chan int) {
313 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
315 t.Errorf("err2: %v", err)
320 go func(sem chan int) {
321 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
323 t.Errorf("err3: %v", err)
328 // Wait for all goroutines to finish
329 for done := 0; done < 3; {
333 // Double check that we actually wrote the blocks we expected to write.
334 buf, err := v.Get(TEST_HASH)
336 t.Errorf("Get #1: %v", err)
338 if bytes.Compare(buf, TEST_BLOCK) != 0 {
339 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
342 buf, err = v.Get(TEST_HASH_2)
344 t.Errorf("Get #2: %v", err)
346 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
347 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
350 buf, err = v.Get(TEST_HASH_3)
352 t.Errorf("Get #3: %v", err)
354 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
355 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
359 func TestIsFull(t *testing.T) {
360 v := NewTestableUnixVolume(t, false, false)
363 full_path := v.root + "/full"
364 now := fmt.Sprintf("%d", time.Now().Unix())
365 os.Symlink(now, full_path)
367 t.Errorf("%s: claims not to be full", v)
371 // Test with an expired /full link.
372 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
373 os.Symlink(expired, full_path)
375 t.Errorf("%s: should no longer be full", v)
379 func TestNodeStatus(t *testing.T) {
380 v := NewTestableUnixVolume(t, false, false)
383 // Get node status and make a basic sanity check.
384 volinfo := v.Status()
385 if volinfo.MountPoint != v.root {
386 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
388 if volinfo.DeviceNum == 0 {
389 t.Errorf("uninitialized device_num in %v", volinfo)
391 if volinfo.BytesFree == 0 {
392 t.Errorf("uninitialized bytes_free in %v", volinfo)
394 if volinfo.BytesUsed == 0 {
395 t.Errorf("uninitialized bytes_used in %v", volinfo)
399 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
400 v := NewTestableUnixVolume(t, false, false)
403 v.Put(TEST_HASH, TEST_BLOCK)
404 mockErr := errors.New("Mock error")
405 err := v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
409 t.Errorf("Got %v, expected %v", err, mockErr)
413 func TestUnixVolumeGetFuncFileError(t *testing.T) {
414 v := NewTestableUnixVolume(t, false, false)
418 err := v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
423 t.Errorf("Expected error opening non-existent file")
426 t.Errorf("Worker func should not have been called")
430 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
431 v := NewTestableUnixVolume(t, false, false)
434 v.Put(TEST_HASH, TEST_BLOCK)
436 mtx := NewMockMutex()
439 funcCalled := make(chan struct{})
440 go v.getFunc(v.blockPath(TEST_HASH), func(rdr io.Reader) error {
441 funcCalled <- struct{}{}
445 case mtx.AllowLock <- struct{}{}:
447 t.Fatal("Function was called before mutex was acquired")
448 case <-time.After(5 * time.Second):
449 t.Fatal("Timed out before mutex was acquired")
453 case mtx.AllowUnlock <- struct{}{}:
454 t.Fatal("Mutex was released before function was called")
455 case <-time.After(5 * time.Second):
456 t.Fatal("Timed out waiting for funcCalled")
459 case mtx.AllowUnlock <- struct{}{}:
460 case <-time.After(5 * time.Second):
461 t.Fatal("Timed out waiting for getFunc() to release mutex")
465 func TestUnixVolumeCompare(t *testing.T) {
466 v := NewTestableUnixVolume(t, false, false)
469 v.Put(TEST_HASH, TEST_BLOCK)
470 err := v.Compare(TEST_HASH, TEST_BLOCK)
472 t.Errorf("Got err %q, expected nil", err)
475 err = v.Compare(TEST_HASH, []byte("baddata"))
476 if err != CollisionError {
477 t.Errorf("Got err %q, expected %q", err, CollisionError)
480 v.Put(TEST_HASH, []byte("baddata"))
481 err = v.Compare(TEST_HASH, TEST_BLOCK)
482 if err != DiskHashError {
483 t.Errorf("Got err %q, expected %q", err, DiskHashError)
486 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
488 err = v.Compare(TEST_HASH, TEST_BLOCK)
489 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
490 t.Errorf("Got err %q, expected %q", err, "permission denied")