18 check "gopkg.in/check.v1"
21 type TestableUnixVolume struct {
26 func NewTestableUnixVolume(t TB, serialize bool, readonly bool) *TestableUnixVolume {
27 d, err := ioutil.TempDir("", "volume_test")
31 var locker sync.Locker
33 locker = &sync.Mutex{}
35 return &TestableUnixVolume{
36 UnixVolume: UnixVolume{
45 // PutRaw writes a Keep block directly into a UnixVolume, even if
46 // the volume is readonly.
47 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
48 defer func(orig bool) {
52 err := v.Put(context.Background(), locator, data)
58 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
59 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
65 func (v *TestableUnixVolume) Teardown() {
66 if err := os.RemoveAll(v.Root); err != nil {
71 // serialize = false; readonly = false
72 func TestUnixVolumeWithGenericTests(t *testing.T) {
73 DoGenericVolumeTests(t, func(t TB) TestableVolume {
74 return NewTestableUnixVolume(t, false, false)
78 // serialize = false; readonly = true
79 func TestUnixVolumeWithGenericTestsReadOnly(t *testing.T) {
80 DoGenericVolumeTests(t, func(t TB) TestableVolume {
81 return NewTestableUnixVolume(t, false, true)
85 // serialize = true; readonly = false
86 func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
87 DoGenericVolumeTests(t, func(t TB) TestableVolume {
88 return NewTestableUnixVolume(t, true, false)
92 // serialize = false; readonly = false
93 func TestUnixVolumeHandlersWithGenericVolumeTests(t *testing.T) {
94 DoHandlersWithGenericVolumeTests(t, func(t TB) (*RRVolumeManager, []TestableVolume) {
95 vols := make([]Volume, 2)
96 testableUnixVols := make([]TestableVolume, 2)
99 v := NewTestableUnixVolume(t, false, false)
101 testableUnixVols[i] = v
104 return MakeRRVolumeManager(vols), testableUnixVols
108 func TestReplicationDefault1(t *testing.T) {
113 if err := v.Start(); err != nil {
116 if got := v.Replication(); got != 1 {
117 t.Errorf("Replication() returned %d, expected 1 if no config given", got)
121 func TestGetNotFound(t *testing.T) {
122 v := NewTestableUnixVolume(t, false, false)
124 v.Put(context.Background(), TestHash, TestBlock)
126 buf := make([]byte, BlockSize)
127 n, err := v.Get(context.Background(), TestHash2, buf)
129 case os.IsNotExist(err):
132 t.Errorf("Read should have failed, returned %+q", buf[:n])
134 t.Errorf("Read expected ErrNotExist, got: %s", err)
138 func TestPut(t *testing.T) {
139 v := NewTestableUnixVolume(t, false, false)
142 err := v.Put(context.Background(), TestHash, TestBlock)
146 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
147 if buf, err := ioutil.ReadFile(p); err != nil {
149 } else if bytes.Compare(buf, TestBlock) != 0 {
150 t.Errorf("Write should have stored %s, did store %s",
151 string(TestBlock), string(buf))
155 func TestPutBadVolume(t *testing.T) {
156 v := NewTestableUnixVolume(t, false, false)
159 os.Chmod(v.Root, 000)
160 err := v.Put(context.Background(), TestHash, TestBlock)
162 t.Error("Write should have failed")
166 func TestUnixVolumeReadonly(t *testing.T) {
167 v := NewTestableUnixVolume(t, false, true)
170 v.PutRaw(TestHash, TestBlock)
172 buf := make([]byte, BlockSize)
173 _, err := v.Get(context.Background(), TestHash, buf)
175 t.Errorf("got err %v, expected nil", err)
178 err = v.Put(context.Background(), TestHash, TestBlock)
179 if err != MethodDisabledError {
180 t.Errorf("got err %v, expected MethodDisabledError", err)
183 err = v.Touch(TestHash)
184 if err != MethodDisabledError {
185 t.Errorf("got err %v, expected MethodDisabledError", err)
188 err = v.Trash(TestHash)
189 if err != MethodDisabledError {
190 t.Errorf("got err %v, expected MethodDisabledError", err)
194 func TestIsFull(t *testing.T) {
195 v := NewTestableUnixVolume(t, false, false)
198 fullPath := v.Root + "/full"
199 now := fmt.Sprintf("%d", time.Now().Unix())
200 os.Symlink(now, fullPath)
202 t.Errorf("%s: claims not to be full", v)
206 // Test with an expired /full link.
207 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
208 os.Symlink(expired, fullPath)
210 t.Errorf("%s: should no longer be full", v)
214 func TestNodeStatus(t *testing.T) {
215 v := NewTestableUnixVolume(t, false, false)
218 // Get node status and make a basic sanity check.
219 volinfo := v.Status()
220 if volinfo.MountPoint != v.Root {
221 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.Root)
223 if volinfo.DeviceNum == 0 {
224 t.Errorf("uninitialized device_num in %v", volinfo)
226 if volinfo.BytesFree == 0 {
227 t.Errorf("uninitialized bytes_free in %v", volinfo)
229 if volinfo.BytesUsed == 0 {
230 t.Errorf("uninitialized bytes_used in %v", volinfo)
234 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
235 v := NewTestableUnixVolume(t, false, false)
238 v.Put(context.Background(), TestHash, TestBlock)
239 mockErr := errors.New("Mock error")
240 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
244 t.Errorf("Got %v, expected %v", err, mockErr)
248 func TestUnixVolumeGetFuncFileError(t *testing.T) {
249 v := NewTestableUnixVolume(t, false, false)
253 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
258 t.Errorf("Expected error opening non-existent file")
261 t.Errorf("Worker func should not have been called")
265 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
266 v := NewTestableUnixVolume(t, false, false)
269 v.Put(context.Background(), TestHash, TestBlock)
271 mtx := NewMockMutex()
274 funcCalled := make(chan struct{})
275 go v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
276 funcCalled <- struct{}{}
280 case mtx.AllowLock <- struct{}{}:
282 t.Fatal("Function was called before mutex was acquired")
283 case <-time.After(5 * time.Second):
284 t.Fatal("Timed out before mutex was acquired")
288 case mtx.AllowUnlock <- struct{}{}:
289 t.Fatal("Mutex was released before function was called")
290 case <-time.After(5 * time.Second):
291 t.Fatal("Timed out waiting for funcCalled")
294 case mtx.AllowUnlock <- struct{}{}:
295 case <-time.After(5 * time.Second):
296 t.Fatal("Timed out waiting for getFunc() to release mutex")
300 func TestUnixVolumeCompare(t *testing.T) {
301 v := NewTestableUnixVolume(t, false, false)
304 v.Put(context.Background(), TestHash, TestBlock)
305 err := v.Compare(context.Background(), TestHash, TestBlock)
307 t.Errorf("Got err %q, expected nil", err)
310 err = v.Compare(context.Background(), TestHash, []byte("baddata"))
311 if err != CollisionError {
312 t.Errorf("Got err %q, expected %q", err, CollisionError)
315 v.Put(context.Background(), TestHash, []byte("baddata"))
316 err = v.Compare(context.Background(), TestHash, TestBlock)
317 if err != DiskHashError {
318 t.Errorf("Got err %q, expected %q", err, DiskHashError)
321 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
323 err = v.Compare(context.Background(), TestHash, TestBlock)
324 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
325 t.Errorf("Got err %q, expected %q", err, "permission denied")
329 func TestUnixVolumeContextCancelPut(t *testing.T) {
330 v := NewTestableUnixVolume(t, true, false)
333 ctx, cancel := context.WithCancel(context.Background())
335 time.Sleep(50 * time.Millisecond)
337 time.Sleep(50 * time.Millisecond)
340 err := v.Put(ctx, TestHash, TestBlock)
341 if err != context.Canceled {
342 t.Errorf("Put() returned %s -- expected short read / canceled", err)
346 func TestUnixVolumeContextCancelGet(t *testing.T) {
347 v := NewTestableUnixVolume(t, false, false)
349 bpath := v.blockPath(TestHash)
350 v.PutRaw(TestHash, TestBlock)
352 err := syscall.Mkfifo(bpath, 0600)
354 t.Fatalf("Mkfifo %s: %s", bpath, err)
356 defer os.Remove(bpath)
357 ctx, cancel := context.WithCancel(context.Background())
359 time.Sleep(50 * time.Millisecond)
362 buf := make([]byte, len(TestBlock))
363 n, err := v.Get(ctx, TestHash, buf)
364 if n == len(TestBlock) || err != context.Canceled {
365 t.Errorf("Get() returned %d, %s -- expected short read / canceled", n, err)
369 var _ = check.Suite(&UnixVolumeSuite{})
371 type UnixVolumeSuite struct {
372 volume *TestableUnixVolume
375 func (s *UnixVolumeSuite) TearDownTest(c *check.C) {
381 func (s *UnixVolumeSuite) TestStats(c *check.C) {
382 s.volume = NewTestableUnixVolume(c, false, false)
383 stats := func() string {
384 buf, err := json.Marshal(s.volume.InternalStats())
385 c.Check(err, check.IsNil)
389 c.Check(stats(), check.Matches, `.*"StatOps":0,.*`)
390 c.Check(stats(), check.Matches, `.*"Errors":0,.*`)
392 loc := "acbd18db4cc2f85cedef654fccc4a4d8"
393 _, err := s.volume.Get(context.Background(), loc, make([]byte, 3))
394 c.Check(err, check.NotNil)
395 c.Check(stats(), check.Matches, `.*"StatOps":[^0],.*`)
396 c.Check(stats(), check.Matches, `.*"Errors":[^0],.*`)
397 c.Check(stats(), check.Matches, `.*"\*os\.PathError":[^0].*`)
398 c.Check(stats(), check.Matches, `.*"InBytes":0,.*`)
399 c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
400 c.Check(stats(), check.Matches, `.*"CreateOps":0,.*`)
402 err = s.volume.Put(context.Background(), loc, []byte("foo"))
403 c.Check(err, check.IsNil)
404 c.Check(stats(), check.Matches, `.*"OutBytes":3,.*`)
405 c.Check(stats(), check.Matches, `.*"CreateOps":1,.*`)
406 c.Check(stats(), check.Matches, `.*"OpenOps":0,.*`)
407 c.Check(stats(), check.Matches, `.*"UtimesOps":0,.*`)
409 err = s.volume.Touch(loc)
410 c.Check(err, check.IsNil)
411 c.Check(stats(), check.Matches, `.*"FlockOps":1,.*`)
412 c.Check(stats(), check.Matches, `.*"OpenOps":1,.*`)
413 c.Check(stats(), check.Matches, `.*"UtimesOps":1,.*`)
415 _, err = s.volume.Get(context.Background(), loc, make([]byte, 3))
416 c.Check(err, check.IsNil)
417 err = s.volume.Compare(context.Background(), loc, []byte("foo"))
418 c.Check(err, check.IsNil)
419 c.Check(stats(), check.Matches, `.*"InBytes":6,.*`)
420 c.Check(stats(), check.Matches, `.*"OpenOps":3,.*`)
422 err = s.volume.Trash(loc)
423 c.Check(err, check.IsNil)
424 c.Check(stats(), check.Matches, `.*"FlockOps":2,.*`)