github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/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 "camlistore.org/pkg/types" 37 ) 38 39 func Test(t *testing.T, fn func(*testing.T) (sto blobserver.Storage, cleanup func())) { 40 sto, cleanup := fn(t) 41 defer func() { 42 if t.Failed() { 43 t.Logf("test %T FAILED, skipping cleanup!", sto) 44 } else { 45 cleanup() 46 } 47 }() 48 t.Logf("Testing blobserver storage %T", sto) 49 50 t.Logf("Testing Enumerate for empty") 51 testEnumerate(t, sto, nil) 52 53 var blobs []*test.Blob 54 var blobRefs []blob.Ref 55 var blobSizedRefs []blob.SizedRef 56 57 contents := []string{"foo", "quux", "asdf", "qwerty", "0123456789"} 58 if !testing.Short() { 59 for i := 0; i < 95; i++ { 60 contents = append(contents, "foo-"+strconv.Itoa(i)) 61 } 62 } 63 t.Logf("Testing receive") 64 for _, x := range contents { 65 b1 := &test.Blob{x} 66 b1s, err := sto.ReceiveBlob(b1.BlobRef(), b1.Reader()) 67 if err != nil { 68 t.Fatalf("ReceiveBlob of %s: %v", b1, err) 69 } 70 if b1s != b1.SizedRef() { 71 t.Fatal("Received %v; want %v", b1s, b1.SizedRef()) 72 } 73 blobs = append(blobs, b1) 74 blobRefs = append(blobRefs, b1.BlobRef()) 75 blobSizedRefs = append(blobSizedRefs, b1.SizedRef()) 76 77 switch len(blobSizedRefs) { 78 case 1, 5, 100: 79 t.Logf("Testing Enumerate for %d blobs", len(blobSizedRefs)) 80 testEnumerate(t, sto, blobSizedRefs) 81 } 82 } 83 b1 := blobs[0] 84 85 // finish here if you want to examine the test directory 86 //t.Fatalf("FINISH") 87 88 t.Logf("Testing FetchStreaming") 89 for i, b2 := range blobs { 90 rc, size, err := sto.FetchStreaming(b2.BlobRef()) 91 if err != nil { 92 t.Fatalf("error fetching %d. %s: %v", i, b2, err) 93 } 94 defer rc.Close() 95 testSizedBlob(t, rc, b2.BlobRef(), size) 96 } 97 98 if fetcher, ok := sto.(fetcher); ok { 99 rsc, size, err := fetcher.Fetch(b1.BlobRef()) 100 if err != nil { 101 t.Fatalf("error fetching %s: %v", b1, err) 102 } 103 defer rsc.Close() 104 n, err := rsc.Seek(0, 0) 105 if err != nil { 106 t.Fatalf("error seeking in %s: %v", rsc, err) 107 } 108 if n != 0 { 109 t.Fatalf("after seeking to 0, we are at %d!", n) 110 } 111 testSizedBlob(t, rsc, b1.BlobRef(), size) 112 } 113 114 t.Logf("Testing Stat") 115 dest := make(chan blob.SizedRef) 116 go func() { 117 if err := sto.StatBlobs(dest, blobRefs); err != nil { 118 t.Fatalf("error stating blobs %s: %v", blobRefs, err) 119 } 120 }() 121 testStat(t, dest, blobSizedRefs) 122 123 // Enumerate tests. 124 sort.Sort(blob.SizedByRef(blobSizedRefs)) 125 126 t.Logf("Testing Enumerate on all") 127 testEnumerate(t, sto, blobSizedRefs) 128 129 t.Logf("Testing Enumerate 'limit' param") 130 testEnumerate(t, sto, blobSizedRefs[:3], 3) 131 132 // Enumerate 'after' 133 { 134 after := blobSizedRefs[2].Ref.String() 135 t.Logf("Testing Enumerate 'after' param; after %q", after) 136 testEnumerate(t, sto, blobSizedRefs[3:], after) 137 } 138 139 // Enumerate 'after' + limit 140 { 141 after := blobSizedRefs[2].Ref.String() 142 t.Logf("Testing Enumerate 'after' + 'limit' param; after %q, limit 1", after) 143 testEnumerate(t, sto, blobSizedRefs[3:4], after, 1) 144 } 145 146 t.Logf("Testing Remove") 147 if err := sto.RemoveBlobs(blobRefs); err != nil { 148 if strings.Contains(err.Error(), "not implemented") { 149 t.Logf("RemoveBlob %s: %v", b1, err) 150 } else { 151 t.Fatalf("RemoveBlob %s: %v", b1, err) 152 } 153 } 154 } 155 156 type fetcher interface { 157 Fetch(blob blob.Ref) (types.ReadSeekCloser, int64, error) 158 } 159 160 func testSizedBlob(t *testing.T, r io.Reader, b1 blob.Ref, size int64) { 161 h := b1.Hash() 162 n, err := io.Copy(h, r) 163 if err != nil { 164 t.Fatalf("error reading from %s: %v", r, err) 165 } 166 if n != size { 167 t.Fatalf("read %d bytes from %s, metadata said %d!", n, size) 168 } 169 b2 := blob.RefFromHash(h) 170 if b2 != b1 { 171 t.Fatalf("content mismatch (awaited %s, got %s)", b1, b2) 172 } 173 } 174 175 func testEnumerate(t *testing.T, sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) { 176 var after string 177 var n = 1000 178 for _, opt := range opts { 179 switch v := opt.(type) { 180 case string: 181 after = v 182 case int: 183 n = v 184 default: 185 panic("bad option of type " + fmt.Sprint("%T", v)) 186 } 187 } 188 189 want := append([]blob.SizedRef(nil), wantUnsorted...) 190 sort.Sort(blob.SizedByRef(want)) 191 192 sbc := make(chan blob.SizedRef, 10) 193 194 var got []blob.SizedRef 195 var grp syncutil.Group 196 sawEnd := make(chan bool, 1) 197 grp.Go(func() error { 198 if err := sto.EnumerateBlobs(context.New(), sbc, after, n); err != nil { 199 return fmt.Errorf("EnumerateBlobs(%q, %d): %v", after, n) 200 } 201 return nil 202 }) 203 grp.Go(func() error { 204 for sb := range sbc { 205 if !sb.Valid() { 206 return fmt.Errorf("invalid blobref %#v received in enumerate", sb) 207 } 208 got = append(got, sb) 209 } 210 sawEnd <- true 211 return nil 212 213 }) 214 grp.Go(func() error { 215 select { 216 case <-sawEnd: 217 return nil 218 case <-time.After(10 * time.Second): 219 return errors.New("timeout waiting for EnumerateBlobs to close its channel") 220 } 221 222 }) 223 if err := grp.Err(); err != nil { 224 t.Fatalf("Enumerate error: %v", err) 225 return 226 } 227 if len(got) == 0 && len(want) == 0 { 228 return 229 } 230 if !reflect.DeepEqual(got, want) { 231 t.Fatalf("Enumerate mismatch. Got %d; want %d.\n Got: %v\nWant: %v\n", 232 len(got), len(want), got, want) 233 } 234 } 235 236 func testStat(t *testing.T, enum <-chan blob.SizedRef, want []blob.SizedRef) { 237 // blobs may arrive in ANY order 238 m := make(map[string]int, len(want)) 239 for i, sb := range want { 240 m[sb.Ref.String()] = i 241 } 242 243 i := 0 244 for sb := range enum { 245 if !sb.Valid() { 246 break 247 } 248 wanted := want[m[sb.Ref.String()]] 249 if wanted.Size != sb.Size { 250 t.Fatalf("received blob size is %d, wanted %d for &%d", sb.Size, wanted.Size, i) 251 } 252 if wanted.Ref != sb.Ref { 253 t.Fatalf("received blob ref mismatch &%d: wanted %s, got %s", i, sb.Ref, wanted.Ref) 254 } 255 i++ 256 if i >= len(want) { 257 break 258 } 259 } 260 }