fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

1package build 2 3import ( 4 "errors" 5 "flag" 6 "io" 7 "log" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/google/go-cmp/cmp" 16 "github.com/google/go-cmp/cmp/cmpopts" 17 18 "github.com/sourcegraph/zoekt" 19) 20 21var update = flag.Bool("update", false, "update golden file") 22 23// ensure we don't regress on how we build v16 24func TestBuildv16(t *testing.T) { 25 dir := t.TempDir() 26 27 opts := Options{ 28 IndexDir: dir, 29 RepositoryDescription: zoekt.Repository{ 30 Name: "repo", 31 Source: "./testdata/repo/", 32 }, 33 DisableCTags: true, 34 } 35 opts.SetDefaults() 36 37 b, err := NewBuilder(opts) 38 if err != nil { 39 t.Fatal(err) 40 } 41 42 for _, p := range []string{"main.go"} { 43 blob, err := os.ReadFile(filepath.Join("../testdata/repo", p)) 44 if err != nil { 45 t.Fatal(err) 46 } 47 if err := b.AddFile(p, blob); err != nil { 48 t.Fatal(err) 49 } 50 } 51 52 wantP := filepath.Join("../testdata/shards", "repo_v16.00000.zoekt") 53 54 // fields indexTime and id depend on time. For this test, we copy the fields from 55 // the old shard. 56 _, wantMetadata, err := zoekt.ReadMetadataPath(wantP) 57 if err != nil { 58 t.Fatal(err) 59 } 60 b.indexTime = wantMetadata.IndexTime 61 b.id = wantMetadata.ID 62 63 if err := b.Finish(); err != nil { 64 t.Fatal(err) 65 } 66 67 gotP := filepath.Join(dir, "repo_v16.00000.zoekt") 68 69 if *update { 70 data, err := os.ReadFile(gotP) 71 if err != nil { 72 t.Fatal(err) 73 } 74 err = os.WriteFile(wantP, data, 0644) 75 if err != nil { 76 t.Fatal(err) 77 } 78 return 79 } 80 81 got, err := os.ReadFile(gotP) 82 if err != nil { 83 t.Fatal(err) 84 } 85 want, err := os.ReadFile(wantP) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 if d := cmp.Diff(want, got); d != "" { 91 t.Errorf("mismatch (-want +got):\n%s", d) 92 } 93} 94 95func TestFlags(t *testing.T) { 96 cases := []struct { 97 args []string 98 want Options 99 }{{ 100 // Defaults 101 args: []string{}, 102 want: Options{}, 103 }, { 104 args: []string{"-index", "/tmp"}, 105 want: Options{ 106 IndexDir: "/tmp", 107 }, 108 }, { 109 // single large file pattern 110 args: []string{"-large_file", "*.md"}, 111 want: Options{ 112 LargeFiles: []string{"*.md"}, 113 }, 114 }, { 115 // multiple large file pattern 116 args: []string{"-large_file", "*.md", "-large_file", "*.yaml"}, 117 want: Options{ 118 LargeFiles: []string{"*.md", "*.yaml"}, 119 }, 120 }, { 121 // multiple large file pattern with negated pattern 122 args: []string{"-large_file", "*.md", "-large_file", "!*.yaml"}, 123 want: Options{ 124 LargeFiles: []string{"*.md", "!*.yaml"}, 125 }, 126 }, { 127 // multiple large file pattern with escaped character 128 args: []string{"-large_file", "*.md", "-large_file", "\\!*.yaml"}, 129 want: Options{ 130 LargeFiles: []string{"*.md", "\\!*.yaml"}, 131 }, 132 }} 133 134 ignored := []cmp.Option{ 135 // depends on $PATH setting. 136 cmpopts.IgnoreFields(Options{}, "CTagsPath"), 137 cmpopts.IgnoreFields(Options{}, "changedOrRemovedFiles"), 138 cmpopts.IgnoreFields(zoekt.Repository{}, "priority"), 139 } 140 141 for _, c := range cases { 142 c.want.SetDefaults() 143 // depends on $PATH setting. 144 c.want.CTagsPath = "" 145 146 got := Options{} 147 fs := flag.NewFlagSet("", flag.ContinueOnError) 148 got.Flags(fs) 149 if err := fs.Parse(c.args); err != nil { 150 t.Errorf("failed to parse args %v: %v", c.args, err) 151 } else if d := cmp.Diff(c.want, got, ignored...); d != "" { 152 t.Errorf("mismatch for %v (-want +got):\n%s", c.args, d) 153 } 154 } 155} 156 157func TestIncrementalSkipIndexing(t *testing.T) { 158 cases := []struct { 159 name string 160 want bool 161 opts Options 162 }{{ 163 name: "v17-noop", 164 want: true, 165 opts: Options{ 166 RepositoryDescription: zoekt.Repository{ 167 Name: "repo17", 168 }, 169 SizeMax: 2097152, 170 DisableCTags: true, 171 }, 172 }, { 173 name: "v16-noop", 174 want: true, 175 opts: Options{ 176 RepositoryDescription: zoekt.Repository{ 177 Name: "repo", 178 }, 179 SizeMax: 2097152, 180 DisableCTags: true, 181 }, 182 }, { 183 name: "v17-id", 184 want: false, 185 opts: Options{ 186 RepositoryDescription: zoekt.Repository{ 187 Name: "repo17", 188 RawConfig: map[string]string{ 189 "repoid": "123", 190 }, 191 }, 192 SizeMax: 2097152, 193 DisableCTags: true, 194 }, 195 }, { 196 name: "doesnotexist", 197 want: false, 198 opts: Options{ 199 RepositoryDescription: zoekt.Repository{ 200 Name: "doesnotexist", 201 }, 202 SizeMax: 2097152, 203 DisableCTags: true, 204 }, 205 }} 206 207 for _, tc := range cases { 208 t.Run(tc.name, func(t *testing.T) { 209 tc.opts.IndexDir = "../testdata/shards" 210 t.Log(tc.opts.IndexState()) 211 got := tc.opts.IncrementalSkipIndexing() 212 if got != tc.want { 213 t.Fatalf("want %v got %v", tc.want, got) 214 } 215 }) 216 } 217} 218 219func TestMain(m *testing.M) { 220 flag.Parse() 221 if !testing.Verbose() { 222 log.SetOutput(io.Discard) 223 } 224 os.Exit(m.Run()) 225} 226 227func TestDontCountContentOfSkippedFiles(t *testing.T) { 228 b, err := NewBuilder(Options{RepositoryDescription: zoekt.Repository{ 229 Name: "foo", 230 }}) 231 if err != nil { 232 t.Fatal(err) 233 } 234 235 // content with at least 100 bytes 236 binary := append([]byte("abc def \x00"), make([]byte, 100)...) 237 err = b.Add(zoekt.Document{ 238 Name: "f1", 239 Content: binary, 240 }) 241 if err != nil { 242 t.Fatal(err) 243 } 244 if len(b.todo) != 1 || b.todo[0].SkipReason == "" { 245 t.Fatalf("document should have been skipped") 246 } 247 if b.size >= 100 { 248 t.Fatalf("content of skipped documents should not count towards shard size thresold") 249 } 250} 251 252func TestOptions_FindAllShards(t *testing.T) { 253 type simpleShard struct { 254 Repository zoekt.Repository 255 // NumShards is the number of shards that should be created that 256 // contain data for "Repository". 257 NumShards int 258 } 259 260 tests := []struct { 261 name string 262 simpleShards []simpleShard 263 compoundShards [][]zoekt.Repository 264 expectedShardCount int 265 expectedRepository zoekt.Repository 266 }{ 267 { 268 name: "repository in normal shard", 269 simpleShards: []simpleShard{ 270 {Repository: zoekt.Repository{Name: "repoA", ID: 1}}, 271 {Repository: zoekt.Repository{Name: "repoB", ID: 2}}, 272 {Repository: zoekt.Repository{Name: "repoC", ID: 3}}, 273 }, 274 expectedShardCount: 1, 275 expectedRepository: zoekt.Repository{Name: "repoB", ID: 2}, 276 }, 277 { 278 name: "repository in compound shard", 279 compoundShards: [][]zoekt.Repository{ 280 { 281 {Name: "repoA", ID: 1}, 282 {Name: "repoB", ID: 2}, 283 {Name: "repoC", ID: 3}, 284 }, 285 { 286 {Name: "repoD", ID: 4}, 287 {Name: "repoE", ID: 5}, 288 {Name: "repoF", ID: 6}, 289 }, 290 }, 291 expectedShardCount: 1, 292 expectedRepository: zoekt.Repository{Name: "repoB", ID: 2}, 293 }, 294 { 295 name: "repository split across multiple shards", 296 simpleShards: []simpleShard{ 297 {Repository: zoekt.Repository{Name: "repoA", ID: 1}}, 298 {Repository: zoekt.Repository{Name: "repoB", ID: 2}, NumShards: 2}, 299 {Repository: zoekt.Repository{Name: "repoC", ID: 3}}, 300 }, 301 expectedShardCount: 2, 302 expectedRepository: zoekt.Repository{Name: "repoB", ID: 2}, 303 }, 304 { 305 name: "unknown repository", 306 simpleShards: []simpleShard{ 307 {Repository: zoekt.Repository{Name: "repoA", ID: 1}}, 308 {Repository: zoekt.Repository{Name: "repoB", ID: 2}}, 309 {Repository: zoekt.Repository{Name: "repoC", ID: 3}}, 310 }, 311 compoundShards: [][]zoekt.Repository{ 312 { 313 {Name: "repoD", ID: 4}, 314 {Name: "repoE", ID: 5}, 315 {Name: "repoF", ID: 6}, 316 }, 317 }, 318 expectedShardCount: 0, 319 }, 320 { 321 name: "match on ID, not name (compound only)", 322 compoundShards: [][]zoekt.Repository{ 323 { 324 {Name: "repoA", ID: 1}, 325 {Name: "sameName", ID: 2}, 326 {Name: "sameName", ID: 3}, 327 }, 328 { 329 {Name: "repoB", ID: 4}, 330 {Name: "sameName", ID: 5}, 331 {Name: "sameName", ID: 6}, 332 }, 333 }, 334 expectedShardCount: 1, 335 expectedRepository: zoekt.Repository{Name: "sameName", ID: 5}, 336 }, 337 } 338 for _, tt := range tests { 339 t.Run(tt.name, func(t *testing.T) { 340 t.Parallel() 341 342 // prepare 343 indexDir := t.TempDir() 344 345 for _, s := range tt.simpleShards { 346 createTestShard(t, indexDir, s.Repository, s.NumShards) 347 } 348 349 for _, repositoryGroup := range tt.compoundShards { 350 createTestCompoundShard(t, indexDir, repositoryGroup) 351 } 352 353 o := &Options{ 354 IndexDir: indexDir, 355 RepositoryDescription: tt.expectedRepository, 356 } 357 o.SetDefaults() 358 359 // run test 360 shards := o.FindAllShards() 361 362 // verify results 363 if len(shards) != tt.expectedShardCount { 364 t.Fatalf("expected %d shard(s), received %d shard(s)", tt.expectedShardCount, len(shards)) 365 } 366 367 if tt.expectedShardCount > 0 { 368 for _, s := range shards { 369 // all shards should contain the metadata for the desired repository 370 repos, _, err := zoekt.ReadMetadataPathAlive(s) 371 if err != nil { 372 t.Fatalf("reading metadata from shard %q: %s", s, err) 373 } 374 375 foundRepository := false 376 for _, r := range repos { 377 if r.ID == tt.expectedRepository.ID { 378 foundRepository = true 379 break 380 } 381 } 382 383 if !foundRepository { 384 t.Errorf("shard %q doesn't contain metadata for repository %d", s, tt.expectedRepository.ID) 385 } 386 } 387 } 388 }) 389 } 390} 391 392func TestBuilder_BranchNamesEqual(t *testing.T) { 393 for i, test := range []struct { 394 oldBranches []zoekt.RepositoryBranch 395 newBranches []zoekt.RepositoryBranch 396 expected bool 397 }{ 398 { 399 oldBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}, {Name: "release", Version: "v1"}}, 400 newBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}, {Name: "release", Version: "v1"}}, 401 expected: true, 402 }, 403 { 404 oldBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}, {Name: "release", Version: "v3"}}, 405 newBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v2"}, {Name: "release", Version: "v4"}}, 406 expected: true, 407 }, 408 { 409 oldBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}}, 410 newBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v2"}, {Name: "release", Version: "v1"}}, 411 expected: false, 412 }, 413 { 414 oldBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}}, 415 newBranches: []zoekt.RepositoryBranch{{Name: "release", Version: "v1"}}, 416 expected: false, 417 }, 418 { 419 oldBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}}, 420 newBranches: []zoekt.RepositoryBranch{}, 421 expected: false, 422 }, 423 { 424 oldBranches: []zoekt.RepositoryBranch{}, 425 newBranches: []zoekt.RepositoryBranch{{Name: "main", Version: "v1"}}, 426 expected: false, 427 }, 428 } { 429 t.Run(strconv.Itoa(i), func(t *testing.T) { 430 actual := BranchNamesEqual(test.oldBranches, test.newBranches) 431 if test.expected != actual { 432 t.Errorf("expected: %t, got: %t", test.expected, actual) 433 } 434 }) 435 } 436} 437 438func TestBuilder_DeltaShardsBuildsShouldErrorOnBranchSet(t *testing.T) { 439 indexDir := t.TempDir() 440 441 repository := zoekt.Repository{ 442 Name: "repo", 443 ID: 1, 444 Branches: []zoekt.RepositoryBranch{{Name: "foo"}, {Name: "bar"}}, 445 } 446 createTestShard(t, indexDir, repository, 2) 447 448 repositoryNewBranches := zoekt.Repository{ 449 Name: "repo", 450 ID: 1, 451 Branches: []zoekt.RepositoryBranch{{Name: "foo"}, {Name: "baz"}}, 452 } 453 454 o := Options{ 455 IndexDir: indexDir, 456 RepositoryDescription: repositoryNewBranches, 457 IsDelta: true, 458 } 459 o.SetDefaults() 460 461 b, err := NewBuilder(o) 462 if err != nil { 463 t.Fatalf("NewBuilder: %v", err) 464 } 465 466 err = b.Finish() 467 if !errors.As(err, &deltaBranchSetError{}) { 468 t.Fatalf("expected error complaning about different branch names, got: %s", err) 469 } 470} 471 472func TestBuilder_DeltaShardsBuildsShouldErrorOnIndexOptionsMismatch(t *testing.T) { 473 repository := zoekt.Repository{ 474 Name: "repo", 475 ID: 1, 476 Branches: []zoekt.RepositoryBranch{{Name: "foo"}}, 477 } 478 479 for _, test := range []struct { 480 name string 481 options func(options *Options) 482 }{ 483 { 484 name: "update option CTagsPath to non default", 485 options: func(options *Options) { options.CTagsPath = "ctags_updated_test" }, 486 }, 487 { 488 name: "update option DisableCTags to non default", 489 options: func(options *Options) { options.DisableCTags = true }, 490 }, 491 { 492 name: "update option SizeMax to non default", 493 options: func(options *Options) { options.SizeMax -= 10 }, 494 }, 495 { 496 name: "update option LargeFiles to non default", 497 options: func(options *Options) { options.LargeFiles = []string{"-large_file", "*.md", "-large_file", "*.yaml"} }, 498 }, 499 } { 500 test := test 501 502 t.Run(test.name, func(t *testing.T) { 503 indexDir := t.TempDir() 504 505 // initially use default options 506 createTestShard(t, indexDir, repository, 2) 507 508 o := Options{ 509 IndexDir: indexDir, 510 RepositoryDescription: repository, 511 IsDelta: true, 512 } 513 test.options(&o) 514 515 b, err := NewBuilder(o) 516 if err != nil { 517 t.Fatalf("NewBuilder: %v", err) 518 } 519 520 err = b.Finish() 521 if err == nil { 522 t.Fatalf("no error regarding index options mismatch") 523 } 524 525 var optionsMismatchError *deltaIndexOptionsMismatchError 526 if !errors.As(err, &optionsMismatchError) { 527 t.Fatalf("expected error complaining about index options mismatch, got: %s", err) 528 } 529 }) 530 } 531} 532 533func TestBuilder_DeltaShardsMetadataInOlderShards(t *testing.T) { 534 olderTime := time.Unix(0, 0) 535 newerTime := time.Unix(10000, 0) 536 537 for _, test := range []struct { 538 name string 539 originalRepository zoekt.Repository 540 updatedRepository zoekt.Repository 541 }{ 542 { 543 name: "update commit information", 544 originalRepository: zoekt.Repository{ 545 Name: "repo", 546 ID: 1, 547 Branches: []zoekt.RepositoryBranch{ 548 {Name: "main", Version: "v1"}, 549 {Name: "release", Version: "v1"}, 550 }, 551 }, 552 updatedRepository: zoekt.Repository{ 553 Name: "repo", 554 ID: 1, 555 Branches: []zoekt.RepositoryBranch{ 556 {Name: "main", Version: "v2"}, 557 {Name: "release", Version: "v2"}, 558 }, 559 }, 560 }, 561 { 562 name: "update latest commit date (older -> newer)", 563 originalRepository: zoekt.Repository{ 564 Name: "repo", 565 ID: 1, 566 Branches: []zoekt.RepositoryBranch{ 567 {Name: "main", Version: "v1"}, 568 }, 569 LatestCommitDate: olderTime, 570 }, 571 updatedRepository: zoekt.Repository{ 572 Name: "repo", 573 ID: 1, 574 Branches: []zoekt.RepositoryBranch{ 575 {Name: "main", Version: "v2"}, 576 }, 577 LatestCommitDate: newerTime, 578 }, 579 }, 580 { 581 name: "update latest commit date (even if latest commit date is older - the most recent commits are the source of truth)", 582 originalRepository: zoekt.Repository{ 583 Name: "repo", 584 ID: 1, 585 Branches: []zoekt.RepositoryBranch{ 586 {Name: "main", Version: "v1"}, 587 }, 588 LatestCommitDate: newerTime, 589 }, 590 updatedRepository: zoekt.Repository{ 591 Name: "repo", 592 ID: 1, 593 Branches: []zoekt.RepositoryBranch{ 594 {Name: "main", Version: "v2"}, 595 }, 596 LatestCommitDate: olderTime, 597 }, 598 }, 599 } { 600 test := test 601 602 t.Run(test.name, func(t *testing.T) { 603 indexDir := t.TempDir() 604 605 createTestShard(t, indexDir, test.originalRepository, 2, func(o *Options) { 606 o.DisableCTags = true 607 }) 608 609 shards := createTestShard(t, indexDir, test.updatedRepository, 1, func(o *Options) { 610 o.IsDelta = true 611 o.DisableCTags = true 612 }) 613 614 if len(shards) < 3 { 615 t.Fatalf("expected at least 3 shards, got %d (%s)", len(shards), strings.Join(shards, ", ")) 616 } 617 618 for _, s := range shards { 619 repositories, _, err := zoekt.ReadMetadataPathAlive(s) 620 if err != nil { 621 t.Fatalf("reading repository metadata from shard %q", s) 622 } 623 624 var foundRepository *zoekt.Repository 625 for _, r := range repositories { 626 if r.ID == test.updatedRepository.ID { 627 foundRepository = r 628 break 629 } 630 } 631 632 if foundRepository == nil { 633 t.Fatalf("repository ID %d not in shard %q", test.updatedRepository.ID, s) 634 } 635 636 diffOptions := []cmp.Option{ 637 cmpopts.IgnoreUnexported(zoekt.Repository{}), 638 cmpopts.IgnoreFields(zoekt.Repository{}, "IndexOptions"), 639 cmpopts.EquateEmpty(), 640 } 641 642 if diff := cmp.Diff(&test.updatedRepository, foundRepository, diffOptions...); diff != "" { 643 t.Errorf("shard %q: unexpected diff in repository metadata (-want +got):\n%s", s, diff) 644 } 645 } 646 }) 647 } 648} 649 650func TestFindRepositoryMetadata(t *testing.T) { 651 tests := []struct { 652 name string 653 normalShardRepositories []zoekt.Repository 654 compoundShardRepositories []zoekt.Repository 655 input *zoekt.Repository 656 expectedRepository *zoekt.Repository 657 expectedOk bool 658 }{ 659 { 660 name: "repository in normal shards", 661 normalShardRepositories: []zoekt.Repository{ 662 {Name: "repoA", ID: 1}, 663 {Name: "repoB", ID: 2}, 664 {Name: "repoC", ID: 3}, 665 }, 666 compoundShardRepositories: []zoekt.Repository{ 667 {Name: "repoD", ID: 4}, 668 {Name: "repoE", ID: 5}, 669 {Name: "repoF", ID: 6}, 670 }, 671 input: &zoekt.Repository{Name: "repoB", ID: 2}, 672 expectedRepository: &zoekt.Repository{Name: "repoB", ID: 2}, 673 expectedOk: true, 674 }, 675 { 676 name: "repository in compound shards", 677 normalShardRepositories: []zoekt.Repository{ 678 {Name: "repoA", ID: 1}, 679 {Name: "repoB", ID: 2}, 680 {Name: "repoC", ID: 3}, 681 }, 682 compoundShardRepositories: []zoekt.Repository{ 683 {Name: "repoD", ID: 4}, 684 {Name: "repoE", ID: 5}, 685 {Name: "repoF", ID: 6}, 686 }, 687 input: &zoekt.Repository{Name: "repoE", ID: 5}, 688 expectedRepository: &zoekt.Repository{Name: "repoE", ID: 5}, 689 expectedOk: true, 690 }, 691 { 692 name: "repository not in any shard", 693 normalShardRepositories: []zoekt.Repository{ 694 {Name: "repoA", ID: 1}, 695 {Name: "repoB", ID: 2}, 696 {Name: "repoC", ID: 3}, 697 }, 698 compoundShardRepositories: []zoekt.Repository{ 699 {Name: "repoD", ID: 4}, 700 {Name: "repoE", ID: 5}, 701 {Name: "repoF", ID: 6}, 702 }, 703 input: &zoekt.Repository{Name: "notPresent", ID: 123}, 704 expectedRepository: nil, 705 expectedOk: false, 706 }, 707 } 708 for _, tt := range tests { 709 t.Run(tt.name, func(t *testing.T) { 710 // setup 711 indexDir := t.TempDir() 712 713 optFns := []func(o *Options){ 714 // ctags aren't important for this test, and the equality checks 715 // for diffing repositories can break due to local configuration 716 func(o *Options) { 717 o.DisableCTags = true 718 }, 719 } 720 721 for _, r := range tt.normalShardRepositories { 722 createTestShard(t, indexDir, r, 1, optFns...) 723 } 724 725 if len(tt.compoundShardRepositories) > 0 { 726 createTestCompoundShard(t, indexDir, tt.compoundShardRepositories, optFns...) 727 } 728 729 o := &Options{ 730 IndexDir: indexDir, 731 RepositoryDescription: *tt.input, 732 } 733 o.SetDefaults() 734 735 // run test 736 got, gotOk, err := o.FindRepositoryMetadata() 737 if err != nil { 738 t.Errorf("received unexpected error: %v", err) 739 return 740 } 741 742 // check outcome 743 compareOptions := []cmp.Option{ 744 cmpopts.IgnoreUnexported(zoekt.Repository{}), 745 cmpopts.IgnoreFields(zoekt.Repository{}, "IndexOptions"), 746 cmpopts.EquateEmpty(), 747 } 748 749 if diff := cmp.Diff(tt.expectedRepository, got, compareOptions...); diff != "" { 750 t.Errorf("unexpected difference in repositories (-want +got):\n%s", diff) 751 } 752 753 if tt.expectedOk != gotOk { 754 t.Errorf("unexpected difference in 'ok' value: wanted %t, got %t", tt.expectedOk, gotOk) 755 } 756 }) 757 } 758} 759 760func TestIsLowPriority(t *testing.T) { 761 cases := []string{ 762 "builder_test.go", 763 "TestQuery.java", 764 "test/mocks.go", 765 "search/vendor/thirdparty.cc", 766 "search/node_modules/search/index.js", 767 "search.min.js", 768 "internal/search.js.map", 769 } 770 771 for _, tt := range cases { 772 t.Run(tt, func(t *testing.T) { 773 if !IsLowPriority(tt) { 774 t.Errorf("expected file '%s' to be low priority", tt) 775 } 776 }) 777 } 778 779 negativeCases := []string{ 780 "builder.go", 781 "RoutesTrigger.java", 782 "search.js", 783 } 784 785 for _, tt := range negativeCases { 786 t.Run(tt, func(t *testing.T) { 787 if IsLowPriority(tt) { 788 t.Errorf("did not expect file '%s' to be low priority", tt) 789 } 790 }) 791 } 792} 793 794func createTestShard(t *testing.T, indexDir string, r zoekt.Repository, numShards int, optFns ...func(options *Options)) []string { 795 t.Helper() 796 797 if err := os.MkdirAll(filepath.Dir(indexDir), 0700); err != nil { 798 t.Fatal(err) 799 } 800 801 o := Options{ 802 IndexDir: indexDir, 803 RepositoryDescription: r, 804 ShardMax: 75, // create a new shard every 75 bytes 805 } 806 o.SetDefaults() 807 808 for _, fn := range optFns { 809 fn(&o) 810 } 811 812 b, err := NewBuilder(o) 813 if err != nil { 814 t.Fatalf("NewBuilder: %v", err) 815 } 816 817 if numShards == 0 { 818 // We have to make at least 1 shard. 819 numShards = 1 820 } 821 822 for i := 0; i < numShards; i++ { 823 // Create entries (file + contents) that are ~100 bytes each. 824 // This (along with our shardMax setting of 75 bytes) means that each shard 825 // will contain at most one of these. 826 fileName := strconv.Itoa(i) 827 document := zoekt.Document{Name: fileName, Content: []byte(strings.Repeat("A", 100))} 828 for _, branch := range o.RepositoryDescription.Branches { 829 document.Branches = append(document.Branches, branch.Name) 830 } 831 832 err := b.Add(document) 833 if err != nil { 834 t.Fatalf("failed to add file %q to builder: %s", fileName, err) 835 } 836 } 837 838 if err := b.Finish(); err != nil { 839 t.Fatalf("Finish: %v", err) 840 } 841 842 return o.FindAllShards() 843} 844 845func createTestCompoundShard(t *testing.T, indexDir string, repositories []zoekt.Repository, optFns ...func(options *Options)) { 846 t.Helper() 847 848 var shardNames []string 849 850 for _, r := range repositories { 851 // create an isolated scratch space to store normal shards for this repository 852 scratchDir := t.TempDir() 853 854 // create shards that'll be merged later 855 createTestShard(t, scratchDir, r, 1, optFns...) 856 857 // discover file names for all the normal shards we created 858 // note: this only looks in the immediate 'scratchDir' folder and doesn't recurse 859 shards, err := filepath.Glob(filepath.Join(scratchDir, "*.zoekt")) 860 if err != nil { 861 t.Fatalf("while globbing %q to find normal shards: %s", scratchDir, err) 862 } 863 864 shardNames = append(shardNames, shards...) 865 } 866 867 // load the normal shards that we created 868 var files []zoekt.IndexFile 869 for _, shard := range shardNames { 870 f, err := os.Open(shard) 871 if err != nil { 872 t.Fatalf("opening shard file: %s", err) 873 } 874 defer f.Close() 875 876 indexFile, err := zoekt.NewIndexFile(f) 877 if err != nil { 878 t.Fatalf("creating index file: %s", err) 879 } 880 defer indexFile.Close() 881 882 files = append(files, indexFile) 883 } 884 885 // merge all the simple shards into a compound shard 886 tmpName, dstName, err := zoekt.Merge(indexDir, files...) 887 if err != nil { 888 t.Fatalf("merging index files into compound shard: %s", err) 889 } 890 if err := os.Rename(tmpName, dstName); err != nil { 891 t.Fatal(err) 892 } 893} 894 895func TestIgnoreSizeMax(t *testing.T) { 896 897 for _, test := range []struct { 898 name string 899 largeFiles []string 900 filePaths []string 901 expected bool 902 }{ 903 { 904 name: "empty pattern does nothing", 905 largeFiles: []string{""}, 906 filePaths: []string{"F0"}, 907 expected: false, 908 }, 909 { 910 name: "positive match allows", 911 largeFiles: []string{"F0"}, 912 filePaths: []string{"F0"}, 913 expected: true, 914 }, 915 { 916 name: "positive and negative patterns allows", 917 largeFiles: []string{"F?", "!F0"}, 918 filePaths: []string{"F1"}, 919 expected: true, 920 }, 921 { 922 name: "positive and negative patterns disallows", 923 largeFiles: []string{"F?", "!F0"}, 924 filePaths: []string{"F0"}, 925 expected: false, 926 }, 927 { 928 name: "positive escaped pattern allows", 929 largeFiles: []string{"\\!F?"}, 930 filePaths: []string{"!F0", "!F1"}, 931 expected: true, 932 }, 933 { 934 name: "postive escaped pattern does not disallow", 935 largeFiles: []string{"F0", "\\!F?"}, 936 filePaths: []string{"F0", "!F0"}, 937 expected: true, 938 }, 939 { 940 name: "combined meta and literal interpretation disallows", 941 largeFiles: []string{"*F*", "!!F*"}, 942 filePaths: []string{"!F0"}, 943 expected: false, 944 }, 945 { 946 name: "combined meta and literal interpretation allows", 947 largeFiles: []string{"*F*", "!!F*"}, 948 filePaths: []string{"F0"}, 949 expected: true, 950 }, 951 { 952 name: "largeFiles order: positive match overrides previous negative match and allows", 953 largeFiles: []string{"F?", "!F0", "!F1", "F0"}, 954 filePaths: []string{"F0"}, 955 expected: true, 956 }, 957 { 958 name: "largeFiles order: positive match overrides previous negative match and disallows", 959 largeFiles: []string{"F?", "!F0", "!F1", "F0"}, 960 filePaths: []string{"F1"}, 961 expected: false, 962 }, 963 { 964 name: "largeFiles order: negative match overrides previous positive match and allows", 965 largeFiles: []string{"F?", "!?0", "F0", "!F0"}, 966 filePaths: []string{"F1"}, 967 expected: true, 968 }, 969 { 970 name: "largeFiles order: negative match overrides previous positive match and disallows", 971 largeFiles: []string{"F?", "!?0", "F0", "!F0"}, 972 filePaths: []string{"F0"}, 973 expected: false, 974 }, 975 } { 976 t.Run(test.name, func(t *testing.T) { 977 o := Options{ 978 LargeFiles: test.largeFiles, 979 } 980 981 for _, filePath := range test.filePaths { 982 ignore := o.IgnoreSizeMax(filePath) 983 if ignore != test.expected { 984 t.Errorf("IgnoreSizeMax() for filepath %v returned unexpected result %v", filePath, ignore) 985 } 986 } 987 }) 988 } 989}