Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 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 ( |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 8 | "bytes" |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 9 | "reflect" |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 10 | "sort" |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 11 | "strings" |
| 12 | "testing" |
| 13 | |
Andrew Gerrand | 5ebbcd1 | 2014-11-10 08:50:40 +1100 | [diff] [blame] | 14 | "golang.org/x/tools/godoc/vfs/mapfs" |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 15 | ) |
| 16 | |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 17 | func newCorpus(t *testing.T) *Corpus { |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 18 | c := NewCorpus(mapfs.New(map[string]string{ |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 19 | "src/foo/foo.go": `// Package foo is an example. |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 20 | package foo |
| 21 | |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 22 | import "bar" |
| 23 | |
Marko Mikulicic | ae3dd78 | 2013-11-15 11:03:25 -0800 | [diff] [blame] | 24 | const Pi = 3.1415 |
| 25 | |
| 26 | var Foos []Foo |
| 27 | |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 28 | // Foo is stuff. |
| 29 | type Foo struct{} |
| 30 | |
| 31 | func New() *Foo { |
| 32 | return new(Foo) |
| 33 | } |
| 34 | `, |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 35 | "src/bar/bar.go": `// Package bar is another example to test races. |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 36 | package bar |
| 37 | `, |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 38 | "src/other/bar/bar.go": `// Package bar is another bar package. |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 39 | package bar |
| 40 | func X() {} |
| 41 | `, |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 42 | "src/skip/skip.go": `// Package skip should be skipped. |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 43 | package skip |
| 44 | func Skip() {} |
| 45 | `, |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 46 | "src/bar/readme.txt": `Whitelisted text file. |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 47 | `, |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 48 | "src/bar/baz.zzz": `Text file not whitelisted. |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 49 | `, |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 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 | } |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 59 | return c |
| 60 | } |
| 61 | |
| 62 | func TestIndex(t *testing.T) { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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 | } |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 79 | } |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 80 | } |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 81 | |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 82 | func TestIndexWriteRead(t *testing.T) { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 83 | type key struct { |
| 84 | docs, goCode, fullText bool |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 85 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 86 | type val struct { |
| 87 | buf *bytes.Buffer |
| 88 | c *Corpus |
| 89 | } |
| 90 | m := map[key]val{} |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 91 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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 | } |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 122 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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 | } |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 134 | } |
Brad Fitzpatrick | 515bcdc | 2013-11-12 14:58:47 -0800 | [diff] [blame] | 135 | } |
| 136 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 137 | func testIndex(t *testing.T, c *Corpus, ix *Index) { |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 138 | if _, ok := ix.words["Skip"]; ok { |
| 139 | t.Errorf("the word Skip was found; expected it to be skipped") |
| 140 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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 | } |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 147 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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) { |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 180 | t.Errorf("ImportCount = %v; want %v", got, want) |
| 181 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 182 | } |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 183 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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{ |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 190 | "foo": { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 191 | "foo": true, |
| 192 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 193 | "bar": { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 194 | "bar": true, |
| 195 | "other/bar": true, |
| 196 | }, |
| 197 | } |
| 198 | } |
| 199 | if got := ix.PackagePath(); !reflect.DeepEqual(got, want) { |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 200 | t.Errorf("PackagePath = %v; want %v", got, want) |
| 201 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 202 | } |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 203 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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{ |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 210 | "foo": { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 211 | "Pi": ConstDecl, |
| 212 | "Foos": VarDecl, |
| 213 | "Foo": TypeDecl, |
| 214 | "New": FuncDecl, |
| 215 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 216 | "other/bar": { |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 217 | "X": FuncDecl, |
| 218 | }, |
| 219 | } |
| 220 | } |
| 221 | if got := ix.Exports(); !reflect.DeepEqual(got, want) { |
Brad Fitzpatrick | 56a1b4d | 2013-11-06 15:00:26 -0500 | [diff] [blame] | 222 | t.Errorf("Exports = %v; want %v", got, want) |
| 223 | } |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 224 | } |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 225 | |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 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{ |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 232 | PackageClause: { |
| 233 | "bar": { |
Brad Garcia | 936715c | 2014-06-27 10:25:57 -0400 | [diff] [blame] | 234 | {"bar", "bar", "bar", "Package bar is another example to test races."}, |
| 235 | {"other/bar", "bar", "bar", "Package bar is another bar package."}, |
Brad Garcia | 26542a8 | 2014-01-06 14:53:03 -0500 | [diff] [blame] | 236 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 237 | "foo": {{"foo", "foo", "foo", "Package foo is an example."}}, |
| 238 | "other": {{"other/bar", "bar", "bar", "Package bar is another bar package."}}, |
Brad Garcia | 26542a8 | 2014-01-06 14:53:03 -0500 | [diff] [blame] | 239 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 240 | ConstDecl: { |
| 241 | "Pi": {{"foo", "foo", "Pi", ""}}, |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 242 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 243 | VarDecl: { |
| 244 | "Foos": {{"foo", "foo", "Foos", ""}}, |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 245 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 246 | TypeDecl: { |
| 247 | "Foo": {{"foo", "foo", "Foo", "Foo is stuff."}}, |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 248 | }, |
David R. Jenni | 5984771 | 2016-11-04 19:52:17 +0100 | [diff] [blame] | 249 | FuncDecl: { |
| 250 | "New": {{"foo", "foo", "New", ""}}, |
| 251 | "X": {{"other/bar", "bar", "X", ""}}, |
Brad Garcia | 8880e2e | 2013-11-25 14:23:36 -0500 | [diff] [blame] | 252 | }, |
| 253 | } |
| 254 | } |
| 255 | if got := ix.Idents(); !reflect.DeepEqual(got, want) { |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 256 | t.Errorf("Idents = %v; want %v", got, want) |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | func TestIdentResultSort(t *testing.T) { |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 261 | ic := map[string]int{ |
| 262 | "/a/b/pkg1": 10, |
| 263 | "/a/b/pkg2": 2, |
| 264 | "/b/d/pkg3": 20, |
| 265 | } |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 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{ |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 277 | {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 278 | {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| 279 | {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 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", ""}, |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 290 | }, |
| 291 | }, |
| 292 | } { |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 293 | if sort.Sort(byImportCount{tc.ir, ic}); !reflect.DeepEqual(tc.ir, tc.exp) { |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 294 | t.Errorf("got: %v, want %v", tc.ir, tc.exp) |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 299 | func TestIdentFilter(t *testing.T) { |
| 300 | ic := map[string]int{} |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 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 | } { |
Brad Garcia | 345b643 | 2014-06-27 12:47:39 -0400 | [diff] [blame] | 318 | res := byImportCount{tc.ir, ic}.filter(tc.pak) |
| 319 | if !reflect.DeepEqual(res, tc.exp) { |
Brad Garcia | f3faf8b | 2013-11-21 11:55:42 -0500 | [diff] [blame] | 320 | t.Errorf("got: %v, want %v", res, tc.exp) |
| 321 | } |
| 322 | } |
Brad Fitzpatrick | 964f0f5 | 2013-11-05 09:35:58 -0500 | [diff] [blame] | 323 | } |