16 func TempUnixVolume(t *testing.T, serialize bool, readonly bool) *UnixVolume {
17 d, err := ioutil.TempDir("", "volume_test")
28 func _teardown(v *UnixVolume) {
32 // _store writes a Keep block directly into a UnixVolume, bypassing
33 // the overhead and safeguards of Put(). Useful for storing bogus data
34 // and isolating unit tests from Put() behavior.
35 func _store(t *testing.T, vol *UnixVolume, filename string, block []byte) {
36 blockdir := fmt.Sprintf("%s/%s", vol.root, filename[:3])
37 if err := os.MkdirAll(blockdir, 0755); err != nil {
41 blockpath := fmt.Sprintf("%s/%s", blockdir, filename)
42 if f, err := os.Create(blockpath); err == nil {
50 func TestGet(t *testing.T) {
51 v := TempUnixVolume(t, false, false)
53 _store(t, v, TEST_HASH, TEST_BLOCK)
55 buf, err := v.Get(TEST_HASH)
59 if bytes.Compare(buf, TEST_BLOCK) != 0 {
60 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
64 func TestGetNotFound(t *testing.T) {
65 v := TempUnixVolume(t, false, false)
67 _store(t, v, TEST_HASH, TEST_BLOCK)
69 buf, err := v.Get(TEST_HASH_2)
71 case os.IsNotExist(err):
74 t.Errorf("Read should have failed, returned %s", string(buf))
76 t.Errorf("Read expected ErrNotExist, got: %s", err)
80 func TestIndexTo(t *testing.T) {
81 v := TempUnixVolume(t, false, false)
84 _store(t, v, TEST_HASH, TEST_BLOCK)
85 _store(t, v, TEST_HASH_2, TEST_BLOCK_2)
86 _store(t, v, TEST_HASH_3, TEST_BLOCK_3)
88 buf := new(bytes.Buffer)
90 index_rows := strings.Split(string(buf.Bytes()), "\n")
91 sort.Strings(index_rows)
92 sorted_index := strings.Join(index_rows, "\n")
93 m, err := regexp.MatchString(
94 `^\n`+TEST_HASH+`\+\d+ \d+\n`+
95 TEST_HASH_3+`\+\d+ \d+\n`+
96 TEST_HASH_2+`\+\d+ \d+$`,
101 t.Errorf("Got index %q for empty prefix", sorted_index)
104 for _, prefix := range []string{"f", "f15", "f15ac"} {
105 buf = new(bytes.Buffer)
106 v.IndexTo(prefix, buf)
107 m, err := regexp.MatchString(`^`+TEST_HASH_2+`\+\d+ \d+\n$`, string(buf.Bytes()))
111 t.Errorf("Got index %q for prefix %q", string(buf.Bytes()), prefix)
116 func TestPut(t *testing.T) {
117 v := TempUnixVolume(t, false, false)
120 err := v.Put(TEST_HASH, TEST_BLOCK)
124 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
125 if buf, err := ioutil.ReadFile(p); err != nil {
127 } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
128 t.Errorf("Write should have stored %s, did store %s",
129 string(TEST_BLOCK), string(buf))
133 func TestPutBadVolume(t *testing.T) {
134 v := TempUnixVolume(t, false, false)
137 os.Chmod(v.root, 000)
138 err := v.Put(TEST_HASH, TEST_BLOCK)
140 t.Error("Write should have failed")
144 func TestUnixVolumeReadonly(t *testing.T) {
145 v := TempUnixVolume(t, false, false)
148 // First write something before marking readonly
149 err := v.Put(TEST_HASH, TEST_BLOCK)
151 t.Error("got err %v, expected nil", err)
156 _, err = v.Get(TEST_HASH)
158 t.Error("got err %v, expected nil", err)
161 err = v.Put(TEST_HASH, TEST_BLOCK)
162 if err != MethodDisabledError {
163 t.Error("got err %v, expected MethodDisabledError", err)
166 err = v.Touch(TEST_HASH)
167 if err != MethodDisabledError {
168 t.Error("got err %v, expected MethodDisabledError", err)
171 err = v.Delete(TEST_HASH)
172 if err != MethodDisabledError {
173 t.Error("got err %v, expected MethodDisabledError", err)
178 // Test that when applying PUT to a block that already exists,
179 // the block's modification time is updated.
180 func TestPutTouch(t *testing.T) {
181 v := TempUnixVolume(t, false, false)
184 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
188 // We'll verify { t0 < threshold < t1 }, where t0 is the
189 // existing block's timestamp on disk before Put() and t1 is
190 // its timestamp after Put().
191 threshold := time.Now().Add(-time.Second)
193 // Set the stored block's mtime far enough in the past that we
194 // can see the difference between "timestamp didn't change"
195 // and "timestamp granularity is too low".
197 oldtime := time.Now().Add(-20 * time.Second).Unix()
198 if err := syscall.Utime(v.blockPath(TEST_HASH),
199 &syscall.Utimbuf{oldtime, oldtime}); err != nil {
203 // Make sure v.Mtime() agrees the above Utime really worked.
204 if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
205 t.Errorf("Setting mtime failed: %v, %v", t0, err)
209 // Write the same block again.
210 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
214 // Verify threshold < t1
215 t1, err := v.Mtime(TEST_HASH)
219 if t1.Before(threshold) {
220 t.Errorf("t1 %v must be >= threshold %v after v.Put ",
225 // Serialization tests: launch a bunch of concurrent
227 // TODO(twp): show that the underlying Read/Write operations executed
228 // serially and not concurrently. The easiest way to do this is
229 // probably to activate verbose or debug logging, capture log output
230 // and examine it to confirm that Reads and Writes did not overlap.
232 // TODO(twp): a proper test of I/O serialization requires that a
233 // second request start while the first one is still underway.
234 // Guaranteeing that the test behaves this way requires some tricky
235 // synchronization and mocking. For now we'll just launch a bunch of
236 // requests simultaenously in goroutines and demonstrate that they
237 // return accurate results.
239 func TestGetSerialized(t *testing.T) {
240 // Create a volume with I/O serialization enabled.
241 v := TempUnixVolume(t, true, false)
244 _store(t, v, TEST_HASH, TEST_BLOCK)
245 _store(t, v, TEST_HASH_2, TEST_BLOCK_2)
246 _store(t, v, TEST_HASH_3, TEST_BLOCK_3)
248 sem := make(chan int)
249 go func(sem chan int) {
250 buf, err := v.Get(TEST_HASH)
252 t.Errorf("err1: %v", err)
254 if bytes.Compare(buf, TEST_BLOCK) != 0 {
255 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
260 go func(sem chan int) {
261 buf, err := v.Get(TEST_HASH_2)
263 t.Errorf("err2: %v", err)
265 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
266 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
271 go func(sem chan int) {
272 buf, err := v.Get(TEST_HASH_3)
274 t.Errorf("err3: %v", err)
276 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
277 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
282 // Wait for all goroutines to finish
283 for done := 0; done < 3; {
288 func TestPutSerialized(t *testing.T) {
289 // Create a volume with I/O serialization enabled.
290 v := TempUnixVolume(t, true, false)
293 sem := make(chan int)
294 go func(sem chan int) {
295 err := v.Put(TEST_HASH, TEST_BLOCK)
297 t.Errorf("err1: %v", err)
302 go func(sem chan int) {
303 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
305 t.Errorf("err2: %v", err)
310 go func(sem chan int) {
311 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
313 t.Errorf("err3: %v", err)
318 // Wait for all goroutines to finish
319 for done := 0; done < 3; {
323 // Double check that we actually wrote the blocks we expected to write.
324 buf, err := v.Get(TEST_HASH)
326 t.Errorf("Get #1: %v", err)
328 if bytes.Compare(buf, TEST_BLOCK) != 0 {
329 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
332 buf, err = v.Get(TEST_HASH_2)
334 t.Errorf("Get #2: %v", err)
336 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
337 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
340 buf, err = v.Get(TEST_HASH_3)
342 t.Errorf("Get #3: %v", err)
344 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
345 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
349 func TestIsFull(t *testing.T) {
350 v := TempUnixVolume(t, false, false)
353 full_path := v.root + "/full"
354 now := fmt.Sprintf("%d", time.Now().Unix())
355 os.Symlink(now, full_path)
357 t.Errorf("%s: claims not to be full", v)
361 // Test with an expired /full link.
362 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
363 os.Symlink(expired, full_path)
365 t.Errorf("%s: should no longer be full", v)
369 func TestNodeStatus(t *testing.T) {
370 v := TempUnixVolume(t, false, false)
373 // Get node status and make a basic sanity check.
374 volinfo := v.Status()
375 if volinfo.MountPoint != v.root {
376 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
378 if volinfo.DeviceNum == 0 {
379 t.Errorf("uninitialized device_num in %v", volinfo)
381 if volinfo.BytesFree == 0 {
382 t.Errorf("uninitialized bytes_free in %v", volinfo)
384 if volinfo.BytesUsed == 0 {
385 t.Errorf("uninitialized bytes_used in %v", volinfo)