16 type TestableUnixVolume struct {
21 func NewTestableUnixVolume(t *testing.T, serialize bool, readonly bool) *TestableUnixVolume {
22 d, err := ioutil.TempDir("", "volume_test")
26 return &TestableUnixVolume{
27 UnixVolume: UnixVolume{
36 // PutRaw writes a Keep block directly into a UnixVolume, even if
37 // the volume is readonly.
38 func (v *TestableUnixVolume) PutRaw(locator string, data []byte) {
39 defer func(orig bool) {
43 err := v.Put(locator, data)
49 func (v *TestableUnixVolume) TouchWithDate(locator string, lastPut time.Time) {
50 err := syscall.Utime(v.blockPath(locator), &syscall.Utimbuf{lastPut.Unix(), lastPut.Unix()})
56 func (v *TestableUnixVolume) Teardown() {
57 if err := os.RemoveAll(v.root); err != nil {
62 func TestUnixVolumeWithGenericTests(t *testing.T) {
63 DoGenericVolumeTests(t, func(t *testing.T) TestableVolume {
64 return NewTestableUnixVolume(t, false, false)
68 func TestGet(t *testing.T) {
69 v := NewTestableUnixVolume(t, false, false)
71 v.Put(TEST_HASH, TEST_BLOCK)
73 buf, err := v.Get(TEST_HASH)
77 if bytes.Compare(buf, TEST_BLOCK) != 0 {
78 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
82 func TestGetNotFound(t *testing.T) {
83 v := NewTestableUnixVolume(t, false, false)
85 v.Put(TEST_HASH, TEST_BLOCK)
87 buf, err := v.Get(TEST_HASH_2)
89 case os.IsNotExist(err):
92 t.Errorf("Read should have failed, returned %s", string(buf))
94 t.Errorf("Read expected ErrNotExist, got: %s", err)
98 func TestIndexTo(t *testing.T) {
99 v := NewTestableUnixVolume(t, false, false)
102 v.Put(TEST_HASH, TEST_BLOCK)
103 v.Put(TEST_HASH_2, TEST_BLOCK_2)
104 v.Put(TEST_HASH_3, TEST_BLOCK_3)
106 buf := new(bytes.Buffer)
108 index_rows := strings.Split(string(buf.Bytes()), "\n")
109 sort.Strings(index_rows)
110 sorted_index := strings.Join(index_rows, "\n")
111 m, err := regexp.MatchString(
112 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
113 TEST_HASH_3+`\+\d+ \d+\n`+
114 TEST_HASH_2+`\+\d+ \d+$`,
119 t.Errorf("Got index %q for empty prefix", sorted_index)
122 for _, prefix := range []string{"f", "f15", "f15ac"} {
123 buf = new(bytes.Buffer)
124 v.IndexTo(prefix, buf)
125 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
129 t.Errorf("Got index %q for prefix %q", string(buf.Bytes()), prefix)
134 func TestPut(t *testing.T) {
135 v := NewTestableUnixVolume(t, false, false)
138 err := v.Put(TEST_HASH, TEST_BLOCK)
142 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
143 if buf, err := ioutil.ReadFile(p); err != nil {
145 } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
146 t.Errorf("Write should have stored %s, did store %s",
147 string(TEST_BLOCK), string(buf))
151 func TestPutBadVolume(t *testing.T) {
152 v := NewTestableUnixVolume(t, false, false)
155 os.Chmod(v.root, 000)
156 err := v.Put(TEST_HASH, TEST_BLOCK)
158 t.Error("Write should have failed")
162 func TestUnixVolumeReadonly(t *testing.T) {
163 v := NewTestableUnixVolume(t, false, true)
166 v.PutRaw(TEST_HASH, TEST_BLOCK)
168 _, err := v.Get(TEST_HASH)
170 t.Errorf("got err %v, expected nil", err)
173 err = v.Put(TEST_HASH, TEST_BLOCK)
174 if err != MethodDisabledError {
175 t.Errorf("got err %v, expected MethodDisabledError", err)
178 err = v.Touch(TEST_HASH)
179 if err != MethodDisabledError {
180 t.Errorf("got err %v, expected MethodDisabledError", err)
183 err = v.Delete(TEST_HASH)
184 if err != MethodDisabledError {
185 t.Errorf("got err %v, expected MethodDisabledError", err)
190 // Test that when applying PUT to a block that already exists,
191 // the block's modification time is updated.
192 func TestPutTouch(t *testing.T) {
193 v := NewTestableUnixVolume(t, false, false)
196 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
200 // We'll verify { t0 < threshold < t1 }, where t0 is the
201 // existing block's timestamp on disk before Put() and t1 is
202 // its timestamp after Put().
203 threshold := time.Now().Add(-time.Second)
205 // Set the stored block's mtime far enough in the past that we
206 // can see the difference between "timestamp didn't change"
207 // and "timestamp granularity is too low".
208 v.TouchWithDate(TEST_HASH, time.Now().Add(-20*time.Second))
210 // Make sure v.Mtime() agrees the above Utime really worked.
211 if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
212 t.Errorf("Setting mtime failed: %v, %v", t0, err)
215 // Write the same block again.
216 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
220 // Verify threshold < t1
221 if t1, err := v.Mtime(TEST_HASH); err != nil {
223 } else if t1.Before(threshold) {
224 t.Errorf("t1 %v should be >= threshold %v after v.Put ", t1, threshold)
228 // Serialization tests: launch a bunch of concurrent
230 // TODO(twp): show that the underlying Read/Write operations executed
231 // serially and not concurrently. The easiest way to do this is
232 // probably to activate verbose or debug logging, capture log output
233 // and examine it to confirm that Reads and Writes did not overlap.
235 // TODO(twp): a proper test of I/O serialization requires that a
236 // second request start while the first one is still underway.
237 // Guaranteeing that the test behaves this way requires some tricky
238 // synchronization and mocking. For now we'll just launch a bunch of
239 // requests simultaenously in goroutines and demonstrate that they
240 // return accurate results.
242 func TestGetSerialized(t *testing.T) {
243 // Create a volume with I/O serialization enabled.
244 v := NewTestableUnixVolume(t, true, false)
247 v.Put(TEST_HASH, TEST_BLOCK)
248 v.Put(TEST_HASH_2, TEST_BLOCK_2)
249 v.Put(TEST_HASH_3, TEST_BLOCK_3)
251 sem := make(chan int)
252 go func(sem chan int) {
253 buf, err := v.Get(TEST_HASH)
255 t.Errorf("err1: %v", err)
257 if bytes.Compare(buf, TEST_BLOCK) != 0 {
258 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
263 go func(sem chan int) {
264 buf, err := v.Get(TEST_HASH_2)
266 t.Errorf("err2: %v", err)
268 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
269 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
274 go func(sem chan int) {
275 buf, err := v.Get(TEST_HASH_3)
277 t.Errorf("err3: %v", err)
279 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
280 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
285 // Wait for all goroutines to finish
286 for done := 0; done < 3; {
291 func TestPutSerialized(t *testing.T) {
292 // Create a volume with I/O serialization enabled.
293 v := NewTestableUnixVolume(t, true, false)
296 sem := make(chan int)
297 go func(sem chan int) {
298 err := v.Put(TEST_HASH, TEST_BLOCK)
300 t.Errorf("err1: %v", err)
305 go func(sem chan int) {
306 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
308 t.Errorf("err2: %v", err)
313 go func(sem chan int) {
314 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
316 t.Errorf("err3: %v", err)
321 // Wait for all goroutines to finish
322 for done := 0; done < 3; {
326 // Double check that we actually wrote the blocks we expected to write.
327 buf, err := v.Get(TEST_HASH)
329 t.Errorf("Get #1: %v", err)
331 if bytes.Compare(buf, TEST_BLOCK) != 0 {
332 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
335 buf, err = v.Get(TEST_HASH_2)
337 t.Errorf("Get #2: %v", err)
339 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
340 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
343 buf, err = v.Get(TEST_HASH_3)
345 t.Errorf("Get #3: %v", err)
347 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
348 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
352 func TestIsFull(t *testing.T) {
353 v := NewTestableUnixVolume(t, false, false)
356 full_path := v.root + "/full"
357 now := fmt.Sprintf("%d", time.Now().Unix())
358 os.Symlink(now, full_path)
360 t.Errorf("%s: claims not to be full", v)
364 // Test with an expired /full link.
365 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
366 os.Symlink(expired, full_path)
368 t.Errorf("%s: should no longer be full", v)
372 func TestNodeStatus(t *testing.T) {
373 v := NewTestableUnixVolume(t, false, false)
376 // Get node status and make a basic sanity check.
377 volinfo := v.Status()
378 if volinfo.MountPoint != v.root {
379 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
381 if volinfo.DeviceNum == 0 {
382 t.Errorf("uninitialized device_num in %v", volinfo)
384 if volinfo.BytesFree == 0 {
385 t.Errorf("uninitialized bytes_free in %v", volinfo)
387 if volinfo.BytesUsed == 0 {
388 t.Errorf("uninitialized bytes_used in %v", volinfo)