golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/godoc/index_test.go (about)

     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  }