fork of https://github.com/sourcegraph/zoekt
1package index
2
3import (
4 "bytes"
5 "fmt"
6 "testing"
7
8 "github.com/google/go-cmp/cmp"
9
10 "github.com/sourcegraph/zoekt"
11)
12
13func TestLimitMatches(t *testing.T) {
14 cases := []struct {
15 // Represents a SearchResult with three dimensions:
16 // 1. outer slice is `Files`
17 // 2. inner slice is `{Chunk,Line}Matches`
18 // 3. value is the length of `Ranges`/`LineFragments`
19 in [][]int
20 limit int
21 expected [][]int
22 }{{
23 in: [][]int{{1, 1, 1}},
24 limit: 1,
25 expected: [][]int{{1}},
26 }, {
27 in: [][]int{{1, 1, 1}},
28 limit: 3,
29 expected: [][]int{{1, 1, 1}},
30 }, {
31 in: [][]int{{1, 1, 1}},
32 limit: 4,
33 expected: [][]int{{1, 1, 1}},
34 }, {
35 in: [][]int{{2, 2, 2}},
36 limit: 4,
37 expected: [][]int{{2, 2}},
38 }, {
39 in: [][]int{{2, 2, 2}},
40 limit: 3,
41 expected: [][]int{{2, 1}},
42 }, {
43 in: [][]int{{2, 2, 2}},
44 limit: 1,
45 expected: [][]int{{1}},
46 }, {
47 in: [][]int{{1}, {1}},
48 limit: 2,
49 expected: [][]int{{1}, {1}},
50 }, {
51 in: [][]int{{1}, {1}},
52 limit: 1,
53 expected: [][]int{{1}},
54 }, {
55 in: [][]int{{1}, {1, 3}},
56 limit: 4,
57 expected: [][]int{{1}, {1, 2}},
58 }, {
59 in: [][]int{{1}, {2, 2}, {3, 3, 3}},
60 limit: 4,
61 expected: [][]int{{1}, {2, 1}},
62 }}
63
64 for _, tc := range cases {
65 t.Run("ChunkMatches", func(t *testing.T) {
66 // Generate a ChunkMatch suitable for testing `LimitChunkMatches`.
67 generateChunkMatch := func(numRanges, lineNumber int) (zoekt.ChunkMatch, int) {
68 cm := zoekt.ChunkMatch{SymbolInfo: make([]*zoekt.Symbol, numRanges)}
69
70 // To simplify testing, we generate Content and the associated
71 // Ranges with fixed logic: each ChunkMatch has 1 line of
72 // context, and each Range spans two lines. It'd probably be
73 // better to do some kind of property-based testing, but this is
74 // alright.
75
76 // 1 line of context.
77 cm.Content = append(cm.Content, []byte("context\n")...)
78 for i := 0; i < numRanges; i += 1 {
79 cm.Ranges = append(cm.Ranges, zoekt.Range{
80 // We only provide LineNumber as that's all that's
81 // relevant.
82 Start: zoekt.Location{LineNumber: uint32(lineNumber + (2 * i) + 1)},
83 End: zoekt.Location{LineNumber: uint32(lineNumber + (2 * i) + 2)},
84 })
85 cm.Content = append(cm.Content, fmt.Appendf(nil, "range%dStart\nrange%dEnd\n", i, i)...)
86 }
87 // 1 line of context. Content in zoekt notably just does not
88 // contain a trailing newline.
89 cm.Content = append(cm.Content, []byte("context")...)
90
91 // Next Chunk starts two lines past the number of lines we just
92 // added.
93 return cm, lineNumber + (2 * numRanges) + 4
94 }
95
96 res := zoekt.SearchResult{}
97 for _, file := range tc.in {
98 fm := zoekt.FileMatch{}
99 lineNumber := 0
100 for _, numRanges := range file {
101 var cm zoekt.ChunkMatch
102 cm, lineNumber = generateChunkMatch(numRanges, lineNumber)
103 fm.ChunkMatches = append(fm.ChunkMatches, cm)
104 }
105 res.Files = append(res.Files, fm)
106 }
107
108 res.Files = SortAndTruncateFiles(res.Files, &zoekt.SearchOptions{
109 MaxMatchDisplayCount: tc.limit,
110 ChunkMatches: true,
111 })
112
113 var got [][]int
114 for _, fm := range res.Files {
115 var matches []int
116 for _, cm := range fm.ChunkMatches {
117 if len(cm.Ranges) != len(cm.SymbolInfo) {
118 t.Errorf("Expected Ranges and SymbolInfo to be the same size, but got %d and %d", len(cm.Ranges), len(cm.SymbolInfo))
119 }
120
121 // Using the logic from generateChunkMatch.
122 expectedNewlines := 1 + (len(cm.Ranges) * 2)
123 actualNewlines := bytes.Count(cm.Content, []byte("\n"))
124 if actualNewlines != expectedNewlines {
125 t.Errorf("Expected Content to have %d newlines but got %d", expectedNewlines, actualNewlines)
126 }
127
128 matches = append(matches, len(cm.Ranges))
129 }
130 got = append(got, matches)
131 }
132 if !cmp.Equal(tc.expected, got) {
133 t.Errorf("Expected %v but got %v", tc.expected, got)
134 }
135 })
136
137 t.Run("LineMatches", func(t *testing.T) {
138 res := zoekt.SearchResult{}
139 for _, file := range tc.in {
140 fm := zoekt.FileMatch{}
141 for _, numFragments := range file {
142 fm.LineMatches = append(fm.LineMatches, zoekt.LineMatch{LineFragments: make([]zoekt.LineFragmentMatch, numFragments)})
143 }
144 res.Files = append(res.Files, fm)
145 }
146
147 res.Files = SortAndTruncateFiles(res.Files, &zoekt.SearchOptions{
148 MaxMatchDisplayCount: tc.limit,
149 ChunkMatches: false,
150 })
151
152 var got [][]int
153 for _, fm := range res.Files {
154 var matches []int
155 for _, lm := range fm.LineMatches {
156 matches = append(matches, len(lm.LineFragments))
157 }
158 got = append(got, matches)
159 }
160 if !cmp.Equal(tc.expected, got) {
161 t.Errorf("Expected %v but got %v", tc.expected, got)
162 }
163 })
164 }
165}