17 type TestableUnixVolume struct {
22 func NewTestableUnixVolume(t TB, serialize bool, readonly bool) *TestableUnixVolume {
23 d, err := ioutil.TempDir("", "volume_test")
27 var locker sync.Locker
29 locker = &sync.Mutex{}
31 return &TestableUnixVolume{
32 UnixVolume: UnixVolume{
41 // PutRaw writes a Keep block directly into a UnixVolume, even if
42 // the volume is readonly.
43 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
44 defer func(orig bool) {
48 err := v.Put(locator, data)
54 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
55 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
61 func (v *TestableUnixVolume) Teardown() {
62 if err := os.RemoveAll(v.root); err != nil {
67 // serialize = false; readonly = false
68 func TestUnixVolumeWithGenericTests(t *testing.T) {
69 DoGenericVolumeTests(t, func(t TB) TestableVolume {
70 return NewTestableUnixVolume(t, false, false)
74 // serialize = false; readonly = true
75 func TestUnixVolumeWithGenericTestsReadOnly(t *testing.T) {
76 DoGenericVolumeTests(t, func(t TB) TestableVolume {
77 return NewTestableUnixVolume(t, false, true)
81 // serialize = true; readonly = false
82 func TestUnixVolumeWithGenericTestsSerialized(t *testing.T) {
83 DoGenericVolumeTests(t, func(t TB) TestableVolume {
84 return NewTestableUnixVolume(t, true, false)
88 // serialize = false; readonly = false
89 func TestUnixVolumeHandlersWithGenericVolumeTests(t *testing.T) {
90 DoHandlersWithGenericVolumeTests(t, func(t TB) (*RRVolumeManager, []TestableVolume) {
91 vols := make([]Volume, 2)
92 testableUnixVols := make([]TestableVolume, 2)
95 v := NewTestableUnixVolume(t, false, false)
97 testableUnixVols[i] = v
100 return MakeRRVolumeManager(vols), testableUnixVols
104 func TestGetNotFound(t *testing.T) {
105 v := NewTestableUnixVolume(t, false, false)
107 v.Put(TestHash, TestBlock)
109 buf, err := v.Get(TestHash2)
111 case os.IsNotExist(err):
114 t.Errorf("Read should have failed, returned %s", string(buf))
116 t.Errorf("Read expected ErrNotExist, got: %s", err)
120 func TestPut(t *testing.T) {
121 v := NewTestableUnixVolume(t, false, false)
124 err := v.Put(TestHash, TestBlock)
128 p := fmt.Sprintf("%s/%s/%s", v.root, TestHash[:3], TestHash)
129 if buf, err := ioutil.ReadFile(p); err != nil {
131 } else if bytes.Compare(buf, TestBlock) != 0 {
132 t.Errorf("Write should have stored %s, did store %s",
133 string(TestBlock), string(buf))
137 func TestPutBadVolume(t *testing.T) {
138 v := NewTestableUnixVolume(t, false, false)
141 os.Chmod(v.root, 000)
142 err := v.Put(TestHash, TestBlock)
144 t.Error("Write should have failed")
148 func TestUnixVolumeReadonly(t *testing.T) {
149 v := NewTestableUnixVolume(t, false, true)
152 v.PutRaw(TestHash, TestBlock)
154 _, err := v.Get(TestHash)
156 t.Errorf("got err %v, expected nil", err)
159 err = v.Put(TestHash, TestBlock)
160 if err != MethodDisabledError {
161 t.Errorf("got err %v, expected MethodDisabledError", err)
164 err = v.Touch(TestHash)
165 if err != MethodDisabledError {
166 t.Errorf("got err %v, expected MethodDisabledError", err)
169 err = v.Trash(TestHash)
170 if err != MethodDisabledError {
171 t.Errorf("got err %v, expected MethodDisabledError", err)
175 func TestIsFull(t *testing.T) {
176 v := NewTestableUnixVolume(t, false, false)
179 fullPath := v.root + "/full"
180 now := fmt.Sprintf("%d", time.Now().Unix())
181 os.Symlink(now, fullPath)
183 t.Errorf("%s: claims not to be full", v)
187 // Test with an expired /full link.
188 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
189 os.Symlink(expired, fullPath)
191 t.Errorf("%s: should no longer be full", v)
195 func TestNodeStatus(t *testing.T) {
196 v := NewTestableUnixVolume(t, false, false)
199 // Get node status and make a basic sanity check.
200 volinfo := v.Status()
201 if volinfo.MountPoint != v.root {
202 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
204 if volinfo.DeviceNum == 0 {
205 t.Errorf("uninitialized device_num in %v", volinfo)
207 if volinfo.BytesFree == 0 {
208 t.Errorf("uninitialized bytes_free in %v", volinfo)
210 if volinfo.BytesUsed == 0 {
211 t.Errorf("uninitialized bytes_used in %v", volinfo)
215 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
216 v := NewTestableUnixVolume(t, false, false)
219 v.Put(TestHash, TestBlock)
220 mockErr := errors.New("Mock error")
221 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
225 t.Errorf("Got %v, expected %v", err, mockErr)
229 func TestUnixVolumeGetFuncFileError(t *testing.T) {
230 v := NewTestableUnixVolume(t, false, false)
234 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
239 t.Errorf("Expected error opening non-existent file")
242 t.Errorf("Worker func should not have been called")
246 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
247 v := NewTestableUnixVolume(t, false, false)
250 v.Put(TestHash, TestBlock)
252 mtx := NewMockMutex()
255 funcCalled := make(chan struct{})
256 go v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
257 funcCalled <- struct{}{}
261 case mtx.AllowLock <- struct{}{}:
263 t.Fatal("Function was called before mutex was acquired")
264 case <-time.After(5 * time.Second):
265 t.Fatal("Timed out before mutex was acquired")
269 case mtx.AllowUnlock <- struct{}{}:
270 t.Fatal("Mutex was released before function was called")
271 case <-time.After(5 * time.Second):
272 t.Fatal("Timed out waiting for funcCalled")
275 case mtx.AllowUnlock <- struct{}{}:
276 case <-time.After(5 * time.Second):
277 t.Fatal("Timed out waiting for getFunc() to release mutex")
281 func TestUnixVolumeCompare(t *testing.T) {
282 v := NewTestableUnixVolume(t, false, false)
285 v.Put(TestHash, TestBlock)
286 err := v.Compare(TestHash, TestBlock)
288 t.Errorf("Got err %q, expected nil", err)
291 err = v.Compare(TestHash, []byte("baddata"))
292 if err != CollisionError {
293 t.Errorf("Got err %q, expected %q", err, CollisionError)
296 v.Put(TestHash, []byte("baddata"))
297 err = v.Compare(TestHash, TestBlock)
298 if err != DiskHashError {
299 t.Errorf("Got err %q, expected %q", err, DiskHashError)
302 p := fmt.Sprintf("%s/%s/%s", v.root, TestHash[:3], TestHash)
304 err = v.Compare(TestHash, TestBlock)
305 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
306 t.Errorf("Got err %q, expected %q", err, "permission denied")
310 // TODO(twp): show that the underlying Read/Write operations executed
311 // serially and not concurrently. The easiest way to do this is
312 // probably to activate verbose or debug logging, capture log output
313 // and examine it to confirm that Reads and Writes did not overlap.
315 // TODO(twp): a proper test of I/O serialization requires that a
316 // second request start while the first one is still underway.
317 // Guaranteeing that the test behaves this way requires some tricky
318 // synchronization and mocking. For now we'll just launch a bunch of
319 // requests simultaenously in goroutines and demonstrate that they
320 // return accurate results.