13 func TempUnixVolume(t *testing.T, serialize bool, readonly bool) *UnixVolume {
14 d, err := ioutil.TempDir("", "volume_test")
25 func _teardown(v *UnixVolume) {
29 // _store writes a Keep block directly into a UnixVolume, bypassing
30 // the overhead and safeguards of Put(). Useful for storing bogus data
31 // and isolating unit tests from Put() behavior.
32 func _store(t *testing.T, vol *UnixVolume, filename string, block []byte) {
33 blockdir := fmt.Sprintf("%s/%s", vol.root, filename[:3])
34 if err := os.MkdirAll(blockdir, 0755); err != nil {
38 blockpath := fmt.Sprintf("%s/%s", blockdir, filename)
39 if f, err := os.Create(blockpath); err == nil {
47 func TestGet(t *testing.T) {
48 v := TempUnixVolume(t, false, false)
50 _store(t, v, TEST_HASH, TEST_BLOCK)
52 buf, err := v.Get(TEST_HASH)
56 if bytes.Compare(buf, TEST_BLOCK) != 0 {
57 t.Errorf("expected %s, got %s", string(TEST_BLOCK), string(buf))
61 func TestGetNotFound(t *testing.T) {
62 v := TempUnixVolume(t, false, false)
64 _store(t, v, TEST_HASH, TEST_BLOCK)
66 buf, err := v.Get(TEST_HASH_2)
68 case os.IsNotExist(err):
71 t.Errorf("Read should have failed, returned %s", string(buf))
73 t.Errorf("Read expected ErrNotExist, got: %s", err)
77 func TestPut(t *testing.T) {
78 v := TempUnixVolume(t, false, false)
81 err := v.Put(TEST_HASH, TEST_BLOCK)
85 p := fmt.Sprintf("%s/%s/%s", v.root, TEST_HASH[:3], TEST_HASH)
86 if buf, err := ioutil.ReadFile(p); err != nil {
88 } else if bytes.Compare(buf, TEST_BLOCK) != 0 {
89 t.Errorf("Write should have stored %s, did store %s",
90 string(TEST_BLOCK), string(buf))
94 func TestPutBadVolume(t *testing.T) {
95 v := TempUnixVolume(t, false, false)
99 err := v.Put(TEST_HASH, TEST_BLOCK)
101 t.Error("Write should have failed")
105 func TestUnixVolumeReadonly(t *testing.T) {
106 v := TempUnixVolume(t, false, false)
109 // First write something before marking readonly
110 err := v.Put(TEST_HASH, TEST_BLOCK)
112 t.Error("got err %v, expected nil", err)
117 _, err = v.Get(TEST_HASH)
119 t.Error("got err %v, expected nil", err)
122 err = v.Put(TEST_HASH, TEST_BLOCK)
123 if err != MethodDisabledError {
124 t.Error("got err %v, expected MethodDisabledError", err)
127 err = v.Touch(TEST_HASH)
128 if err != MethodDisabledError {
129 t.Error("got err %v, expected MethodDisabledError", err)
132 err = v.Delete(TEST_HASH)
133 if err != MethodDisabledError {
134 t.Error("got err %v, expected MethodDisabledError", err)
139 // Test that when applying PUT to a block that already exists,
140 // the block's modification time is updated.
141 func TestPutTouch(t *testing.T) {
142 v := TempUnixVolume(t, false, false)
145 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
149 // We'll verify { t0 < threshold < t1 }, where t0 is the
150 // existing block's timestamp on disk before Put() and t1 is
151 // its timestamp after Put().
152 threshold := time.Now().Add(-time.Second)
154 // Set the stored block's mtime far enough in the past that we
155 // can see the difference between "timestamp didn't change"
156 // and "timestamp granularity is too low".
158 oldtime := time.Now().Add(-20 * time.Second).Unix()
159 if err := syscall.Utime(v.blockPath(TEST_HASH),
160 &syscall.Utimbuf{oldtime, oldtime}); err != nil {
164 // Make sure v.Mtime() agrees the above Utime really worked.
165 if t0, err := v.Mtime(TEST_HASH); err != nil || t0.IsZero() || !t0.Before(threshold) {
166 t.Errorf("Setting mtime failed: %v, %v", t0, err)
170 // Write the same block again.
171 if err := v.Put(TEST_HASH, TEST_BLOCK); err != nil {
175 // Verify threshold < t1
176 t1, err := v.Mtime(TEST_HASH)
180 if t1.Before(threshold) {
181 t.Errorf("t1 %v must be >= threshold %v after v.Put ",
186 // Serialization tests: launch a bunch of concurrent
188 // TODO(twp): show that the underlying Read/Write operations executed
189 // serially and not concurrently. The easiest way to do this is
190 // probably to activate verbose or debug logging, capture log output
191 // and examine it to confirm that Reads and Writes did not overlap.
193 // TODO(twp): a proper test of I/O serialization requires that a
194 // second request start while the first one is still underway.
195 // Guaranteeing that the test behaves this way requires some tricky
196 // synchronization and mocking. For now we'll just launch a bunch of
197 // requests simultaenously in goroutines and demonstrate that they
198 // return accurate results.
200 func TestGetSerialized(t *testing.T) {
201 // Create a volume with I/O serialization enabled.
202 v := TempUnixVolume(t, true, false)
205 _store(t, v, TEST_HASH, TEST_BLOCK)
206 _store(t, v, TEST_HASH_2, TEST_BLOCK_2)
207 _store(t, v, TEST_HASH_3, TEST_BLOCK_3)
209 sem := make(chan int)
210 go func(sem chan int) {
211 buf, err := v.Get(TEST_HASH)
213 t.Errorf("err1: %v", err)
215 if bytes.Compare(buf, TEST_BLOCK) != 0 {
216 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK), string(buf))
221 go func(sem chan int) {
222 buf, err := v.Get(TEST_HASH_2)
224 t.Errorf("err2: %v", err)
226 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
227 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_2), string(buf))
232 go func(sem chan int) {
233 buf, err := v.Get(TEST_HASH_3)
235 t.Errorf("err3: %v", err)
237 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
238 t.Errorf("buf should be %s, is %s", string(TEST_BLOCK_3), string(buf))
243 // Wait for all goroutines to finish
244 for done := 0; done < 3; {
249 func TestPutSerialized(t *testing.T) {
250 // Create a volume with I/O serialization enabled.
251 v := TempUnixVolume(t, true, false)
254 sem := make(chan int)
255 go func(sem chan int) {
256 err := v.Put(TEST_HASH, TEST_BLOCK)
258 t.Errorf("err1: %v", err)
263 go func(sem chan int) {
264 err := v.Put(TEST_HASH_2, TEST_BLOCK_2)
266 t.Errorf("err2: %v", err)
271 go func(sem chan int) {
272 err := v.Put(TEST_HASH_3, TEST_BLOCK_3)
274 t.Errorf("err3: %v", err)
279 // Wait for all goroutines to finish
280 for done := 0; done < 2; {
284 // Double check that we actually wrote the blocks we expected to write.
285 buf, err := v.Get(TEST_HASH)
287 t.Errorf("Get #1: %v", err)
289 if bytes.Compare(buf, TEST_BLOCK) != 0 {
290 t.Errorf("Get #1: expected %s, got %s", string(TEST_BLOCK), string(buf))
293 buf, err = v.Get(TEST_HASH_2)
295 t.Errorf("Get #2: %v", err)
297 if bytes.Compare(buf, TEST_BLOCK_2) != 0 {
298 t.Errorf("Get #2: expected %s, got %s", string(TEST_BLOCK_2), string(buf))
301 buf, err = v.Get(TEST_HASH_3)
303 t.Errorf("Get #3: %v", err)
305 if bytes.Compare(buf, TEST_BLOCK_3) != 0 {
306 t.Errorf("Get #3: expected %s, got %s", string(TEST_BLOCK_3), string(buf))
310 func TestIsFull(t *testing.T) {
311 v := TempUnixVolume(t, false, false)
314 full_path := v.root + "/full"
315 now := fmt.Sprintf("%d", time.Now().Unix())
316 os.Symlink(now, full_path)
318 t.Errorf("%s: claims not to be full", v)
322 // Test with an expired /full link.
323 expired := fmt.Sprintf("%d", time.Now().Unix()-3605)
324 os.Symlink(expired, full_path)
326 t.Errorf("%s: should no longer be full", v)
330 func TestNodeStatus(t *testing.T) {
331 v := TempUnixVolume(t, false, false)
334 // Get node status and make a basic sanity check.
335 volinfo := v.Status()
336 if volinfo.MountPoint != v.root {
337 t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
339 if volinfo.DeviceNum == 0 {
340 t.Errorf("uninitialized device_num in %v", volinfo)
342 if volinfo.BytesFree == 0 {
343 t.Errorf("uninitialized bytes_free in %v", volinfo)
345 if volinfo.BytesUsed == 0 {
346 t.Errorf("uninitialized bytes_used in %v", volinfo)