1 | // Copyright 2013 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | package godoc |
6 | |
7 | import ( |
8 | "bytes" |
9 | "reflect" |
10 | "sort" |
11 | "strings" |
12 | "testing" |
13 | |
14 | "golang.org/x/tools/godoc/vfs/mapfs" |
15 | ) |
16 | |
17 | func newCorpus(t *testing.T) *Corpus { |
18 | c := NewCorpus(mapfs.New(map[string]string{ |
19 | "src/foo/foo.go": `// Package foo is an example. |
20 | package foo |
21 | |
22 | import "bar" |
23 | |
24 | const Pi = 3.1415 |
25 | |
26 | var Foos []Foo |
27 | |
28 | // Foo is stuff. |
29 | type Foo struct{} |
30 | |
31 | func New() *Foo { |
32 | return new(Foo) |
33 | } |
34 | `, |
35 | "src/bar/bar.go": `// Package bar is another example to test races. |
36 | package bar |
37 | `, |
38 | "src/other/bar/bar.go": `// Package bar is another bar package. |
39 | package bar |
40 | func X() {} |
41 | `, |
42 | "src/skip/skip.go": `// Package skip should be skipped. |
43 | package skip |
44 | func Skip() {} |
45 | `, |
46 | "src/bar/readme.txt": `Whitelisted text file. |
47 | `, |
48 | "src/bar/baz.zzz": `Text file not whitelisted. |
49 | `, |
50 | })) |
51 | c.IndexEnabled = true |
52 | c.IndexDirectory = func(dir string) bool { |
53 | return !strings.Contains(dir, "skip") |
54 | } |
55 | |
56 | if err := c.Init(); err != nil { |
57 | t.Fatal(err) |
58 | } |
59 | return c |
60 | } |
61 | |
62 | func TestIndex(t *testing.T) { |
63 | for _, docs := range []bool{true, false} { |
64 | for _, goCode := range []bool{true, false} { |
65 | for _, fullText := range []bool{true, false} { |
66 | c := newCorpus(t) |
67 | c.IndexDocs = docs |
68 | c.IndexGoCode = goCode |
69 | c.IndexFullText = fullText |
70 | c.UpdateIndex() |
71 | ix, _ := c.CurrentIndex() |
72 | if ix == nil { |
73 | t.Fatal("no index") |
74 | } |
75 | t.Logf("docs, goCode, fullText = %v,%v,%v", docs, goCode, fullText) |
76 | testIndex(t, c, ix) |
77 | } |
78 | } |
79 | } |
80 | } |
81 | |
82 | func TestIndexWriteRead(t *testing.T) { |
83 | type key struct { |
84 | docs, goCode, fullText bool |
85 | } |
86 | type val struct { |
87 | buf *bytes.Buffer |
88 | c *Corpus |
89 | } |
90 | m := map[key]val{} |
91 | |
92 | for _, docs := range []bool{true, false} { |
93 | for _, goCode := range []bool{true, false} { |
94 | for _, fullText := range []bool{true, false} { |
95 | k := key{docs, goCode, fullText} |
96 | c := newCorpus(t) |
97 | c.IndexDocs = docs |
98 | c.IndexGoCode = goCode |
99 | c.IndexFullText = fullText |
100 | c.UpdateIndex() |
101 | ix, _ := c.CurrentIndex() |
102 | if ix == nil { |
103 | t.Fatal("no index") |
104 | } |
105 | var buf bytes.Buffer |
106 | nw, err := ix.WriteTo(&buf) |
107 | if err != nil { |
108 | t.Fatalf("Index.WriteTo: %v", err) |
109 | } |
110 | m[k] = val{bytes.NewBuffer(buf.Bytes()), c} |
111 | ix2 := new(Index) |
112 | nr, err := ix2.ReadFrom(&buf) |
113 | if err != nil { |
114 | t.Fatalf("Index.ReadFrom: %v", err) |
115 | } |
116 | if nr != nw { |
117 | t.Errorf("Wrote %d bytes to index but read %d", nw, nr) |
118 | } |
119 | testIndex(t, c, ix) |
120 | } |
121 | } |
122 | } |
123 | // Test CompatibleWith |
124 | for k1, v1 := range m { |
125 | ix := new(Index) |
126 | if _, err := ix.ReadFrom(v1.buf); err != nil { |
127 | t.Fatalf("Index.ReadFrom: %v", err) |
128 | } |
129 | for k2, v2 := range m { |
130 | if got, want := ix.CompatibleWith(v2.c), k1 == k2; got != want { |
131 | t.Errorf("CompatibleWith = %v; want %v for %v, %v", got, want, k1, k2) |
132 | } |
133 | } |
134 | } |
135 | } |
136 | |
137 | func testIndex(t *testing.T, c *Corpus, ix *Index) { |
138 | if _, ok := ix.words["Skip"]; ok { |
139 | t.Errorf("the word Skip was found; expected it to be skipped") |
140 | } |
141 | checkStats(t, c, ix) |
142 | checkImportCount(t, c, ix) |
143 | checkPackagePath(t, c, ix) |
144 | checkExports(t, c, ix) |
145 | checkIdents(t, c, ix) |
146 | } |
147 | |
148 | // checkStats checks the Index's statistics. |
149 | // Some statistics are only set when we're indexing Go code. |
150 | func checkStats(t *testing.T, c *Corpus, ix *Index) { |
151 | want := Statistics{} |
152 | if c.IndexFullText { |
153 | want.Bytes = 314 |
154 | want.Files = 4 |
155 | want.Lines = 21 |
156 | } else if c.IndexDocs || c.IndexGoCode { |
157 | want.Bytes = 291 |
158 | want.Files = 3 |
159 | want.Lines = 20 |
160 | } |
161 | if c.IndexGoCode { |
162 | want.Words = 8 |
163 | want.Spots = 12 |
164 | } |
165 | if got := ix.Stats(); !reflect.DeepEqual(got, want) { |
166 | t.Errorf("Stats = %#v; want %#v", got, want) |
167 | } |
168 | } |
169 | |
170 | // checkImportCount checks the Index's import count map. |
171 | // It is only set when we're indexing Go code. |
172 | func checkImportCount(t *testing.T, c *Corpus, ix *Index) { |
173 | want := map[string]int{} |
174 | if c.IndexGoCode { |
175 | want = map[string]int{ |
176 | "bar": 1, |
177 | } |
178 | } |
179 | if got := ix.ImportCount(); !reflect.DeepEqual(got, want) { |
180 | t.Errorf("ImportCount = %v; want %v", got, want) |
181 | } |
182 | } |
183 | |
184 | // checkPackagePath checks the Index's package path map. |
185 | // It is set if at least one of the indexing options is enabled. |
186 | func checkPackagePath(t *testing.T, c *Corpus, ix *Index) { |
187 | want := map[string]map[string]bool{} |
188 | if c.IndexDocs || c.IndexGoCode || c.IndexFullText { |
189 | want = map[string]map[string]bool{ |
190 | "foo": { |
191 | "foo": true, |
192 | }, |
193 | "bar": { |
194 | "bar": true, |
195 | "other/bar": true, |
196 | }, |
197 | } |
198 | } |
199 | if got := ix.PackagePath(); !reflect.DeepEqual(got, want) { |
200 | t.Errorf("PackagePath = %v; want %v", got, want) |
201 | } |
202 | } |
203 | |
204 | // checkExports checks the Index's exports map. |
205 | // It is only set when we're indexing Go code. |
206 | func checkExports(t *testing.T, c *Corpus, ix *Index) { |
207 | want := map[string]map[string]SpotKind{} |
208 | if c.IndexGoCode { |
209 | want = map[string]map[string]SpotKind{ |
210 | "foo": { |
211 | "Pi": ConstDecl, |
212 | "Foos": VarDecl, |
213 | "Foo": TypeDecl, |
214 | "New": FuncDecl, |
215 | }, |
216 | "other/bar": { |
217 | "X": FuncDecl, |
218 | }, |
219 | } |
220 | } |
221 | if got := ix.Exports(); !reflect.DeepEqual(got, want) { |
222 | t.Errorf("Exports = %v; want %v", got, want) |
223 | } |
224 | } |
225 | |
226 | // checkIdents checks the Index's indents map. |
227 | // It is only set when we're indexing documentation. |
228 | func checkIdents(t *testing.T, c *Corpus, ix *Index) { |
229 | want := map[SpotKind]map[string][]Ident{} |
230 | if c.IndexDocs { |
231 | want = map[SpotKind]map[string][]Ident{ |
232 | PackageClause: { |
233 | "bar": { |
234 | {"bar", "bar", "bar", "Package bar is another example to test races."}, |
235 | {"other/bar", "bar", "bar", "Package bar is another bar package."}, |
236 | }, |
237 | "foo": {{"foo", "foo", "foo", "Package foo is an example."}}, |
238 | "other": {{"other/bar", "bar", "bar", "Package bar is another bar package."}}, |
239 | }, |
240 | ConstDecl: { |
241 | "Pi": {{"foo", "foo", "Pi", ""}}, |
242 | }, |
243 | VarDecl: { |
244 | "Foos": {{"foo", "foo", "Foos", ""}}, |
245 | }, |
246 | TypeDecl: { |
247 | "Foo": {{"foo", "foo", "Foo", "Foo is stuff."}}, |
248 | }, |
249 | FuncDecl: { |
250 | "New": {{"foo", "foo", "New", ""}}, |
251 | "X": {{"other/bar", "bar", "X", ""}}, |
252 | }, |
253 | } |
254 | } |
255 | if got := ix.Idents(); !reflect.DeepEqual(got, want) { |
256 | t.Errorf("Idents = %v; want %v", got, want) |
257 | } |
258 | } |
259 | |
260 | func TestIdentResultSort(t *testing.T) { |
261 | ic := map[string]int{ |
262 | "/a/b/pkg1": 10, |
263 | "/a/b/pkg2": 2, |
264 | "/b/d/pkg3": 20, |
265 | } |
266 | for _, tc := range []struct { |
267 | ir []Ident |
268 | exp []Ident |
269 | }{ |
270 | { |
271 | ir: []Ident{ |
272 | {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
273 | {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
274 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
275 | }, |
276 | exp: []Ident{ |
277 | {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
278 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
279 | {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
280 | }, |
281 | }, |
282 | { |
283 | ir: []Ident{ |
284 | {"/a/a/pkg1", "pkg1", "MyFunc1", ""}, |
285 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
286 | }, |
287 | exp: []Ident{ |
288 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
289 | {"/a/a/pkg1", "pkg1", "MyFunc1", ""}, |
290 | }, |
291 | }, |
292 | } { |
293 | if sort.Sort(byImportCount{tc.ir, ic}); !reflect.DeepEqual(tc.ir, tc.exp) { |
294 | t.Errorf("got: %v, want %v", tc.ir, tc.exp) |
295 | } |
296 | } |
297 | } |
298 | |
299 | func TestIdentFilter(t *testing.T) { |
300 | ic := map[string]int{} |
301 | for _, tc := range []struct { |
302 | ir []Ident |
303 | pak string |
304 | exp []Ident |
305 | }{ |
306 | { |
307 | ir: []Ident{ |
308 | {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
309 | {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
310 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
311 | }, |
312 | pak: "pkg2", |
313 | exp: []Ident{ |
314 | {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
315 | }, |
316 | }, |
317 | } { |
318 | res := byImportCount{tc.ir, ic}.filter(tc.pak) |
319 | if !reflect.DeepEqual(res, tc.exp) { |
320 | t.Errorf("got: %v, want %v", res, tc.exp) |
321 | } |
322 | } |
323 | } |
324 |
Members