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 TestReplicationDefault1(t *testing.T) {
109 if err := v.Start(); err != nil {
112 if got := v.Replication(); got != 1 {
113 t.Errorf("Replication() returned %d, expected 1 if no config given", got)
117 func TestGetNotFound(t *testing.T) {
118 v := NewTestableUnixVolume(t, false, false)
120 v.Put(TestHash, TestBlock)
122 buf := make([]byte, BlockSize)
123 n, err := v.Get(TestHash2, buf)
125 case os.IsNotExist(err):
128 t.Errorf("Read should have failed, returned %+q", buf[:n])
130 t.Errorf("Read expected ErrNotExist, got: %s", err)
134 func TestPut(t *testing.T) {
135 v := NewTestableUnixVolume(t, false, false)
138 err := v.Put(TestHash, TestBlock)
142 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
143 if buf, err := ioutil.ReadFile(p); err != nil {
145 } else if bytes.Compare(buf, TestBlock) != 0 {
146 t.Errorf("Write should have stored %s, did store %s",
147 string(TestBlock), string(buf))
151 func TestPutBadVolume(t *testing.T) {
152 v := NewTestableUnixVolume(t, false, false)
155 os.Chmod(v.Root, 000)
156 err := v.Put(TestHash, TestBlock)
158 t.Error("Write should have failed")
162 func TestUnixVolumeReadonly(t *testing.T) {
163 v := NewTestableUnixVolume(t, false, true)
166 v.PutRaw(TestHash, TestBlock)
168 buf := make([]byte, BlockSize)
169 _, err := v.Get(TestHash, buf)
171 t.Errorf("got err %v, expected nil", err)
174 err = v.Put(TestHash, TestBlock)
175 if err != MethodDisabledError {
176 t.Errorf("got err %v, expected MethodDisabledError", err)
179 err = v.Touch(TestHash)
180 if err != MethodDisabledError {
181 t.Errorf("got err %v, expected MethodDisabledError", err)
184 err = v.Trash(TestHash)
185 if err != MethodDisabledError {
186 t.Errorf("got err %v, expected MethodDisabledError", err)
190 func TestIsFull(t *testing.T) {
191 v := NewTestableUnixVolume(t, false, false)
194 fullPath := v.Root + "/full"
195 now := fmt.Sprintf("%d", time.Now().Unix())
196 os.Symlink(now, fullPath)
198 t.Errorf("%s: claims not to be full", v)
202 // Test with an expired /full link.
203 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
204 os.Symlink(expired, fullPath)
206 t.Errorf("%s: should no longer be full", v)
210 func TestNodeStatus(t *testing.T) {
211 v := NewTestableUnixVolume(t, false, false)
214 // Get node status and make a basic sanity check.
215 volinfo := v.Status()
216 if volinfo.MountPoint != v.Root {
217 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.Root)
219 if volinfo.DeviceNum == 0 {
220 t.Errorf("uninitialized device_num in %v", volinfo)
222 if volinfo.BytesFree == 0 {
223 t.Errorf("uninitialized bytes_free in %v", volinfo)
225 if volinfo.BytesUsed == 0 {
226 t.Errorf("uninitialized bytes_used in %v", volinfo)
230 func TestUnixVolumeGetFuncWorkerError(t *testing.T) {
231 v := NewTestableUnixVolume(t, false, false)
234 v.Put(TestHash, TestBlock)
235 mockErr := errors.New("Mock error")
236 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
240 t.Errorf("Got %v, expected %v", err, mockErr)
244 func TestUnixVolumeGetFuncFileError(t *testing.T) {
245 v := NewTestableUnixVolume(t, false, false)
249 err := v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
254 t.Errorf("Expected error opening non-existent file")
257 t.Errorf("Worker func should not have been called")
261 func TestUnixVolumeGetFuncWorkerWaitsOnMutex(t *testing.T) {
262 v := NewTestableUnixVolume(t, false, false)
265 v.Put(TestHash, TestBlock)
267 mtx := NewMockMutex()
270 funcCalled := make(chan struct{})
271 go v.getFunc(v.blockPath(TestHash), func(rdr io.Reader) error {
272 funcCalled <- struct{}{}
276 case mtx.AllowLock <- struct{}{}:
278 t.Fatal("Function was called before mutex was acquired")
279 case <-time.After(5 * time.Second):
280 t.Fatal("Timed out before mutex was acquired")
284 case mtx.AllowUnlock <- struct{}{}:
285 t.Fatal("Mutex was released before function was called")
286 case <-time.After(5 * time.Second):
287 t.Fatal("Timed out waiting for funcCalled")
290 case mtx.AllowUnlock <- struct{}{}:
291 case <-time.After(5 * time.Second):
292 t.Fatal("Timed out waiting for getFunc() to release mutex")
296 func TestUnixVolumeCompare(t *testing.T) {
297 v := NewTestableUnixVolume(t, false, false)
300 v.Put(TestHash, TestBlock)
301 err := v.Compare(TestHash, TestBlock)
303 t.Errorf("Got err %q, expected nil", err)
306 err = v.Compare(TestHash, []byte("baddata"))
307 if err != CollisionError {
308 t.Errorf("Got err %q, expected %q", err, CollisionError)
311 v.Put(TestHash, []byte("baddata"))
312 err = v.Compare(TestHash, TestBlock)
313 if err != DiskHashError {
314 t.Errorf("Got err %q, expected %q", err, DiskHashError)
317 p := fmt.Sprintf("%s/%s/%s", v.Root, TestHash[:3], TestHash)
319 err = v.Compare(TestHash, TestBlock)
320 if err == nil || strings.Index(err.Error(), "permission denied") < 0 {
321 t.Errorf("Got err %q, expected %q", err, "permission denied")
325 // TODO(twp): show that the underlying Read/Write operations executed
326 // serially and not concurrently. The easiest way to do this is
327 // probably to activate verbose or debug logging, capture log output
328 // and examine it to confirm that Reads and Writes did not overlap.
330 // TODO(twp): a proper test of I/O serialization requires that a
331 // second request start while the first one is still underway.
332 // Guaranteeing that the test behaves this way requires some tricky
333 // synchronization and mocking. For now we'll just launch a bunch of
334 // requests simultaenously in goroutines and demonstrate that they
335 // return accurate results.