18 type TestableUnixVolume struct {
23 func NewTestableUnixVolume(t TB, serialize bool, readonly bool) *TestableUnixVolume {
24 d, err := ioutil.TempDir("", "volume_test")
28 var locker sync.Locker
30 locker = &sync.Mutex{}
32 return &TestableUnixVolume{
33 UnixVolume: UnixVolume{
42 // PutRaw writes a Keep block directly into a UnixVolume, even if
43 // the volume is readonly.
44 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
45 defer func(orig bool) {
49 err := v.Put(context.Background(), locator, data)
55 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
56 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
62 func (v *TestableUnixVolume) Teardown() {
63 if err := os.RemoveAll(v.Root); err != nil {
68 // serialize = false; readonly = false
69 func TestUnixVolumeWithGenericTests(t *testing.T) {
70 DoGenericVolumeTests(t, func(t TB) TestableVolume {
71 return NewTestableUnixVolume(t, false, false)
75 // serialize = false; readonly = true
76 func TestUnixVolumeWithGenericTestsReadOnly(t *testing.T) {
77 DoGenericVolumeTests(t, func(t TB) TestableVolume {
78 return NewTestableUnixVolume(t, false, true)
82 // serialize = true; readonly = false
83 func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
84 DoGenericVolumeTests(t, func(t TB) TestableVolume {
85 return NewTestableUnixVolume(t, true, false)
89 // serialize = false; readonly = false
90 func TestUnixVolumeHandlersWithGenericVolumeTests(t *testing.T) {
91 DoHandlersWithGenericVolumeTests(t, func(t TB) (*RRVolumeManager, []TestableVolume) {
92 vols := make([]Volume, 2)
93 testableUnixVols := make([]TestableVolume, 2)
96 v := NewTestableUnixVolume(t, false, false)
98 testableUnixVols[i] = v
101 return MakeRRVolumeManager(vols), testableUnixVols
105 func TestReplicationDefault1(t *testing.T) {
110 if err := v.Start(); err != nil {
113 if got := v.Replication(); got != 1 {
114 t.Errorf("Replication() returned %d, expected 1 if no config given", got)
118 func TestGetNotFound(t *testing.T) {
119 v := NewTestableUnixVolume(t, false, false)
121 v.Put(context.Background(), TestHash, TestBlock)
123 buf := make([]byte, BlockSize)
124 n, err := v.Get(context.Background(), TestHash2, buf)
126 case os.IsNotExist(err):
129 t.Errorf("Read should have failed, returned %+q", buf[:n])
131 t.Errorf("Read expected ErrNotExist, got: %s", err)
135 func TestPut(t *testing.T) {
136 v := NewTestableUnixVolume(t, false, false)
139 err := v.Put(context.Background(), TestHash, TestBlock)
143 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
144 if buf, err := ioutil.ReadFile(p); err != nil {
146 } else if bytes.Compare(buf, TestBlock) != 0 {
147 t.Errorf("Write should have stored %s, did store %s",
148 string(TestBlock), string(buf))
152 func TestPutBadVolume(t *testing.T) {
153 v := NewTestableUnixVolume(t, false, false)
156 os.Chmod(v.Root, 000)
157 err := v.Put(context.Background(), TestHash, TestBlock)
159 t.Error("Write should have failed")
163 func TestUnixVolumeReadonly(t *testing.T) {
164 v := NewTestableUnixVolume(t, false, true)
167 v.PutRaw(TestHash, TestBlock)
169 buf := make([]byte, BlockSize)
170 _, err := v.Get(context.Background(), TestHash, buf)
172 t.Errorf("got err %v, expected nil", err)
175 err = v.Put(context.Background(), TestHash, TestBlock)
176 if err != MethodDisabledError {
177 t.Errorf("got err %v, expected MethodDisabledError", err)
180 err = v.Touch(TestHash)
181 if err != MethodDisabledError {
182 t.Errorf("got err %v, expected MethodDisabledError", err)
185 err = v.Trash(TestHash)
186 if err != MethodDisabledError {
187 t.Errorf("got err %v, expected MethodDisabledError", err)
191 func TestIsFull(t *testing.T) {
192 v := NewTestableUnixVolume(t, false, false)
195 fullPath := v.Root + "/full"
196 now := fmt.Sprintf("%d", time.Now().Unix())
197 os.Symlink(now, fullPath)
199 t.Errorf("%s: claims not to be full", v)
203 // Test with an expired /full link.
204 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
205 os.Symlink(expired, fullPath)
207 t.Errorf("%s: should no longer be full", v)
211 func TestNodeStatus(t *testing.T) {
212 v := NewTestableUnixVolume(t, false, false)
215 // Get node status and make a basic sanity check.
216 volinfo := v.Status()
217 if volinfo.MountPoint != v.Root {
218 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.Root)
220 if volinfo.DeviceNum == 0 {
221 t.Errorf("uninitialized device_num in %v", volinfo)
223 if volinfo.BytesFree == 0 {
224 t.Errorf("uninitialized bytes_free in %v", volinfo)
226 if volinfo.BytesUsed == 0 {
227 t.Errorf("uninitialized bytes_used in %v", volinfo)
231 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
232 v := NewTestableUnixVolume(t, false, false)
235 v.Put(context.Background(), TestHash, TestBlock)
236 mockErr := errors.New("Mock error")
237 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
241 t.Errorf("Got %v, expected %v", err, mockErr)
245 func TestUnixVolumeGetFuncFileError(t *testing.T) {
246 v := NewTestableUnixVolume(t, false, false)
250 err := v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
255 t.Errorf("Expected error opening non-existent file")
258 t.Errorf("Worker func should not have been called")
262 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
263 v := NewTestableUnixVolume(t, false, false)
266 v.Put(context.Background(), TestHash, TestBlock)
268 mtx := NewMockMutex()
271 funcCalled := make(chan struct{})
272 go v.getFunc(context.Background(), v.blockPath(TestHash), func(rdr io.Reader) error {
273 funcCalled <- struct{}{}
277 case mtx.AllowLock <- struct{}{}:
279 t.Fatal("Function was called before mutex was acquired")
280 case <-time.After(5 * time.Second):
281 t.Fatal("Timed out before mutex was acquired")
285 case mtx.AllowUnlock <- struct{}{}:
286 t.Fatal("Mutex was released before function was called")
287 case <-time.After(5 * time.Second):
288 t.Fatal("Timed out waiting for funcCalled")
291 case mtx.AllowUnlock <- struct{}{}:
292 case <-time.After(5 * time.Second):
293 t.Fatal("Timed out waiting for getFunc() to release mutex")
297 func TestUnixVolumeCompare(t *testing.T) {
298 v := NewTestableUnixVolume(t, false, false)
301 v.Put(context.Background(), TestHash, TestBlock)
302 err := v.Compare(context.Background(), TestHash, TestBlock)
304 t.Errorf("Got err %q, expected nil", err)
307 err = v.Compare(context.Background(), TestHash, []byte("baddata"))
308 if err != CollisionError {
309 t.Errorf("Got err %q, expected %q", err, CollisionError)
312 v.Put(context.Background(), TestHash, []byte("baddata"))
313 err = v.Compare(context.Background(), TestHash, TestBlock)
314 if err != DiskHashError {
315 t.Errorf("Got err %q, expected %q", err, DiskHashError)
318 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
320 err = v.Compare(context.Background(), TestHash, TestBlock)
321 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
322 t.Errorf("Got err %q, expected %q", err, "permission denied")
326 // TODO(twp): show that the underlying Read/Write operations executed
327 // serially and not concurrently. The easiest way to do this is
328 // probably to activate verbose or debug logging, capture log output
329 // and examine it to confirm that Reads and Writes did not overlap.
331 // TODO(twp): a proper test of I/O serialization requires that a
332 // second request start while the first one is still underway.
333 // Guaranteeing that the test behaves this way requires some tricky
334 // synchronization and mocking. For now we'll just launch a bunch of
335 // requests simultaenously in goroutines and demonstrate that they
336 // return accurate results.