--- /dev/null
+// 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)
+ }
+ }
+}