| // 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) | 
 | 		} | 
 | 	} | 
 | } |