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