diff --git a/internal/codeintel/codenav/BUILD.bazel b/internal/codeintel/codenav/BUILD.bazel index e8e84bd69d9..c098861ffd5 100644 --- a/internal/codeintel/codenav/BUILD.bazel +++ b/internal/codeintel/codenav/BUILD.bazel @@ -74,9 +74,8 @@ go_test( "service_references_test.go", "service_snapshot_test.go", "service_stencil_test.go", - "service_syntactic_usages_test.go", "service_test.go", - "utils_test.go", + "syntactic_test.go", ], embed = [":codenav"], tags = [TAG_PLATFORM_GRAPH], diff --git a/internal/codeintel/codenav/helpers_test.go b/internal/codeintel/codenav/helpers_test.go index 1df5e22407f..822e16ca067 100644 --- a/internal/codeintel/codenav/helpers_test.go +++ b/internal/codeintel/codenav/helpers_test.go @@ -2,6 +2,7 @@ package codenav import ( "context" + "strings" "testing" genslices "github.com/life4/genesis/slices" @@ -13,6 +14,10 @@ import ( lsifstoremocks "github.com/sourcegraph/sourcegraph/internal/codeintel/codenav/internal/lsifstore/mocks" "github.com/sourcegraph/sourcegraph/internal/codeintel/core" uploadsshared "github.com/sourcegraph/sourcegraph/internal/codeintel/uploads/shared" + "github.com/sourcegraph/sourcegraph/internal/search" + searchClient "github.com/sourcegraph/sourcegraph/internal/search/client" + "github.com/sourcegraph/sourcegraph/internal/search/result" + "github.com/sourcegraph/sourcegraph/internal/search/streaming" "github.com/sourcegraph/sourcegraph/lib/errors" ) @@ -152,38 +157,55 @@ func shiftPos(pos scip.Position, numLines int32) scip.Position { // A GitTreeTranslator that returns positions and ranges shifted by numLines // and returns failed translations for path/range pairs if shouldFail returns true -func fakeTranslator( +type fakeTranslator struct { + from api.CommitID + to api.CommitID + numLines int + shouldFail func(core.RepoRelPath, scip.Range) bool +} + +func (t fakeTranslator) TranslatePosition(ctx context.Context, from, to api.CommitID, path core.RepoRelPath, pos scip.Position) (core.Option[scip.Position], error) { + numLines := t.numLines + if from == t.to && to == t.from { + numLines = -numLines + } + if t.shouldFail(path, scip.Range{Start: pos, End: pos}) { + return core.None[scip.Position](), nil + } + return core.Some(shiftPos(pos, int32(numLines))), nil +} + +func (t fakeTranslator) TranslateRange(ctx context.Context, from, to api.CommitID, path core.RepoRelPath, r scip.Range) (core.Option[scip.Range], error) { + numLines := t.numLines + if from == t.to && to == t.from { + numLines = -numLines + } + if t.shouldFail(path, r) { + return core.None[scip.Range](), nil + } + return core.Some(shiftSCIPRange(r, numLines)), nil +} + +func (t fakeTranslator) Prefetch(ctx context.Context, from api.CommitID, to api.CommitID, paths []core.RepoRelPath) { + return +} + +func NewFakeTranslator( from, to api.CommitID, numLines int, shouldFail func(core.RepoRelPath, scip.Range) bool, ) GitTreeTranslator { - translator := NewMockGitTreeTranslator() - translator.TranslatePositionFunc.SetDefaultHook(func(ctx context.Context, f, t api.CommitID, path core.RepoRelPath, pos scip.Position) (core.Option[scip.Position], error) { - numLines := numLines - if f == to && t == from { - numLines = -numLines - } - if shouldFail(path, scip.Range{Start: pos, End: pos}) { - return core.None[scip.Position](), nil - } - return core.Some(shiftPos(pos, int32(numLines))), nil - }) - translator.TranslateRangeFunc.SetDefaultHook(func(ctx context.Context, f, t api.CommitID, path core.RepoRelPath, range_ scip.Range) (core.Option[scip.Range], error) { - numLines := numLines - if f == to && t == from { - numLines = -numLines - } - if shouldFail(path, range_) { - return core.None[scip.Range](), nil - } - return core.Some(shiftSCIPRange(range_, numLines)), nil - }) - return translator + return fakeTranslator{ + from: from, + to: to, + numLines: numLines, + shouldFail: shouldFail, + } } // A GitTreeTranslator that returns all positions and ranges shifted by numLines. func shiftAllTranslator(from, to api.CommitID, numLines int) GitTreeTranslator { - return fakeTranslator(from, to, numLines, func(path core.RepoRelPath, range_ scip.Range) bool { return false }) + return NewFakeTranslator(from, to, numLines, func(path core.RepoRelPath, range_ scip.Range) bool { return false }) } // A GitTreeTranslator that returns all positions and ranges unchanged @@ -238,3 +260,94 @@ func expectDefinitionRanges[T MatchLike](t *testing.T, matches []T, ranges ...sc } } } + +func scipToResultPosition(p scip.Position) result.Location { + return result.Location{ + Line: int(p.Line), + Column: int(p.Character), + } +} + +func scipToResultRange(r scip.Range) result.Range { + return result.Range{ + Start: scipToResultPosition(r.Start), + End: scipToResultPosition(r.End), + } +} + +// scipToSymbolMatch "reverse engineers" the lsp.Range function on result.Symbol +func scipToSymbolMatch(r scip.Range) *result.SymbolMatch { + return &result.SymbolMatch{ + Symbol: result.Symbol{ + Line: int(r.Start.Line + 1), + Character: int(r.Start.Character), + Name: strings.Repeat("a", int(r.End.Character-r.Start.Character)), + }} +} + +type FakeSearchBuilder struct { + fileMatches []result.Match + symbolMatches []result.Match +} + +func FakeSearchClient() FakeSearchBuilder { + return FakeSearchBuilder{ + fileMatches: []result.Match{}, + symbolMatches: make([]result.Match, 0), + } +} + +func ChunkMatchWithLine(range_ scip.Range, line string) result.ChunkMatch { + return result.ChunkMatch{ + Ranges: []result.Range{scipToResultRange(range_)}, + Content: line, + ContentStart: result.Location{ + Line: int(range_.Start.Line), + Column: 0, + }, + } +} + +func ChunkMatch(range_ scip.Range) result.ChunkMatch { + return ChunkMatchWithLine(range_, "chonky") +} + +func ChunkMatches(ranges ...scip.Range) []result.ChunkMatch { + return genslices.Map(ranges, ChunkMatch) +} + +func (b FakeSearchBuilder) WithFile(file string, matches ...result.ChunkMatch) FakeSearchBuilder { + b.fileMatches = append(b.fileMatches, &result.FileMatch{ + File: result.File{Path: file}, + ChunkMatches: matches, + }) + return b +} + +func (b FakeSearchBuilder) WithSymbols(file string, ranges ...scip.Range) FakeSearchBuilder { + b.symbolMatches = append(b.symbolMatches, &result.FileMatch{ + File: result.File{Path: file}, + Symbols: genslices.Map(ranges, scipToSymbolMatch), + }) + return b +} + +func (b FakeSearchBuilder) Build() searchClient.SearchClient { + mockSearchClient := searchClient.NewMockSearchClient() + mockSearchClient.PlanFunc.SetDefaultHook(func(_ context.Context, _ string, _ *string, query string, _ search.Mode, _ search.Protocol, _ *int32) (*search.Inputs, error) { + return &search.Inputs{OriginalQuery: query}, nil + }) + mockSearchClient.ExecuteFunc.SetDefaultHook(func(_ context.Context, s streaming.Sender, i *search.Inputs) (*search.Alert, error) { + if strings.Contains(i.OriginalQuery, "type:file") { + s.Send(streaming.SearchEvent{ + Results: b.fileMatches, + }) + } else if strings.Contains(i.OriginalQuery, "type:symbol") { + s.Send(streaming.SearchEvent{ + Results: b.symbolMatches, + }) + } + return nil, nil + }) + return mockSearchClient +} diff --git a/internal/codeintel/codenav/mapped_index_test.go b/internal/codeintel/codenav/mapped_index_test.go index bf735f3f1b8..9e360900ee6 100644 --- a/internal/codeintel/codenav/mapped_index_test.go +++ b/internal/codeintel/codenav/mapped_index_test.go @@ -131,7 +131,7 @@ func TestMappedIndex_GetOccurrencesAtRangeAfterGetOccurrences(t *testing.T) { func TestMappedIndex_GetDocumentsFiltersFailedTranslation(t *testing.T) { targetCommit, upload, lsifStore := setupSimpleUpload() - translator := fakeTranslator(upload.GetCommit(), targetCommit, 0, func(path core.RepoRelPath, rg scip.Range) bool { + translator := NewFakeTranslator(upload.GetCommit(), targetCommit, 0, func(path core.RepoRelPath, rg scip.Range) bool { return rg.CompareStrict(testRange(1)) == 0 }) mappedIndex := NewMappedIndexFromTranslator(lsifStore, translator, upload, targetCommit) @@ -147,7 +147,7 @@ func TestMappedIndex_GetDocumentsFiltersFailedTranslation(t *testing.T) { func TestMappedIndex_GetDocumentFailedTranslation(t *testing.T) { targetCommit, upload, lsifStore := setupSimpleUpload() - translator := fakeTranslator(upload.GetCommit(), targetCommit, 0, func(path core.RepoRelPath, rg scip.Range) bool { + translator := NewFakeTranslator(upload.GetCommit(), targetCommit, 0, func(path core.RepoRelPath, rg scip.Range) bool { return path.RawValue() == "indexRoot/b.go" || rg.CompareStrict(testRange(1)) == 0 }) mappedIndex := NewMappedIndexFromTranslator(lsifStore, translator, upload, targetCommit) diff --git a/internal/codeintel/codenav/service_syntactic_usages_test.go b/internal/codeintel/codenav/syntactic_test.go similarity index 98% rename from internal/codeintel/codenav/service_syntactic_usages_test.go rename to internal/codeintel/codenav/syntactic_test.go index ca1164ea323..ecfa0fd532b 100644 --- a/internal/codeintel/codenav/service_syntactic_usages_test.go +++ b/internal/codeintel/codenav/syntactic_test.go @@ -152,7 +152,7 @@ func TestSyntacticUsages_IndexCommitTranslated(t *testing.T) { ref("edited", shiftSCIPRange(editedRange, 2)), ref("noMatch", noMatchRange))) // Ranges in the index are shifted by +2, so the translator needs to shift by -2 to match up with the search results. - fakeMappedIndex := NewMappedIndexFromTranslator(lsifStore, fakeTranslator(upload.GetCommit(), targetCommit, -2, + fakeMappedIndex := NewMappedIndexFromTranslator(lsifStore, NewFakeTranslator(upload.GetCommit(), targetCommit, -2, func(_ core.RepoRelPath, r scip.Range) bool { // When a line was edited in a diff we invalidate all occurrences on that line. return r.CompareStrict(editedRange) == 0 diff --git a/internal/codeintel/codenav/utils_test.go b/internal/codeintel/codenav/utils_test.go deleted file mode 100644 index f0ce3ae43db..00000000000 --- a/internal/codeintel/codenav/utils_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// This file does not contain tests for utils.go, instead it contains utils -// for setting up the test environment for our codenav tests. -package codenav - -import ( - "context" - "strings" - - genslices "github.com/life4/genesis/slices" - "github.com/sourcegraph/scip/bindings/go/scip" - - "github.com/sourcegraph/sourcegraph/internal/search" - searchClient "github.com/sourcegraph/sourcegraph/internal/search/client" - "github.com/sourcegraph/sourcegraph/internal/search/result" - "github.com/sourcegraph/sourcegraph/internal/search/streaming" -) - -func scipToResultPosition(p scip.Position) result.Location { - return result.Location{ - Line: int(p.Line), - Column: int(p.Character), - } -} - -func scipToResultRange(r scip.Range) result.Range { - return result.Range{ - Start: scipToResultPosition(r.Start), - End: scipToResultPosition(r.End), - } -} - -// scipToSymbolMatch "reverse engineers" the lsp.Range function on result.Symbol -func scipToSymbolMatch(r scip.Range) *result.SymbolMatch { - return &result.SymbolMatch{ - Symbol: result.Symbol{ - Line: int(r.Start.Line + 1), - Character: int(r.Start.Character), - Name: strings.Repeat("a", int(r.End.Character-r.Start.Character)), - }} -} - -type FakeSearchBuilder struct { - fileMatches []result.Match - symbolMatches []result.Match -} - -func FakeSearchClient() FakeSearchBuilder { - return FakeSearchBuilder{ - fileMatches: []result.Match{}, - symbolMatches: make([]result.Match, 0), - } -} - -func ChunkMatchWithLine(range_ scip.Range, line string) result.ChunkMatch { - return result.ChunkMatch{ - Ranges: []result.Range{scipToResultRange(range_)}, - Content: line, - ContentStart: result.Location{ - Line: int(range_.Start.Line), - Column: 0, - }, - } -} - -func ChunkMatch(range_ scip.Range) result.ChunkMatch { - return ChunkMatchWithLine(range_, "chonky") -} - -func ChunkMatches(ranges ...scip.Range) []result.ChunkMatch { - return genslices.Map(ranges, ChunkMatch) -} - -func (b FakeSearchBuilder) WithFile(file string, matches ...result.ChunkMatch) FakeSearchBuilder { - b.fileMatches = append(b.fileMatches, &result.FileMatch{ - File: result.File{Path: file}, - ChunkMatches: matches, - }) - return b -} - -func (b FakeSearchBuilder) WithSymbols(file string, ranges ...scip.Range) FakeSearchBuilder { - b.symbolMatches = append(b.symbolMatches, &result.FileMatch{ - File: result.File{Path: file}, - Symbols: genslices.Map(ranges, scipToSymbolMatch), - }) - return b -} - -func (b FakeSearchBuilder) Build() searchClient.SearchClient { - mockSearchClient := searchClient.NewMockSearchClient() - mockSearchClient.PlanFunc.SetDefaultHook(func(_ context.Context, _ string, _ *string, query string, _ search.Mode, _ search.Protocol, _ *int32) (*search.Inputs, error) { - return &search.Inputs{OriginalQuery: query}, nil - }) - mockSearchClient.ExecuteFunc.SetDefaultHook(func(_ context.Context, s streaming.Sender, i *search.Inputs) (*search.Alert, error) { - if strings.Contains(i.OriginalQuery, "type:file") { - s.Send(streaming.SearchEvent{ - Results: b.fileMatches, - }) - } else if strings.Contains(i.OriginalQuery, "type:symbol") { - s.Send(streaming.SearchEvent{ - Results: b.symbolMatches, - }) - } - return nil, nil - }) - return mockSearchClient -}