github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/index/index_test.go (about) 1 /* 2 Copyright 2011 Google Inc. 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 index_test 18 19 import ( 20 "fmt" 21 "go/ast" 22 "go/parser" 23 "go/token" 24 "os" 25 "path/filepath" 26 "reflect" 27 "strings" 28 "testing" 29 30 "camlistore.org/pkg/blob" 31 "camlistore.org/pkg/blobserver" 32 "camlistore.org/pkg/index" 33 "camlistore.org/pkg/index/indextest" 34 "camlistore.org/pkg/sorted" 35 "camlistore.org/pkg/test" 36 "camlistore.org/pkg/types/camtypes" 37 ) 38 39 func TestReverseTimeString(t *testing.T) { 40 in := "2011-11-27T01:23:45Z" 41 got := index.ExpReverseTimeString(in) 42 want := "rt7988-88-72T98:76:54Z" 43 if got != want { 44 t.Fatalf("reverseTimeString = %q, want %q", got, want) 45 } 46 back := index.ExpUnreverseTimeString(got) 47 if back != in { 48 t.Fatalf("unreverseTimeString = %q, want %q", back, in) 49 } 50 } 51 52 func TestIndex_Memory(t *testing.T) { 53 indextest.Index(t, index.NewMemoryIndex) 54 } 55 56 func TestPathsOfSignerTarget_Memory(t *testing.T) { 57 indextest.PathsOfSignerTarget(t, index.NewMemoryIndex) 58 } 59 60 func TestFiles_Memory(t *testing.T) { 61 indextest.Files(t, index.NewMemoryIndex) 62 } 63 64 func TestEdgesTo_Memory(t *testing.T) { 65 indextest.EdgesTo(t, index.NewMemoryIndex) 66 } 67 68 func TestDelete_Memory(t *testing.T) { 69 indextest.Delete(t, index.NewMemoryIndex) 70 } 71 72 var ( 73 // those test files are not specific to an indexer implementation 74 // hence we do not want to check them. 75 notAnIndexer = []string{ 76 "corpus_bench_test.go", 77 "corpus_test.go", 78 "export_test.go", 79 "index_test.go", 80 "keys_test.go", 81 } 82 // A map is used in hasAllRequiredTests to note which required 83 // tests have been found in a package, by setting the corresponding 84 // booleans to true. Those are the keys for this map. 85 requiredTests = []string{"TestIndex_", "TestPathsOfSignerTarget_", "TestFiles_", "TestEdgesTo_"} 86 ) 87 88 // This function checks that all the functions using the tests 89 // defined in indextest, namely: 90 // TestIndex_, TestPathOfSignerTarget_, TestFiles_ 91 // do exist in the provided test file. 92 func hasAllRequiredTests(name string, t *testing.T) error { 93 tests := make(map[string]bool) 94 for _, v := range requiredTests { 95 tests[v] = false 96 } 97 98 if !strings.HasSuffix(name, "_test.go") || skipFromList(name) { 99 return nil 100 } 101 fset := token.NewFileSet() 102 f, err := parser.ParseFile(fset, name, nil, 0) 103 if err != nil { 104 t.Fatalf("%v: %v", name, err) 105 } 106 ast.Inspect(f, func(n ast.Node) bool { 107 switch x := n.(type) { 108 case *ast.FuncDecl: 109 name := x.Name.Name 110 for k, _ := range tests { 111 if strings.HasPrefix(name, k) { 112 tests[k] = true 113 } 114 } 115 } 116 return true 117 }) 118 119 for k, v := range tests { 120 if !v { 121 return fmt.Errorf("%v not implemented in %v", k, name) 122 } 123 } 124 return nil 125 } 126 127 // For each test file dedicated to an indexer implementation, this checks that 128 // all the required tests are present in its test suite. 129 func TestIndexerTestsCompleteness(t *testing.T) { 130 cwd, err := os.Open(".") 131 if err != nil { 132 t.Fatal(err) 133 } 134 defer cwd.Close() 135 files, err := cwd.Readdir(-1) 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 for _, file := range files { 141 name := file.Name() 142 if file.IsDir() || strings.HasPrefix(name, ".") { 143 continue 144 } 145 if err := hasAllRequiredTests(name, t); err != nil { 146 t.Error(err) 147 } 148 } 149 // special case for sqlite as it is the only one left in its own package 150 if err := hasAllRequiredTests(filepath.FromSlash("sqlite/sqlite_test.go"), t); err != nil { 151 t.Error(err) 152 } 153 } 154 155 func skipFromList(name string) bool { 156 for _, v := range notAnIndexer { 157 if name == v { 158 return true 159 } 160 } 161 return false 162 } 163 164 func TestMergeFileInfoRow(t *testing.T) { 165 c := index.ExpNewCorpus() 166 c.Exp_mergeFileInfoRow("fileinfo|sha1-579f7f246bd420d486ddeb0dadbb256cfaf8bf6b", 167 "100|something%2egif|image%2Fgif") 168 fi := c.Exp_files(blob.MustParse("sha1-579f7f246bd420d486ddeb0dadbb256cfaf8bf6b")) 169 want := camtypes.FileInfo{ 170 Size: 100, 171 MIMEType: "image/gif", 172 FileName: "something.gif", 173 } 174 if !reflect.DeepEqual(want, fi) { 175 t.Errorf("Got %+v; want %+v", fi, want) 176 } 177 } 178 179 var ( 180 chunk1 = &test.Blob{Contents: "foo"} 181 chunk2 = &test.Blob{Contents: "bar"} 182 chunk3 = &test.Blob{Contents: "baz"} 183 184 chunk1ref = chunk1.BlobRef() 185 chunk2ref = chunk2.BlobRef() 186 chunk3ref = chunk3.BlobRef() 187 188 fileBlob = &test.Blob{fmt.Sprintf(`{"camliVersion": 1, 189 "camliType": "file", 190 "fileName": "stuff.txt", 191 "parts": [ 192 {"blobRef": "%s", "size": 3}, 193 {"blobRef": "%s", "size": 3}, 194 {"blobRef": "%s", "size": 3} 195 ]}`, chunk1ref, chunk2ref, chunk3ref)} 196 fileBlobRef = fileBlob.BlobRef() 197 ) 198 199 func TestInitNeededMaps(t *testing.T) { 200 s := sorted.NewMemoryKeyValue() 201 202 // Start unknowning that the data chunks are all gone: 203 s.Set("schemaversion", fmt.Sprint(index.Exp_schemaVersion())) 204 s.Set(index.Exp_missingKey(fileBlobRef, chunk1ref), "1") 205 s.Set(index.Exp_missingKey(fileBlobRef, chunk2ref), "1") 206 s.Set(index.Exp_missingKey(fileBlobRef, chunk3ref), "1") 207 ix, err := index.New(s) 208 if err != nil { 209 t.Fatal(err) 210 } 211 { 212 needs, neededBy, _ := ix.NeededMapsForTest() 213 needsWant := map[blob.Ref][]blob.Ref{ 214 fileBlobRef: []blob.Ref{chunk1ref, chunk2ref, chunk3ref}, 215 } 216 neededByWant := map[blob.Ref][]blob.Ref{ 217 chunk1ref: []blob.Ref{fileBlobRef}, 218 chunk2ref: []blob.Ref{fileBlobRef}, 219 chunk3ref: []blob.Ref{fileBlobRef}, 220 } 221 if !reflect.DeepEqual(needs, needsWant) { 222 t.Errorf("needs = %v; want %v", needs, needsWant) 223 } 224 if !reflect.DeepEqual(neededBy, neededByWant) { 225 t.Errorf("neededBy = %v; want %v", neededBy, neededByWant) 226 } 227 } 228 229 ix.Exp_noteBlobIndexed(chunk2ref) 230 231 { 232 needs, neededBy, ready := ix.NeededMapsForTest() 233 needsWant := map[blob.Ref][]blob.Ref{ 234 fileBlobRef: []blob.Ref{chunk1ref, chunk3ref}, 235 } 236 neededByWant := map[blob.Ref][]blob.Ref{ 237 chunk1ref: []blob.Ref{fileBlobRef}, 238 chunk3ref: []blob.Ref{fileBlobRef}, 239 } 240 if !reflect.DeepEqual(needs, needsWant) { 241 t.Errorf("needs = %v; want %v", needs, needsWant) 242 } 243 if !reflect.DeepEqual(neededBy, neededByWant) { 244 t.Errorf("neededBy = %v; want %v", neededBy, neededByWant) 245 } 246 if len(ready) != 0 { 247 t.Errorf("ready = %v; want nothing", ready) 248 } 249 } 250 251 ix.Exp_noteBlobIndexed(chunk1ref) 252 253 { 254 needs, neededBy, ready := ix.NeededMapsForTest() 255 needsWant := map[blob.Ref][]blob.Ref{ 256 fileBlobRef: []blob.Ref{chunk3ref}, 257 } 258 neededByWant := map[blob.Ref][]blob.Ref{ 259 chunk3ref: []blob.Ref{fileBlobRef}, 260 } 261 if !reflect.DeepEqual(needs, needsWant) { 262 t.Errorf("needs = %v; want %v", needs, needsWant) 263 } 264 if !reflect.DeepEqual(neededBy, neededByWant) { 265 t.Errorf("neededBy = %v; want %v", neededBy, neededByWant) 266 } 267 if len(ready) != 0 { 268 t.Errorf("ready = %v; want nothing", ready) 269 } 270 } 271 272 ix.Exp_noteBlobIndexed(chunk3ref) 273 274 { 275 needs, neededBy, ready := ix.NeededMapsForTest() 276 needsWant := map[blob.Ref][]blob.Ref{} 277 neededByWant := map[blob.Ref][]blob.Ref{} 278 if !reflect.DeepEqual(needs, needsWant) { 279 t.Errorf("needs = %v; want %v", needs, needsWant) 280 } 281 if !reflect.DeepEqual(neededBy, neededByWant) { 282 t.Errorf("neededBy = %v; want %v", neededBy, neededByWant) 283 } 284 if !ready[fileBlobRef] { 285 t.Error("fileBlobRef not ready") 286 } 287 } 288 dumpSorted(t, s) 289 } 290 291 func dumpSorted(t *testing.T, s sorted.KeyValue) { 292 foreachSorted(t, s, func(k, v string) { 293 t.Logf("index %q = %q", k, v) 294 }) 295 } 296 297 func foreachSorted(t *testing.T, s sorted.KeyValue, fn func(string, string)) { 298 it := s.Find("", "") 299 for it.Next() { 300 fn(it.Key(), it.Value()) 301 } 302 if err := it.Close(); err != nil { 303 t.Fatal(err) 304 } 305 } 306 307 func TestOutOfOrderIndexing(t *testing.T) { 308 tf := new(test.Fetcher) 309 s := sorted.NewMemoryKeyValue() 310 311 ix, err := index.New(s) 312 if err != nil { 313 t.Fatal(err) 314 } 315 ix.BlobSource = tf 316 317 t.Logf("file ref = %v", fileBlobRef) 318 t.Logf("missing data chunks = %v, %v, %v", chunk1ref, chunk2ref, chunk3ref) 319 320 add := func(b *test.Blob) { 321 tf.AddBlob(b) 322 if _, err := ix.ReceiveBlob(b.BlobRef(), b.Reader()); err != nil { 323 t.Fatalf("ReceiveBlob(%v): %v", b.BlobRef(), err) 324 } 325 } 326 327 add(fileBlob) 328 329 { 330 key := fmt.Sprintf("missing|%s|%s", fileBlobRef, chunk1ref) 331 if got, err := s.Get(key); got == "" || err != nil { 332 t.Errorf("key %q missing (err: %v); want 1", key, err) 333 } 334 } 335 336 add(chunk1) 337 add(chunk2) 338 339 ix.Exp_AwaitReindexing(t) 340 341 { 342 key := fmt.Sprintf("missing|%s|%s", fileBlobRef, chunk3ref) 343 if got, err := s.Get(key); got == "" || err != nil { 344 t.Errorf("key %q missing (err: %v); want 1", key, err) 345 } 346 } 347 348 add(chunk3) 349 350 ix.Exp_AwaitReindexing(t) 351 352 foreachSorted(t, s, func(k, v string) { 353 if strings.HasPrefix(k, "missing|") { 354 t.Errorf("Shouldn't have missing key: %q", k) 355 } 356 }) 357 } 358 359 func TestIndexingClaimMissingPubkey(t *testing.T) { 360 s := sorted.NewMemoryKeyValue() 361 idx, err := index.New(s) 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 id := indextest.NewIndexDeps(idx) 367 id.Fataler = t 368 369 goodKeyFetcher := id.Index.KeyFetcher 370 emptyFetcher := new(test.Fetcher) 371 372 pn := id.NewPermanode() 373 374 // Prevent the index from being able to find the public key: 375 idx.KeyFetcher = emptyFetcher 376 377 // This previous failed to upload, since the signer's public key was 378 // unavailable. 379 claimRef := id.SetAttribute(pn, "tag", "foo") 380 381 t.Logf(" Claim is %v", claimRef) 382 t.Logf("Signer is %v", id.SignerBlobRef) 383 384 // Verify that populateClaim noted the missing public key blob: 385 { 386 key := fmt.Sprintf("missing|%s|%s", claimRef, id.SignerBlobRef) 387 if got, err := s.Get(key); got == "" || err != nil { 388 t.Errorf("key %q missing (err: %v); want 1", key, err) 389 } 390 } 391 392 // Now make it available again: 393 idx.KeyFetcher = idx.BlobSource 394 395 if err := copyBlob(id.SignerBlobRef, idx.BlobSource.(*test.Fetcher), goodKeyFetcher); err != nil { 396 t.Errorf("Error copying public key to BlobSource: %v", err) 397 } 398 if err := copyBlob(id.SignerBlobRef, idx, goodKeyFetcher); err != nil { 399 t.Errorf("Error uploading public key to indexer: %v", err) 400 } 401 402 idx.Exp_AwaitReindexing(t) 403 404 // Verify that populateClaim noted the missing public key blob: 405 { 406 key := fmt.Sprintf("missing|%s|%s", claimRef, id.SignerBlobRef) 407 if got, err := s.Get(key); got != "" || err == nil { 408 t.Errorf("row %q still exists", key) 409 } 410 } 411 } 412 413 func copyBlob(br blob.Ref, dst blobserver.BlobReceiver, src blob.Fetcher) error { 414 rc, _, err := src.Fetch(br) 415 if err != nil { 416 return err 417 } 418 defer rc.Close() 419 _, err = dst.ReceiveBlob(br, rc) 420 return err 421 }