github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/godoc/godoc_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  	"go/parser"
    10  	"go/token"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/powerman/golang-tools/internal/typeparams"
    15  )
    16  
    17  func TestPkgLinkFunc(t *testing.T) {
    18  	for _, tc := range []struct {
    19  		path string
    20  		want string
    21  	}{
    22  		{"/src/fmt", "pkg/fmt"},
    23  		{"src/fmt", "pkg/fmt"},
    24  		{"/fmt", "pkg/fmt"},
    25  		{"fmt", "pkg/fmt"},
    26  	} {
    27  		if got := pkgLinkFunc(tc.path); got != tc.want {
    28  			t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
    29  		}
    30  	}
    31  }
    32  
    33  func TestSrcPosLinkFunc(t *testing.T) {
    34  	for _, tc := range []struct {
    35  		src  string
    36  		line int
    37  		low  int
    38  		high int
    39  		want string
    40  	}{
    41  		{"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"},
    42  		{"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
    43  		{"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"},
    44  		{"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
    45  		{"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
    46  		{"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
    47  		{"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
    48  	} {
    49  		if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want {
    50  			t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, tc.want)
    51  		}
    52  	}
    53  }
    54  
    55  func TestSrcLinkFunc(t *testing.T) {
    56  	for _, tc := range []struct {
    57  		src  string
    58  		want string
    59  	}{
    60  		{"/src/fmt/print.go", "/src/fmt/print.go"},
    61  		{"src/fmt/print.go", "/src/fmt/print.go"},
    62  		{"/fmt/print.go", "/src/fmt/print.go"},
    63  		{"fmt/print.go", "/src/fmt/print.go"},
    64  	} {
    65  		if got := srcLinkFunc(tc.src); got != tc.want {
    66  			t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
    67  		}
    68  	}
    69  }
    70  
    71  func TestQueryLinkFunc(t *testing.T) {
    72  	for _, tc := range []struct {
    73  		src   string
    74  		query string
    75  		line  int
    76  		want  string
    77  	}{
    78  		{"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"},
    79  		{"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"},
    80  		{"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"},
    81  		{"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"},
    82  	} {
    83  		if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want {
    84  			t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, tc.want)
    85  		}
    86  	}
    87  }
    88  
    89  func TestDocLinkFunc(t *testing.T) {
    90  	for _, tc := range []struct {
    91  		src   string
    92  		ident string
    93  		want  string
    94  	}{
    95  		{"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
    96  		{"fmt", "EOF", "/pkg/fmt/#EOF"},
    97  	} {
    98  		if got := docLinkFunc(tc.src, tc.ident); got != tc.want {
    99  			t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want)
   100  		}
   101  	}
   102  }
   103  
   104  func TestSanitizeFunc(t *testing.T) {
   105  	for _, tc := range []struct {
   106  		src  string
   107  		want string
   108  	}{
   109  		{},
   110  		{"foo", "foo"},
   111  		{"func   f()", "func f()"},
   112  		{"func f(a int,)", "func f(a int)"},
   113  		{"func f(a int,\n)", "func f(a int)"},
   114  		{"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"},
   115  		{"  (   a,   b,  c  )  ", "(a, b, c)"},
   116  		{"(  a,  b, c    int, foo   bar  ,  )", "(a, b, c int, foo bar)"},
   117  		{"{   a,   b}", "{a, b}"},
   118  		{"[   a,   b]", "[a, b]"},
   119  	} {
   120  		if got := sanitizeFunc(tc.src); got != tc.want {
   121  			t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want)
   122  		}
   123  	}
   124  }
   125  
   126  // Test that we add <span id="StructName.FieldName"> elements
   127  // to the HTML of struct fields.
   128  func TestStructFieldsIDAttributes(t *testing.T) {
   129  	got := linkifySource(t, []byte(`
   130  package foo
   131  
   132  type T struct {
   133  	NoDoc string
   134  
   135  	// Doc has a comment.
   136  	Doc string
   137  
   138  	// Opt, if non-nil, is an option.
   139  	Opt *int
   140  
   141  	// Опция - другое поле.
   142  	Опция bool
   143  }
   144  `))
   145  	want := `type T struct {
   146  <span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
   147  
   148  <span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
   149  Doc <a href="/pkg/builtin/#string">string</a>
   150  
   151  <span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
   152  Opt *<a href="/pkg/builtin/#int">int</a>
   153  
   154  <span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
   155  Опция <a href="/pkg/builtin/#bool">bool</a>
   156  }`
   157  	if got != want {
   158  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   159  	}
   160  }
   161  
   162  // Test that we add <span id="ConstName"> elements to the HTML
   163  // of definitions in const and var specs.
   164  func TestValueSpecIDAttributes(t *testing.T) {
   165  	got := linkifySource(t, []byte(`
   166  package foo
   167  
   168  const (
   169  	NoDoc string = "NoDoc"
   170  
   171  	// Doc has a comment
   172  	Doc = "Doc"
   173  
   174  	NoVal
   175  )`))
   176  	want := `const (
   177  <span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = &#34;NoDoc&#34;
   178  
   179  <span class="comment">// Doc has a comment</span>
   180  <span id="Doc">Doc</span> = &#34;Doc&#34;
   181  
   182  <span id="NoVal">NoVal</span>
   183  )`
   184  	if got != want {
   185  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   186  	}
   187  }
   188  
   189  func TestCompositeLitLinkFields(t *testing.T) {
   190  	got := linkifySource(t, []byte(`
   191  package foo
   192  
   193  type T struct {
   194  	X int
   195  }
   196  
   197  var S T = T{X: 12}`))
   198  	want := `type T struct {
   199  <span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a>
   200  }
   201  var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}`
   202  	if got != want {
   203  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   204  	}
   205  }
   206  
   207  func TestFuncDeclNotLink(t *testing.T) {
   208  	// Function.
   209  	got := linkifySource(t, []byte(`
   210  package http
   211  
   212  func Get(url string) (resp *Response, err error)`))
   213  	want := `func Get(url <a href="/pkg/builtin/#string">string</a>) (resp *<a href="#Response">Response</a>, err <a href="/pkg/builtin/#error">error</a>)`
   214  	if got != want {
   215  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   216  	}
   217  
   218  	// Method.
   219  	got = linkifySource(t, []byte(`
   220  package http
   221  
   222  func (h Header) Get(key string) string`))
   223  	want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>`
   224  	if got != want {
   225  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   226  	}
   227  }
   228  
   229  func linkifySource(t *testing.T, src []byte) string {
   230  	p := &Presentation{
   231  		DeclLinks: true,
   232  	}
   233  	fset := token.NewFileSet()
   234  	af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  	var buf bytes.Buffer
   239  	pi := &PageInfo{
   240  		FSet: fset,
   241  	}
   242  	sep := ""
   243  	for _, decl := range af.Decls {
   244  		buf.WriteString(sep)
   245  		sep = "\n"
   246  		buf.WriteString(p.node_htmlFunc(pi, decl, true))
   247  	}
   248  	return buf.String()
   249  }
   250  
   251  func TestScanIdentifier(t *testing.T) {
   252  	tests := []struct {
   253  		in, want string
   254  	}{
   255  		{"foo bar", "foo"},
   256  		{"foo/bar", "foo"},
   257  		{" foo", ""},
   258  		{"фоо", "фоо"},
   259  		{"f123", "f123"},
   260  		{"123f", ""},
   261  	}
   262  	for _, tt := range tests {
   263  		got := scanIdentifier([]byte(tt.in))
   264  		if string(got) != tt.want {
   265  			t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
   266  		}
   267  	}
   268  }
   269  
   270  func TestReplaceLeadingIndentation(t *testing.T) {
   271  	oldIndent := strings.Repeat(" ", 2)
   272  	newIndent := strings.Repeat(" ", 4)
   273  	tests := []struct {
   274  		src, want string
   275  	}{
   276  		{"  foo\n    bar\n  baz", "    foo\n      bar\n    baz"},
   277  		{"  '`'\n  '`'\n", "    '`'\n    '`'\n"},
   278  		{"  '\\''\n  '`'\n", "    '\\''\n    '`'\n"},
   279  		{"  \"`\"\n  \"`\"\n", "    \"`\"\n    \"`\"\n"},
   280  		{"  `foo\n  bar`", "    `foo\n      bar`"},
   281  		{"  `foo\\`\n  bar", "    `foo\\`\n    bar"},
   282  		{"  '\\`'`foo\n  bar", "    '\\`'`foo\n      bar"},
   283  		{
   284  			"  if true {\n    foo := `One\n    \tTwo\nThree`\n  }\n",
   285  			"    if true {\n      foo := `One\n        \tTwo\n    Three`\n    }\n",
   286  		},
   287  	}
   288  	for _, tc := range tests {
   289  		if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want {
   290  			t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n",
   291  				tc.src, got, tc.want)
   292  		}
   293  	}
   294  }
   295  
   296  func TestSrcBreadcrumbFunc(t *testing.T) {
   297  	for _, tc := range []struct {
   298  		path string
   299  		want string
   300  	}{
   301  		{"src/", `<span class="text-muted">src/</span>`},
   302  		{"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
   303  		{"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
   304  	} {
   305  		if got := srcBreadcrumbFunc(tc.path); got != tc.want {
   306  			t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
   307  		}
   308  	}
   309  }
   310  
   311  func TestSrcToPkgLinkFunc(t *testing.T) {
   312  	for _, tc := range []struct {
   313  		path string
   314  		want string
   315  	}{
   316  		{"src/", `<a href="/pkg">Index</a>`},
   317  		{"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
   318  		{"pkg/", `<a href="/pkg">Index</a>`},
   319  		{"pkg/LICENSE", `<a href="/pkg">Index</a>`},
   320  	} {
   321  		if got := srcToPkgLinkFunc(tc.path); got != tc.want {
   322  			t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
   323  		}
   324  	}
   325  }
   326  
   327  func TestFilterOutBuildAnnotations(t *testing.T) {
   328  	// TODO: simplify this by using a multiline string once we stop
   329  	// using go vet from 1.10 on the build dashboard.
   330  	// https://golang.org/issue/26627
   331  	src := []byte("// +build !foo\n" +
   332  		"// +build !anothertag\n" +
   333  		"\n" +
   334  		"// non-tag comment\n" +
   335  		"\n" +
   336  		"package foo\n" +
   337  		"\n" +
   338  		"func bar() int {\n" +
   339  		"	return 42\n" +
   340  		"}\n")
   341  
   342  	fset := token.NewFileSet()
   343  	af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
   344  	if err != nil {
   345  		t.Fatal(err)
   346  	}
   347  
   348  	var found bool
   349  	for _, cg := range af.Comments {
   350  		if strings.HasPrefix(cg.Text(), "+build ") {
   351  			found = true
   352  			break
   353  		}
   354  	}
   355  	if !found {
   356  		t.Errorf("TestFilterOutBuildAnnotations is broken: missing build tag in test input")
   357  	}
   358  
   359  	found = false
   360  	for _, cg := range filterOutBuildAnnotations(af.Comments) {
   361  		if strings.HasPrefix(cg.Text(), "+build ") {
   362  			t.Errorf("filterOutBuildAnnotations failed to filter build tag")
   363  		}
   364  
   365  		if strings.Contains(cg.Text(), "non-tag comment") {
   366  			found = true
   367  		}
   368  	}
   369  	if !found {
   370  		t.Errorf("filterOutBuildAnnotations should not remove non-build tag comment")
   371  	}
   372  }
   373  
   374  func TestLinkifyGenerics(t *testing.T) {
   375  	if !typeparams.Enabled {
   376  		t.Skip("type params are not enabled at this Go version")
   377  	}
   378  
   379  	got := linkifySource(t, []byte(`
   380  package foo
   381  
   382  type T struct {
   383  	field *T
   384  }
   385  
   386  type ParametricStruct[T any] struct {
   387  	field *T
   388  }
   389  
   390  func F1[T any](arg T) { }
   391  
   392  func F2(arg T) { }
   393  
   394  func (*ParametricStruct[T]) M(arg T) { }
   395  
   396  func (*T) M(arg T) { }
   397  
   398  type ParametricStruct2[T1, T2 any] struct {
   399  	a T1
   400  	b T2
   401  }
   402  
   403  func (*ParametricStruct2[T1, T2]) M(a T1, b T2) { }
   404  
   405  
   406  `))
   407  
   408  	want := `type T struct {
   409  <span id="T.field"></span>field *<a href="#T">T</a>
   410  }
   411  type ParametricStruct[T <a href="/pkg/builtin/#any">any</a>] struct {
   412  <span id="ParametricStruct.field"></span>field *T
   413  }
   414  func F1[T <a href="/pkg/builtin/#any">any</a>](arg T) {}
   415  func F2(arg <a href="#T">T</a>) {}
   416  func (*<a href="#ParametricStruct">ParametricStruct</a>[T]) M(arg T) {}
   417  func (*<a href="#T">T</a>) M(arg <a href="#T">T</a>) {}
   418  type ParametricStruct2[T1, T2 <a href="/pkg/builtin/#any">any</a>] struct {
   419  <span id="ParametricStruct2.a"></span>a T1
   420  <span id="ParametricStruct2.b"></span>b T2
   421  }
   422  func (*<a href="#ParametricStruct2">ParametricStruct2</a>[T1, T2]) M(a T1, b T2) {}`
   423  
   424  	if got != want {
   425  		t.Errorf("got: %s\n\nwant: %s\n", got, want)
   426  	}
   427  }