github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/storagetest/storagetest.go (about) 1 /* 2 Copyright 2013 The Camlistore Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package storagetest tests blobserver.Storage implementations 18 package storagetest 19 20 import ( 21 "errors" 22 "fmt" 23 "io" 24 "reflect" 25 "sort" 26 "strconv" 27 "strings" 28 "testing" 29 "time" 30 31 "camlistore.org/pkg/blob" 32 "camlistore.org/pkg/blobserver" 33 "camlistore.org/pkg/context" 34 "camlistore.org/pkg/syncutil" 35 "camlistore.org/pkg/test" 36 ) 37 38 func Test(t *testing.T, fn func(*testing.T) (sto blobserver.Storage, cleanup func())) { 39 sto, cleanup := fn(t) 40 defer func() { 41 if t.Failed() { 42 t.Logf("test %T FAILED, skipping cleanup!", sto) 43 } else { 44 cleanup() 45 } 46 }() 47 t.Logf("Testing blobserver storage %T", sto) 48 49 t.Logf("Testing Enumerate for empty") 50 testEnumerate(t, sto, nil) 51 52 var blobs []*test.Blob 53 var blobRefs []blob.Ref 54 var blobSizedRefs []blob.SizedRef 55 56 contents := []string{"foo", "quux", "asdf", "qwerty", "0123456789"} 57 if !testing.Short() { 58 for i := 0; i < 95; i++ { 59 contents = append(contents, "foo-"+strconv.Itoa(i)) 60 } 61 } 62 t.Logf("Testing receive") 63 for _, x := range contents { 64 b1 := &test.Blob{x} 65 b1s, err := sto.ReceiveBlob(b1.BlobRef(), b1.Reader()) 66 if err != nil { 67 t.Fatalf("ReceiveBlob of %s: %v", b1, err) 68 } 69 if b1s != b1.SizedRef() { 70 t.Fatal("Received %v; want %v", b1s, b1.SizedRef()) 71 } 72 blobs = append(blobs, b1) 73 blobRefs = append(blobRefs, b1.BlobRef()) 74 blobSizedRefs = append(blobSizedRefs, b1.SizedRef()) 75 76 switch len(blobSizedRefs) { 77 case 1, 5, 100: 78 t.Logf("Testing Enumerate for %d blobs", len(blobSizedRefs)) 79 testEnumerate(t, sto, blobSizedRefs) 80 } 81 } 82 b1 := blobs[0] 83 84 // finish here if you want to examine the test directory 85 //t.Fatalf("FINISH") 86 87 t.Logf("Testing Fetch") 88 for i, b2 := range blobs { 89 rc, size, err := sto.Fetch(b2.BlobRef()) 90 if err != nil { 91 t.Fatalf("error fetching %d. %s: %v", i, b2, err) 92 } 93 defer rc.Close() 94 testSizedBlob(t, rc, b2.BlobRef(), int64(size)) 95 } 96 97 t.Logf("Testing Stat") 98 dest := make(chan blob.SizedRef) 99 go func() { 100 if err := sto.StatBlobs(dest, blobRefs); err != nil { 101 t.Fatalf("error stating blobs %s: %v", blobRefs, err) 102 } 103 }() 104 testStat(t, dest, blobSizedRefs) 105 106 // Enumerate tests. 107 sort.Sort(blob.SizedByRef(blobSizedRefs)) 108 109 t.Logf("Testing Enumerate on all") 110 testEnumerate(t, sto, blobSizedRefs) 111 112 t.Logf("Testing Enumerate 'limit' param") 113 testEnumerate(t, sto, blobSizedRefs[:3], 3) 114 115 // Enumerate 'after' 116 { 117 after := blobSizedRefs[2].Ref.String() 118 t.Logf("Testing Enumerate 'after' param; after %q", after) 119 testEnumerate(t, sto, blobSizedRefs[3:], after) 120 } 121 122 // Enumerate 'after' + limit 123 { 124 after := blobSizedRefs[2].Ref.String() 125 t.Logf("Testing Enumerate 'after' + 'limit' param; after %q, limit 1", after) 126 testEnumerate(t, sto, blobSizedRefs[3:4], after, 1) 127 } 128 129 // Enumerate 'after' with prefix of a blobref + limit 130 { 131 after := "a" 132 t.Logf("Testing Enumerate 'after' + 'limit' param; after %q, limit 1", after) 133 testEnumerate(t, sto, blobSizedRefs[:1], after, 1) 134 } 135 136 t.Logf("Testing Remove") 137 if err := sto.RemoveBlobs(blobRefs); err != nil { 138 if strings.Contains(err.Error(), "not implemented") { 139 t.Logf("RemoveBlob %s: %v", b1, err) 140 } else { 141 t.Fatalf("RemoveBlob %s: %v", b1, err) 142 } 143 } 144 } 145 146 func testSizedBlob(t *testing.T, r io.Reader, b1 blob.Ref, size int64) { 147 h := b1.Hash() 148 n, err := io.Copy(h, r) 149 if err != nil { 150 t.Fatalf("error reading from %s: %v", r, err) 151 } 152 if n != size { 153 t.Fatalf("read %d bytes from %s, metadata said %d!", n, size) 154 } 155 b2 := blob.RefFromHash(h) 156 if b2 != b1 { 157 t.Fatalf("content mismatch (awaited %s, got %s)", b1, b2) 158 } 159 } 160 161 func CheckEnumerate(sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) error { 162 var after string 163 var n = 1000 164 for _, opt := range opts { 165 switch v := opt.(type) { 166 case string: 167 after = v 168 case int: 169 n = v 170 default: 171 panic("bad option of type " + fmt.Sprint("%T", v)) 172 } 173 } 174 175 want := append([]blob.SizedRef(nil), wantUnsorted...) 176 sort.Sort(blob.SizedByRef(want)) 177 178 sbc := make(chan blob.SizedRef, 10) 179 180 var got []blob.SizedRef 181 var grp syncutil.Group 182 sawEnd := make(chan bool, 1) 183 grp.Go(func() error { 184 if err := sto.EnumerateBlobs(context.New(), sbc, after, n); err != nil { 185 return fmt.Errorf("EnumerateBlobs(%q, %d): %v", after, n, err) 186 } 187 return nil 188 }) 189 grp.Go(func() error { 190 for sb := range sbc { 191 if !sb.Valid() { 192 return fmt.Errorf("invalid blobref %#v received in enumerate", sb) 193 } 194 got = append(got, sb) 195 } 196 sawEnd <- true 197 return nil 198 199 }) 200 grp.Go(func() error { 201 select { 202 case <-sawEnd: 203 return nil 204 case <-time.After(10 * time.Second): 205 return errors.New("timeout waiting for EnumerateBlobs to close its channel") 206 } 207 208 }) 209 if err := grp.Err(); err != nil { 210 return fmt.Errorf("Enumerate error: %v", err) 211 } 212 if len(got) == 0 && len(want) == 0 { 213 return nil 214 } 215 if !reflect.DeepEqual(got, want) { 216 return fmt.Errorf("Enumerate mismatch. Got %d; want %d.\n Got: %v\nWant: %v\n", 217 len(got), len(want), got, want) 218 } 219 return nil 220 } 221 222 func testEnumerate(t *testing.T, sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) { 223 if err := CheckEnumerate(sto, wantUnsorted, opts...); err != nil { 224 t.Fatalf("%v", err) 225 } 226 } 227 228 func testStat(t *testing.T, enum <-chan blob.SizedRef, want []blob.SizedRef) { 229 // blobs may arrive in ANY order 230 m := make(map[string]int, len(want)) 231 for i, sb := range want { 232 m[sb.Ref.String()] = i 233 } 234 235 i := 0 236 for sb := range enum { 237 if !sb.Valid() { 238 break 239 } 240 wanted := want[m[sb.Ref.String()]] 241 if wanted.Size != sb.Size { 242 t.Fatalf("received blob size is %d, wanted %d for &%d", sb.Size, wanted.Size, i) 243 } 244 if wanted.Ref != sb.Ref { 245 t.Fatalf("received blob ref mismatch &%d: wanted %s, got %s", i, sb.Ref, wanted.Ref) 246 } 247 i++ 248 if i >= len(want) { 249 break 250 } 251 } 252 }