| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package godoc |
| |
| import ( |
| "bytes" |
| "reflect" |
| "sort" |
| "strings" |
| "testing" |
| |
| "golang.org/x/tools/godoc/vfs/mapfs" |
| ) |
| |
| func newCorpus(t *testing.T) *Corpus { |
| c := NewCorpus(mapfs.New(map[string]string{ |
| "src/foo/foo.go": `// Package foo is an example. |
| package foo |
| |
| import "bar" |
| |
| const Pi = 3.1415 |
| |
| var Foos []Foo |
| |
| // Foo is stuff. |
| type Foo struct{} |
| |
| func New() *Foo { |
| return new(Foo) |
| } |
| `, |
| "src/bar/bar.go": `// Package bar is another example to test races. |
| package bar |
| `, |
| "src/other/bar/bar.go": `// Package bar is another bar package. |
| package bar |
| func X() {} |
| `, |
| "src/skip/skip.go": `// Package skip should be skipped. |
| package skip |
| func Skip() {} |
| `, |
| "src/bar/readme.txt": `Whitelisted text file. |
| `, |
| "src/bar/baz.zzz": `Text file not whitelisted. |
| `, |
| })) |
| c.IndexEnabled = true |
| c.IndexDirectory = func(dir string) bool { |
| return !strings.Contains(dir, "skip") |
| } |
| |
| if err := c.Init(); err != nil { |
| t.Fatal(err) |
| } |
| return c |
| } |
| |
| func TestIndex(t *testing.T) { |
| for _, docs := range []bool{true, false} { |
| for _, goCode := range []bool{true, false} { |
| for _, fullText := range []bool{true, false} { |
| c := newCorpus(t) |
| c.IndexDocs = docs |
| c.IndexGoCode = goCode |
| c.IndexFullText = fullText |
| c.UpdateIndex() |
| ix, _ := c.CurrentIndex() |
| if ix == nil { |
| t.Fatal("no index") |
| } |
| t.Logf("docs, goCode, fullText = %v,%v,%v", docs, goCode, fullText) |
| testIndex(t, c, ix) |
| } |
| } |
| } |
| } |
| |
| func TestIndexWriteRead(t *testing.T) { |
| type key struct { |
| docs, goCode, fullText bool |
| } |
| type val struct { |
| buf *bytes.Buffer |
| c *Corpus |
| } |
| m := map[key]val{} |
| |
| for _, docs := range []bool{true, false} { |
| for _, goCode := range []bool{true, false} { |
| for _, fullText := range []bool{true, false} { |
| k := key{docs, goCode, fullText} |
| c := newCorpus(t) |
| c.IndexDocs = docs |
| c.IndexGoCode = goCode |
| c.IndexFullText = fullText |
| c.UpdateIndex() |
| ix, _ := c.CurrentIndex() |
| if ix == nil { |
| t.Fatal("no index") |
| } |
| var buf bytes.Buffer |
| nw, err := ix.WriteTo(&buf) |
| if err != nil { |
| t.Fatalf("Index.WriteTo: %v", err) |
| } |
| m[k] = val{bytes.NewBuffer(buf.Bytes()), c} |
| ix2 := new(Index) |
| nr, err := ix2.ReadFrom(&buf) |
| if err != nil { |
| t.Fatalf("Index.ReadFrom: %v", err) |
| } |
| if nr != nw { |
| t.Errorf("Wrote %d bytes to index but read %d", nw, nr) |
| } |
| testIndex(t, c, ix) |
| } |
| } |
| } |
| // Test CompatibleWith |
| for k1, v1 := range m { |
| ix := new(Index) |
| if _, err := ix.ReadFrom(v1.buf); err != nil { |
| t.Fatalf("Index.ReadFrom: %v", err) |
| } |
| for k2, v2 := range m { |
| if got, want := ix.CompatibleWith(v2.c), k1 == k2; got != want { |
| t.Errorf("CompatibleWith = %v; want %v for %v, %v", got, want, k1, k2) |
| } |
| } |
| } |
| } |
| |
| func testIndex(t *testing.T, c *Corpus, ix *Index) { |
| if _, ok := ix.words["Skip"]; ok { |
| t.Errorf("the word Skip was found; expected it to be skipped") |
| } |
| checkStats(t, c, ix) |
| checkImportCount(t, c, ix) |
| checkPackagePath(t, c, ix) |
| checkExports(t, c, ix) |
| checkIdents(t, c, ix) |
| } |
| |
| // checkStats checks the Index's statistics. |
| // Some statistics are only set when we're indexing Go code. |
| func checkStats(t *testing.T, c *Corpus, ix *Index) { |
| want := Statistics{} |
| if c.IndexFullText { |
| want.Bytes = 314 |
| want.Files = 4 |
| want.Lines = 21 |
| } else if c.IndexDocs || c.IndexGoCode { |
| want.Bytes = 291 |
| want.Files = 3 |
| want.Lines = 20 |
| } |
| if c.IndexGoCode { |
| want.Words = 8 |
| want.Spots = 12 |
| } |
| if got := ix.Stats(); !reflect.DeepEqual(got, want) { |
| t.Errorf("Stats = %#v; want %#v", got, want) |
| } |
| } |
| |
| // checkImportCount checks the Index's import count map. |
| // It is only set when we're indexing Go code. |
| func checkImportCount(t *testing.T, c *Corpus, ix *Index) { |
| want := map[string]int{} |
| if c.IndexGoCode { |
| want = map[string]int{ |
| "bar": 1, |
| } |
| } |
| if got := ix.ImportCount(); !reflect.DeepEqual(got, want) { |
| t.Errorf("ImportCount = %v; want %v", got, want) |
| } |
| } |
| |
| // checkPackagePath checks the Index's package path map. |
| // It is set if at least one of the indexing options is enabled. |
| func checkPackagePath(t *testing.T, c *Corpus, ix *Index) { |
| want := map[string]map[string]bool{} |
| if c.IndexDocs || c.IndexGoCode || c.IndexFullText { |
| want = map[string]map[string]bool{ |
| "foo": { |
| "foo": true, |
| }, |
| "bar": { |
| "bar": true, |
| "other/bar": true, |
| }, |
| } |
| } |
| if got := ix.PackagePath(); !reflect.DeepEqual(got, want) { |
| t.Errorf("PackagePath = %v; want %v", got, want) |
| } |
| } |
| |
| // checkExports checks the Index's exports map. |
| // It is only set when we're indexing Go code. |
| func checkExports(t *testing.T, c *Corpus, ix *Index) { |
| want := map[string]map[string]SpotKind{} |
| if c.IndexGoCode { |
| want = map[string]map[string]SpotKind{ |
| "foo": { |
| "Pi": ConstDecl, |
| "Foos": VarDecl, |
| "Foo": TypeDecl, |
| "New": FuncDecl, |
| }, |
| "other/bar": { |
| "X": FuncDecl, |
| }, |
| } |
| } |
| if got := ix.Exports(); !reflect.DeepEqual(got, want) { |
| t.Errorf("Exports = %v; want %v", got, want) |
| } |
| } |
| |
| // checkIdents checks the Index's indents map. |
| // It is only set when we're indexing documentation. |
| func checkIdents(t *testing.T, c *Corpus, ix *Index) { |
| want := map[SpotKind]map[string][]Ident{} |
| if c.IndexDocs { |
| want = map[SpotKind]map[string][]Ident{ |
| PackageClause: { |
| "bar": { |
| {"bar", "bar", "bar", "Package bar is another example to test races."}, |
| {"other/bar", "bar", "bar", "Package bar is another bar package."}, |
| }, |
| "foo": {{"foo", "foo", "foo", "Package foo is an example."}}, |
| "other": {{"other/bar", "bar", "bar", "Package bar is another bar package."}}, |
| }, |
| ConstDecl: { |
| "Pi": {{"foo", "foo", "Pi", ""}}, |
| }, |
| VarDecl: { |
| "Foos": {{"foo", "foo", "Foos", ""}}, |
| }, |
| TypeDecl: { |
| "Foo": {{"foo", "foo", "Foo", "Foo is stuff."}}, |
| }, |
| FuncDecl: { |
| "New": {{"foo", "foo", "New", ""}}, |
| "X": {{"other/bar", "bar", "X", ""}}, |
| }, |
| } |
| } |
| if got := ix.Idents(); !reflect.DeepEqual(got, want) { |
| t.Errorf("Idents = %v; want %v", got, want) |
| } |
| } |
| |
| func TestIdentResultSort(t *testing.T) { |
| ic := map[string]int{ |
| "/a/b/pkg1": 10, |
| "/a/b/pkg2": 2, |
| "/b/d/pkg3": 20, |
| } |
| for _, tc := range []struct { |
| ir []Ident |
| exp []Ident |
| }{ |
| { |
| ir: []Ident{ |
| {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
| {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
| {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| }, |
| exp: []Ident{ |
| {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
| {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
| }, |
| }, |
| { |
| ir: []Ident{ |
| {"/a/a/pkg1", "pkg1", "MyFunc1", ""}, |
| {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| }, |
| exp: []Ident{ |
| {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| {"/a/a/pkg1", "pkg1", "MyFunc1", ""}, |
| }, |
| }, |
| } { |
| if sort.Sort(byImportCount{tc.ir, ic}); !reflect.DeepEqual(tc.ir, tc.exp) { |
| t.Errorf("got: %v, want %v", tc.ir, tc.exp) |
| } |
| } |
| } |
| |
| func TestIdentFilter(t *testing.T) { |
| ic := map[string]int{} |
| for _, tc := range []struct { |
| ir []Ident |
| pak string |
| exp []Ident |
| }{ |
| { |
| ir: []Ident{ |
| {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
| {"/b/d/pkg3", "pkg3", "MyFunc3", ""}, |
| {"/a/b/pkg1", "pkg1", "MyFunc1", ""}, |
| }, |
| pak: "pkg2", |
| exp: []Ident{ |
| {"/a/b/pkg2", "pkg2", "MyFunc2", ""}, |
| }, |
| }, |
| } { |
| res := byImportCount{tc.ir, ic}.filter(tc.pak) |
| if !reflect.DeepEqual(res, tc.exp) { |
| t.Errorf("got: %v, want %v", res, tc.exp) |
| } |
| } |
| } |