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 := make([]byte, BlockSize)
110 n, err := v.Get(TestHash2, buf)
112 case os.IsNotExist(err):
115 t.Errorf("Read should have failed, returned %+q", buf[:n])
117 t.Errorf("Read expected ErrNotExist, got: %s", err)
121 func TestPut(t *testing.T) {
122 v := NewTestableUnixVolume(t, false, false)
125 err := v.Put(TestHash, TestBlock)
129 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
130 if buf, err := ioutil.ReadFile(p); err != nil {
132 } else if bytes.Compare(buf, TestBlock) != 0 {
133 t.Errorf("Write should have stored %s, did store %s",
134 string(TestBlock), string(buf))
138 func TestPutBadVolume(t *testing.T) {
139 v := NewTestableUnixVolume(t, false, false)
142 os.Chmod(v.Root, 000)
143 err := v.Put(TestHash, TestBlock)
145 t.Error("Write should have failed")
149 func TestUnixVolumeReadonly(t *testing.T) {
150 v := NewTestableUnixVolume(t, false, true)
153 v.PutRaw(TestHash, TestBlock)
155 buf := make([]byte, BlockSize)
156 _, err := v.Get(TestHash, buf)
158 t.Errorf("got err %v, expected nil", err)
161 err = v.Put(TestHash, TestBlock)
162 if err != MethodDisabledError {
163 t.Errorf("got err %v, expected MethodDisabledError", err)
166 err = v.Touch(TestHash)
167 if err != MethodDisabledError {
168 t.Errorf("got err %v, expected MethodDisabledError", err)
171 err = v.Trash(TestHash)
172 if err != MethodDisabledError {
173 t.Errorf("got err %v, expected MethodDisabledError", err)
177 func TestIsFull(t *testing.T) {
178 v := NewTestableUnixVolume(t, false, false)
181 fullPath := v.Root + "/full"
182 now := fmt.Sprintf("%d", time.Now().Unix())
183 os.Symlink(now, fullPath)
185 t.Errorf("%s: claims not to be full", v)
189 // Test with an expired /full link.
190 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
191 os.Symlink(expired, fullPath)
193 t.Errorf("%s: should no longer be full", v)
197 func TestNodeStatus(t *testing.T) {
198 v := NewTestableUnixVolume(t, false, false)
201 // Get node status and make a basic sanity check.
202 volinfo := v.Status()
203 if volinfo.MountPoint != v.Root {
204 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.Root)
206 if volinfo.DeviceNum == 0 {
207 t.Errorf("uninitialized device_num in %v", volinfo)
209 if volinfo.BytesFree == 0 {
210 t.Errorf("uninitialized bytes_free in %v", volinfo)
212 if volinfo.BytesUsed == 0 {
213 t.Errorf("uninitialized bytes_used in %v", volinfo)
217 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
218 v := NewTestableUnixVolume(t, false, false)
221 v.Put(TestHash, TestBlock)
222 mockErr := errors.New("Mock error")
223 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
227 t.Errorf("Got %v, expected %v", err, mockErr)
231 func TestUnixVolumeGetFuncFileError(t *testing.T) {
232 v := NewTestableUnixVolume(t, false, false)
236 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
241 t.Errorf("Expected error opening non-existent file")
244 t.Errorf("Worker func should not have been called")
248 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
249 v := NewTestableUnixVolume(t, false, false)
252 v.Put(TestHash, TestBlock)
254 mtx := NewMockMutex()
257 funcCalled := make(chan struct{})
258 go v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
259 funcCalled <- struct{}{}
263 case mtx.AllowLock <- struct{}{}:
265 t.Fatal("Function was called before mutex was acquired")
266 case <-time.After(5 * time.Second):
267 t.Fatal("Timed out before mutex was acquired")
271 case mtx.AllowUnlock <- struct{}{}:
272 t.Fatal("Mutex was released before function was called")
273 case <-time.After(5 * time.Second):
274 t.Fatal("Timed out waiting for funcCalled")
277 case mtx.AllowUnlock <- struct{}{}:
278 case <-time.After(5 * time.Second):
279 t.Fatal("Timed out waiting for getFunc() to release mutex")
283 func TestUnixVolumeCompare(t *testing.T) {
284 v := NewTestableUnixVolume(t, false, false)
287 v.Put(TestHash, TestBlock)
288 err := v.Compare(TestHash, TestBlock)
290 t.Errorf("Got err %q, expected nil", err)
293 err = v.Compare(TestHash, []byte("baddata"))
294 if err != CollisionError {
295 t.Errorf("Got err %q, expected %q", err, CollisionError)
298 v.Put(TestHash, []byte("baddata"))
299 err = v.Compare(TestHash, TestBlock)
300 if err != DiskHashError {
301 t.Errorf("Got err %q, expected %q", err, DiskHashError)
304 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
306 err = v.Compare(TestHash, TestBlock)
307 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
308 t.Errorf("Got err %q, expected %q", err, "permission denied")
312 // TODO(twp): show that the underlying Read/Write operations executed
313 // serially and not concurrently. The easiest way to do this is
314 // probably to activate verbose or debug logging, capture log output
315 // and examine it to confirm that Reads and Writes did not overlap.
317 // TODO(twp): a proper test of I/O serialization requires that a
318 // second request start while the first one is still underway.
319 // Guaranteeing that the test behaves this way requires some tricky
320 // synchronization and mocking. For now we'll just launch a bunch of
321 // requests simultaenously in goroutines and demonstrate that they
322 // return accurate results.