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

Configure Feed

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

1// Copyright 2021 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package gitindex 16 17import ( 18 "bytes" 19 "context" 20 "fmt" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "sort" 25 "testing" 26 27 "github.com/go-git/go-git/v5" 28 "github.com/go-git/go-git/v5/plumbing" 29 "github.com/google/go-cmp/cmp" 30 "github.com/google/go-cmp/cmp/cmpopts" 31 "github.com/sourcegraph/zoekt" 32 "github.com/sourcegraph/zoekt/build" 33 "github.com/sourcegraph/zoekt/ignore" 34 "github.com/sourcegraph/zoekt/query" 35 "github.com/sourcegraph/zoekt/shards" 36) 37 38func TestIndexEmptyRepo(t *testing.T) { 39 dir := t.TempDir() 40 41 cmd := exec.Command("git", "init", "-b", "master", "repo") 42 cmd.Dir = dir 43 44 if err := cmd.Run(); err != nil { 45 t.Fatalf("cmd.Run: %v", err) 46 } 47 48 desc := zoekt.Repository{ 49 Name: "repo", 50 } 51 opts := Options{ 52 RepoDir: filepath.Join(dir, "repo", ".git"), 53 BuildOptions: build.Options{ 54 RepositoryDescription: desc, 55 IndexDir: dir, 56 }, 57 } 58 59 if err := IndexGitRepo(opts); err != nil { 60 t.Fatalf("IndexGitRepo: %v", err) 61 } 62} 63 64func TestIndexDeltaBasic(t *testing.T) { 65 type branchToDocumentMap map[string][]zoekt.Document 66 67 type step struct { 68 name string 69 addedDocuments branchToDocumentMap 70 deletedDocuments branchToDocumentMap 71 optFn func(t *testing.T, options *Options) 72 73 expectedFallbackToNormalBuild bool 74 expectedDocuments []zoekt.Document 75 } 76 77 helloWorld := zoekt.Document{Name: "hello_world.txt", Content: []byte("hello")} 78 79 fruitV1 := zoekt.Document{Name: "best_fruit.txt", Content: []byte("strawberry")} 80 fruitV1InFolder := zoekt.Document{Name: "the_best/best_fruit.txt", Content: fruitV1.Content} 81 fruitV1WithNewName := zoekt.Document{Name: "new_fruit.txt", Content: fruitV1.Content} 82 83 fruitV2 := zoekt.Document{Name: "best_fruit.txt", Content: []byte("grapes")} 84 fruitV2InFolder := zoekt.Document{Name: "the_best/best_fruit.txt", Content: fruitV2.Content} 85 86 fruitV3 := zoekt.Document{Name: "best_fruit.txt", Content: []byte("oranges")} 87 fruitV4 := zoekt.Document{Name: "best_fruit.txt", Content: []byte("apples")} 88 89 foo := zoekt.Document{Name: "foo.txt", Content: []byte("bar")} 90 91 emptySourcegraphIgnore := zoekt.Document{Name: ignore.IgnoreFile} 92 sourcegraphIgnoreWithContent := zoekt.Document{Name: ignore.IgnoreFile, Content: []byte("good_content.txt")} 93 94 for _, test := range []struct { 95 name string 96 branches []string 97 steps []step 98 }{ 99 { 100 name: "modification", 101 branches: []string{"main"}, 102 steps: []step{ 103 { 104 name: "setup", 105 addedDocuments: branchToDocumentMap{ 106 "main": []zoekt.Document{helloWorld, fruitV1}, 107 }, 108 109 expectedDocuments: []zoekt.Document{helloWorld, fruitV1}, 110 }, 111 { 112 name: "add newer version of fruits", 113 addedDocuments: branchToDocumentMap{ 114 "main": []zoekt.Document{fruitV2}, 115 }, 116 optFn: func(t *testing.T, o *Options) { 117 o.BuildOptions.IsDelta = true 118 }, 119 120 expectedDocuments: []zoekt.Document{helloWorld, fruitV2}, 121 }, 122 }, 123 }, 124 { 125 name: "modification only inside nested folder", 126 branches: []string{"main"}, 127 steps: []step{ 128 { 129 name: "setup", 130 addedDocuments: branchToDocumentMap{ 131 "main": []zoekt.Document{foo, fruitV1InFolder}, 132 }, 133 134 expectedDocuments: []zoekt.Document{foo, fruitV1InFolder}, 135 }, 136 { 137 name: "add newer version of fruits inside folder", 138 addedDocuments: branchToDocumentMap{ 139 "main": []zoekt.Document{fruitV2InFolder}, 140 }, 141 optFn: func(t *testing.T, o *Options) { 142 o.BuildOptions.IsDelta = true 143 }, 144 145 expectedDocuments: []zoekt.Document{foo, fruitV2InFolder}, 146 }, 147 }, 148 }, 149 { 150 name: "addition", 151 branches: []string{"main"}, 152 steps: []step{ 153 { 154 name: "setup", 155 addedDocuments: branchToDocumentMap{ 156 "main": []zoekt.Document{helloWorld, fruitV1}, 157 }, 158 159 expectedDocuments: []zoekt.Document{helloWorld, fruitV1}, 160 }, 161 { 162 name: "add new file - foo", 163 addedDocuments: branchToDocumentMap{ 164 "main": []zoekt.Document{foo}, 165 }, 166 optFn: func(t *testing.T, o *Options) { 167 o.BuildOptions.IsDelta = true 168 }, 169 170 expectedDocuments: []zoekt.Document{helloWorld, fruitV1, foo}, 171 }, 172 }, 173 }, 174 { 175 name: "deletion", 176 branches: []string{"main"}, 177 steps: []step{ 178 { 179 name: "setup", 180 addedDocuments: branchToDocumentMap{ 181 "main": []zoekt.Document{helloWorld, fruitV1, foo}, 182 }, 183 184 expectedDocuments: []zoekt.Document{helloWorld, fruitV1, foo}, 185 }, 186 { 187 name: "delete foo file", 188 addedDocuments: nil, 189 deletedDocuments: branchToDocumentMap{ 190 "main": []zoekt.Document{foo}, 191 }, 192 193 optFn: func(t *testing.T, o *Options) { 194 o.BuildOptions.IsDelta = true 195 }, 196 197 expectedDocuments: []zoekt.Document{helloWorld, fruitV1}, 198 }, 199 }, 200 }, 201 { 202 name: "addition and deletion on only one branch", 203 branches: []string{"main", "release", "dev"}, 204 steps: []step{ 205 { 206 name: "setup", 207 addedDocuments: branchToDocumentMap{ 208 "main": []zoekt.Document{fruitV1}, 209 "release": []zoekt.Document{fruitV2}, 210 "dev": []zoekt.Document{fruitV3}, 211 }, 212 213 expectedDocuments: []zoekt.Document{fruitV1, fruitV2, fruitV3}, 214 }, 215 { 216 name: "replace fruits v3 with v4 on 'dev', delete fruits on 'main'", 217 addedDocuments: branchToDocumentMap{ 218 "dev": []zoekt.Document{fruitV4}, 219 }, 220 deletedDocuments: branchToDocumentMap{ 221 "main": []zoekt.Document{fruitV1}, 222 }, 223 224 optFn: func(t *testing.T, o *Options) { 225 o.BuildOptions.IsDelta = true 226 }, 227 228 expectedDocuments: []zoekt.Document{fruitV2, fruitV4}, 229 }, 230 }, 231 }, 232 { 233 name: "rename", 234 branches: []string{"main", "release"}, 235 steps: []step{ 236 { 237 name: "setup", 238 addedDocuments: branchToDocumentMap{ 239 "main": []zoekt.Document{fruitV1}, 240 "release": []zoekt.Document{fruitV2}, 241 }, 242 expectedDocuments: []zoekt.Document{fruitV1, fruitV2}, 243 }, 244 { 245 name: "rename fruits file on 'main' + ensure that unmodified fruits file on 'release' is still searchable", 246 addedDocuments: branchToDocumentMap{ 247 "main": []zoekt.Document{fruitV1WithNewName}, 248 }, 249 deletedDocuments: branchToDocumentMap{ 250 "main": []zoekt.Document{fruitV1}, 251 }, 252 253 optFn: func(t *testing.T, o *Options) { 254 o.BuildOptions.IsDelta = true 255 }, 256 257 expectedDocuments: []zoekt.Document{fruitV1WithNewName, fruitV2}, 258 }, 259 }, 260 }, 261 { 262 name: "modification: update one branch with version of document from another branch (a.k.a. Keegan's test)", 263 branches: []string{"main", "dev"}, 264 steps: []step{ 265 { 266 name: "setup", 267 addedDocuments: branchToDocumentMap{ 268 "main": []zoekt.Document{fruitV1}, 269 "dev": []zoekt.Document{fruitV2}, 270 }, 271 expectedDocuments: []zoekt.Document{fruitV1, fruitV2}, 272 }, 273 { 274 name: "switch main to dev's older version of fruits + bump dev's fruits to new version", 275 addedDocuments: branchToDocumentMap{ 276 "main": []zoekt.Document{fruitV2}, 277 "dev": []zoekt.Document{fruitV3}, 278 }, 279 280 optFn: func(t *testing.T, o *Options) { 281 o.BuildOptions.IsDelta = true 282 }, 283 284 expectedDocuments: []zoekt.Document{fruitV2, fruitV3}, 285 }, 286 }, 287 }, 288 { 289 name: "no-op delta builds (reindexing the same commits)", 290 branches: []string{"main", "dev"}, 291 steps: []step{ 292 { 293 name: "setup", 294 addedDocuments: branchToDocumentMap{ 295 "main": []zoekt.Document{fruitV1, foo}, 296 "dev": []zoekt.Document{helloWorld}, 297 }, 298 expectedDocuments: []zoekt.Document{fruitV1, foo, helloWorld}, 299 }, 300 { 301 name: "first no-op (normal build -> delta build)", 302 optFn: func(t *testing.T, o *Options) { 303 o.BuildOptions.IsDelta = true 304 }, 305 306 expectedDocuments: []zoekt.Document{fruitV1, foo, helloWorld}, 307 }, 308 { 309 name: "second no-op (delta build -> delta build)", 310 optFn: func(t *testing.T, o *Options) { 311 o.BuildOptions.IsDelta = true 312 }, 313 314 expectedDocuments: []zoekt.Document{fruitV1, foo, helloWorld}, 315 }, 316 }, 317 }, 318 { 319 name: "should fallback to normal build if no prior shards exist", 320 branches: []string{"main"}, 321 steps: []step{ 322 { 323 name: "attempt delta build on a repository that hasn't been indexed yet", 324 addedDocuments: branchToDocumentMap{ 325 "main": []zoekt.Document{helloWorld}, 326 }, 327 optFn: func(t *testing.T, o *Options) { 328 o.BuildOptions.IsDelta = true 329 }, 330 331 expectedFallbackToNormalBuild: true, 332 expectedDocuments: []zoekt.Document{helloWorld}, 333 }, 334 }, 335 }, 336 { 337 name: "should fallback to normal build if the set of requested repository branches changes", 338 branches: []string{"main", "release", "dev"}, 339 steps: []step{ 340 { 341 name: "setup", 342 addedDocuments: branchToDocumentMap{ 343 "main": []zoekt.Document{fruitV1}, 344 "release": []zoekt.Document{fruitV2}, 345 "dev": []zoekt.Document{fruitV3}, 346 }, 347 348 expectedDocuments: []zoekt.Document{fruitV1, fruitV2, fruitV3}, 349 }, 350 { 351 name: "try delta build after dropping 'main' branch from index ", 352 addedDocuments: branchToDocumentMap{ 353 "release": []zoekt.Document{fruitV4}, 354 }, 355 optFn: func(t *testing.T, o *Options) { 356 o.Branches = []string{"HEAD", "release", "dev"} // a bit of a hack to override it this way, but it gets the job done 357 o.BuildOptions.IsDelta = true 358 }, 359 360 expectedFallbackToNormalBuild: true, 361 expectedDocuments: []zoekt.Document{fruitV3, fruitV4}, 362 }, 363 }, 364 }, 365 { 366 name: "should fallback to normal build if one or more index options updates requires a full build", 367 branches: []string{"main"}, 368 steps: []step{ 369 { 370 name: "setup", 371 addedDocuments: branchToDocumentMap{ 372 "main": []zoekt.Document{fruitV1}, 373 }, 374 375 expectedDocuments: []zoekt.Document{fruitV1}, 376 }, 377 { 378 name: "try delta build after updating Disable CTags index option", 379 addedDocuments: branchToDocumentMap{ 380 "main": []zoekt.Document{fruitV2}, 381 }, 382 optFn: func(t *testing.T, o *Options) { 383 o.BuildOptions.IsDelta = true 384 o.BuildOptions.DisableCTags = true 385 }, 386 387 expectedFallbackToNormalBuild: true, 388 expectedDocuments: []zoekt.Document{fruitV2}, 389 }, 390 { 391 name: "try delta build after reverting Disable CTags index option", 392 addedDocuments: branchToDocumentMap{ 393 "main": []zoekt.Document{fruitV3}, 394 }, 395 optFn: func(t *testing.T, o *Options) { 396 o.BuildOptions.IsDelta = true 397 o.BuildOptions.DisableCTags = false 398 }, 399 400 expectedFallbackToNormalBuild: true, 401 expectedDocuments: []zoekt.Document{fruitV3}, 402 }, 403 }, 404 }, 405 { 406 name: "should successfully perform multiple delta builds after disabling symbols", 407 branches: []string{"main"}, 408 steps: []step{ 409 { 410 name: "setup", 411 addedDocuments: branchToDocumentMap{ 412 "main": []zoekt.Document{fruitV1}, 413 }, 414 415 expectedDocuments: []zoekt.Document{fruitV1}, 416 }, 417 { 418 name: "try delta build after updating Disable CTags index option", 419 addedDocuments: branchToDocumentMap{ 420 "main": []zoekt.Document{fruitV2}, 421 }, 422 optFn: func(t *testing.T, o *Options) { 423 o.BuildOptions.IsDelta = true 424 o.BuildOptions.DisableCTags = true 425 }, 426 427 expectedFallbackToNormalBuild: true, 428 expectedDocuments: []zoekt.Document{fruitV2}, 429 }, 430 { 431 name: "try another delta build while CTags is still disabled", 432 addedDocuments: branchToDocumentMap{ 433 "main": []zoekt.Document{fruitV3}, 434 }, 435 optFn: func(t *testing.T, o *Options) { 436 o.BuildOptions.IsDelta = true 437 o.BuildOptions.DisableCTags = true 438 }, 439 440 expectedDocuments: []zoekt.Document{fruitV3}, 441 }, 442 }, 443 }, 444 { 445 name: "should fallback to normal build if repository has unsupported Sourcegraph ignore file", 446 branches: []string{"main"}, 447 steps: []step{ 448 { 449 name: "setup", 450 addedDocuments: branchToDocumentMap{ 451 "main": []zoekt.Document{emptySourcegraphIgnore}, 452 }, 453 454 expectedDocuments: []zoekt.Document{emptySourcegraphIgnore}, 455 }, 456 { 457 name: "attempt delta build after modifying ignore file", 458 addedDocuments: branchToDocumentMap{ 459 "main": []zoekt.Document{sourcegraphIgnoreWithContent}, 460 }, 461 optFn: func(t *testing.T, o *Options) { 462 o.BuildOptions.IsDelta = true 463 }, 464 465 expectedFallbackToNormalBuild: true, 466 expectedDocuments: []zoekt.Document{sourcegraphIgnoreWithContent}, 467 }, 468 }, 469 }, 470 { 471 name: "should fallback to a full, normal build if the repository has more than the specified threshold of shards", 472 branches: []string{"main"}, 473 steps: []step{ 474 { 475 name: "setup: first shard", 476 addedDocuments: branchToDocumentMap{ 477 "main": []zoekt.Document{foo}, 478 }, 479 480 expectedDocuments: []zoekt.Document{foo}, 481 }, 482 { 483 name: "setup: second shard (delta)", 484 addedDocuments: branchToDocumentMap{ 485 "main": []zoekt.Document{fruitV1}, 486 }, 487 optFn: func(t *testing.T, o *Options) { 488 o.BuildOptions.IsDelta = true 489 }, 490 491 expectedDocuments: []zoekt.Document{foo, fruitV1}, 492 }, 493 { 494 name: "setup: third shard (delta)", 495 addedDocuments: branchToDocumentMap{ 496 "main": []zoekt.Document{helloWorld}, 497 }, 498 optFn: func(t *testing.T, o *Options) { 499 o.BuildOptions.IsDelta = true 500 }, 501 502 expectedDocuments: []zoekt.Document{foo, fruitV1, helloWorld}, 503 }, 504 { 505 name: "attempt another delta build after we already blew past the shard threshold", 506 addedDocuments: branchToDocumentMap{ 507 "main": []zoekt.Document{fruitV2InFolder}, 508 }, 509 optFn: func(t *testing.T, o *Options) { 510 o.DeltaShardNumberFallbackThreshold = 2 511 o.BuildOptions.IsDelta = true 512 }, 513 514 expectedFallbackToNormalBuild: true, 515 expectedDocuments: []zoekt.Document{foo, fruitV1, helloWorld, fruitV2InFolder}, 516 }, 517 }, 518 }, 519 } { 520 test := test 521 522 t.Run(test.name, func(t *testing.T) { 523 t.Parallel() 524 525 indexDir := t.TempDir() 526 repositoryDir := t.TempDir() 527 528 // setup: initialize the repository and all of its branches 529 runGitScript := func(t *testing.T, dir, script string) { 530 runScript(t, dir, script, "GIT_CONFIG_GLOBAL=", "GIT_CONFIG_SYSTEM=") 531 } 532 runGitScript(t, repositoryDir, "git init -b master") 533 runGitScript(t, repositoryDir, fmt.Sprintf("git config user.email %q", "you@example.com")) 534 runGitScript(t, repositoryDir, fmt.Sprintf("git config user.name %q", "Your Name")) 535 536 for _, b := range test.branches { 537 runGitScript(t, repositoryDir, fmt.Sprintf("git checkout -b %q", b)) 538 runGitScript(t, repositoryDir, fmt.Sprintf("git commit --allow-empty -m %q", "empty commit")) 539 } 540 541 for _, step := range test.steps { 542 t.Run(step.name, func(t *testing.T) { 543 for _, b := range test.branches { 544 // setup: for each branch, process any document deletions / additions and commit those changes 545 546 hadChange := false 547 548 runGitScript(t, repositoryDir, fmt.Sprintf("git checkout %q", b)) 549 550 for _, d := range step.deletedDocuments[b] { 551 hadChange = true 552 553 file := filepath.Join(repositoryDir, d.Name) 554 555 err := os.Remove(file) 556 if err != nil { 557 t.Fatalf("deleting file %q: %s", d.Name, err) 558 } 559 560 runGitScript(t, repositoryDir, fmt.Sprintf("git add %q", file)) 561 } 562 563 for _, d := range step.addedDocuments[b] { 564 hadChange = true 565 566 file := filepath.Join(repositoryDir, d.Name) 567 568 err := os.MkdirAll(filepath.Dir(file), 0o755) 569 if err != nil { 570 t.Fatalf("ensuring that folders exist for file %q: %s", file, err) 571 } 572 573 err = os.WriteFile(file, d.Content, 0o644) 574 if err != nil { 575 t.Fatalf("writing file %q: %s", d.Name, err) 576 } 577 578 runGitScript(t, repositoryDir, fmt.Sprintf("git add %q", file)) 579 } 580 581 if !hadChange { 582 continue 583 } 584 585 runGitScript(t, repositoryDir, fmt.Sprintf("git commit -m %q", step.name)) 586 } 587 588 // setup: prepare indexOptions with given overrides 589 buildOptions := build.Options{ 590 IndexDir: indexDir, 591 RepositoryDescription: zoekt.Repository{ 592 Name: "repository", 593 }, 594 IsDelta: false, 595 } 596 buildOptions.SetDefaults() 597 598 branches := append([]string{"HEAD"}, test.branches...) 599 600 options := Options{ 601 RepoDir: filepath.Join(repositoryDir, ".git"), 602 BuildOptions: buildOptions, 603 Branches: branches, 604 } 605 606 if step.optFn != nil { 607 step.optFn(t, &options) 608 } 609 610 // setup: prepare spy versions of prepare delta / normal build so that we can observe 611 // whether they were called appropriately 612 deltaBuildCalled := false 613 prepareDeltaSpy := func(options Options, repository *git.Repository) (repos map[fileKey]BlobLocation, branchMap map[fileKey][]string, branchVersions map[string]map[string]plumbing.Hash, changedOrDeletedPaths []string, err error) { 614 deltaBuildCalled = true 615 return prepareDeltaBuild(options, repository) 616 } 617 618 normalBuildCalled := false 619 prepareNormalSpy := func(options Options, repository *git.Repository) (repos map[fileKey]BlobLocation, branchMap map[fileKey][]string, branchVersions map[string]map[string]plumbing.Hash, err error) { 620 normalBuildCalled = true 621 return prepareNormalBuild(options, repository) 622 } 623 624 // run test 625 err := indexGitRepo(options, gitIndexConfig{ 626 prepareDeltaBuild: prepareDeltaSpy, 627 prepareNormalBuild: prepareNormalSpy, 628 }) 629 if err != nil { 630 t.Fatalf("IndexGitRepo: %s", err) 631 } 632 633 if options.BuildOptions.IsDelta != deltaBuildCalled { 634 // We should always try a delta build if we request it in the options. 635 t.Fatalf("expected deltaBuildCalled to be %t, got %t", options.BuildOptions.IsDelta, deltaBuildCalled) 636 } 637 638 if options.BuildOptions.IsDelta && (step.expectedFallbackToNormalBuild != normalBuildCalled) { 639 // We only check the normal spy on delta builds because it's only considered a "fallback" if we 640 // asked for a delta build in the first place. 641 t.Fatalf("expected normalBuildCalled to be %t, got %t", step.expectedFallbackToNormalBuild, normalBuildCalled) 642 } 643 644 // examine outcome: load shards into a searcher instance and run a dummy search query 645 // that returns every document contained in the shards 646 // 647 // then, compare returned set of documents with the expected set for the step and see if they agree 648 649 ss, err := shards.NewDirectorySearcher(indexDir) 650 if err != nil { 651 t.Fatalf("NewDirectorySearcher(%s): %s", indexDir, err) 652 } 653 defer ss.Close() 654 655 searchOpts := &zoekt.SearchOptions{Whole: true} 656 result, err := ss.Search(context.Background(), &query.Const{Value: true}, searchOpts) 657 if err != nil { 658 t.Fatalf("Search: %s", err) 659 } 660 661 var receivedDocuments []zoekt.Document 662 for _, f := range result.Files { 663 receivedDocuments = append(receivedDocuments, zoekt.Document{ 664 Name: f.FileName, 665 Content: f.Content, 666 }) 667 } 668 669 for _, docs := range [][]zoekt.Document{step.expectedDocuments, receivedDocuments} { 670 sort.Slice(docs, func(i, j int) bool { 671 a, b := docs[i], docs[j] 672 673 // first compare names, then fallback to contents if the names are equal 674 675 if a.Name < b.Name { 676 return true 677 } 678 679 if a.Name > b.Name { 680 return false 681 } 682 683 return bytes.Compare(a.Content, b.Content) < 0 684 }) 685 } 686 687 compareOptions := []cmp.Option{ 688 cmpopts.IgnoreFields(zoekt.Document{}, "Branches"), 689 cmpopts.EquateEmpty(), 690 } 691 692 if diff := cmp.Diff(step.expectedDocuments, receivedDocuments, compareOptions...); diff != "" { 693 t.Errorf("diff in received documents (-want +got):%s\n:", diff) 694 } 695 }) 696 } 697 }) 698 } 699} 700 701func TestRepoPathRanks(t *testing.T) { 702 pathRanks := repoPathRanks{ 703 Paths: map[string]float64{ 704 "search.go": 10.23, 705 "internal/index.go": 5.5, 706 "internal/scratch.go": 0.0, 707 "backend/search_test.go": 2.1, 708 }, 709 MeanRank: 3.3, 710 } 711 cases := []struct { 712 name string 713 path string 714 rank float64 715 }{ 716 { 717 name: "rank for standard file", 718 path: "search.go", 719 rank: 10.23, 720 }, 721 { 722 name: "file with rank 0", 723 path: "internal/scratch.go", 724 rank: 0.0, 725 }, 726 { 727 name: "rank for test file", 728 path: "backend/search_test.go", 729 rank: 2.1, 730 }, 731 { 732 name: "file with missing rank", 733 path: "internal/docs.md", 734 rank: 3.3, 735 }, 736 { 737 name: "test file with missing rank", 738 path: "backend/index_test.go", 739 rank: 0.0, 740 }, 741 { 742 name: "third-party file with missing rank", 743 path: "node_modules/search/index.js", 744 rank: 0.0, 745 }, 746 } 747 748 for _, tt := range cases { 749 t.Run(tt.name, func(t *testing.T) { 750 got := pathRanks.rank(tt.path) 751 if got != tt.rank { 752 t.Errorf("expected file '%s' to have rank %f, but got %f", tt.path, tt.rank, got) 753 } 754 }) 755 } 756} 757 758func runScript(t *testing.T, cwd string, script string, env ...string) { 759 err := os.MkdirAll(cwd, 0o755) 760 if err != nil { 761 t.Fatalf("ensuring path %q exists: %s", cwd, err) 762 } 763 764 cmd := exec.Command("sh", "-euxc", script) 765 cmd.Dir = cwd 766 cmd.Env = env 767 768 if out, err := cmd.CombinedOutput(); err != nil { 769 t.Fatalf("execution error: %v, output %s", err, out) 770 } 771}