371 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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"
 | 
						|
	"go/parser"
 | 
						|
	"go/token"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
)
 | 
						|
 | 
						|
func TestPkgLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		path string
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{"/src/fmt", "pkg/fmt"},
 | 
						|
		{"src/fmt", "pkg/fmt"},
 | 
						|
		{"/fmt", "pkg/fmt"},
 | 
						|
		{"fmt", "pkg/fmt"},
 | 
						|
	} {
 | 
						|
		if got := pkgLinkFunc(tc.path); got != tc.want {
 | 
						|
			t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSrcPosLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		src  string
 | 
						|
		line int
 | 
						|
		low  int
 | 
						|
		high int
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"},
 | 
						|
		{"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
 | 
						|
		{"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"},
 | 
						|
		{"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
 | 
						|
		{"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
 | 
						|
		{"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
 | 
						|
		{"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
 | 
						|
	} {
 | 
						|
		if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want {
 | 
						|
			t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSrcLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		src  string
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{"/src/fmt/print.go", "/src/fmt/print.go"},
 | 
						|
		{"src/fmt/print.go", "/src/fmt/print.go"},
 | 
						|
		{"/fmt/print.go", "/src/fmt/print.go"},
 | 
						|
		{"fmt/print.go", "/src/fmt/print.go"},
 | 
						|
	} {
 | 
						|
		if got := srcLinkFunc(tc.src); got != tc.want {
 | 
						|
			t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestQueryLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		src   string
 | 
						|
		query string
 | 
						|
		line  int
 | 
						|
		want  string
 | 
						|
	}{
 | 
						|
		{"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"},
 | 
						|
		{"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"},
 | 
						|
		{"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"},
 | 
						|
		{"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"},
 | 
						|
	} {
 | 
						|
		if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want {
 | 
						|
			t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestDocLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		src   string
 | 
						|
		ident string
 | 
						|
		want  string
 | 
						|
	}{
 | 
						|
		{"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
 | 
						|
		{"fmt", "EOF", "/pkg/fmt/#EOF"},
 | 
						|
	} {
 | 
						|
		if got := docLinkFunc(tc.src, tc.ident); got != tc.want {
 | 
						|
			t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSanitizeFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		src  string
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{},
 | 
						|
		{"foo", "foo"},
 | 
						|
		{"func   f()", "func f()"},
 | 
						|
		{"func f(a int,)", "func f(a int)"},
 | 
						|
		{"func f(a int,\n)", "func f(a int)"},
 | 
						|
		{"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"},
 | 
						|
		{"  (   a,   b,  c  )  ", "(a, b, c)"},
 | 
						|
		{"(  a,  b, c    int, foo   bar  ,  )", "(a, b, c int, foo bar)"},
 | 
						|
		{"{   a,   b}", "{a, b}"},
 | 
						|
		{"[   a,   b]", "[a, b]"},
 | 
						|
	} {
 | 
						|
		if got := sanitizeFunc(tc.src); got != tc.want {
 | 
						|
			t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Test that we add <span id="StructName.FieldName"> elements
 | 
						|
// to the HTML of struct fields.
 | 
						|
func TestStructFieldsIDAttributes(t *testing.T) {
 | 
						|
	got := linkifySource(t, []byte(`
 | 
						|
package foo
 | 
						|
 | 
						|
type T struct {
 | 
						|
	NoDoc string
 | 
						|
 | 
						|
	// Doc has a comment.
 | 
						|
	Doc string
 | 
						|
 | 
						|
	// Opt, if non-nil, is an option.
 | 
						|
	Opt *int
 | 
						|
 | 
						|
	// Опция - другое поле.
 | 
						|
	Опция bool
 | 
						|
}
 | 
						|
`))
 | 
						|
	want := `type T struct {
 | 
						|
<span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
 | 
						|
 | 
						|
<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
 | 
						|
Doc <a href="/pkg/builtin/#string">string</a>
 | 
						|
 | 
						|
<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
 | 
						|
Opt *<a href="/pkg/builtin/#int">int</a>
 | 
						|
 | 
						|
<span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
 | 
						|
Опция <a href="/pkg/builtin/#bool">bool</a>
 | 
						|
}`
 | 
						|
	if got != want {
 | 
						|
		t.Errorf("got: %s\n\nwant: %s\n", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Test that we add <span id="ConstName"> elements to the HTML
 | 
						|
// of definitions in const and var specs.
 | 
						|
func TestValueSpecIDAttributes(t *testing.T) {
 | 
						|
	got := linkifySource(t, []byte(`
 | 
						|
package foo
 | 
						|
 | 
						|
const (
 | 
						|
	NoDoc string = "NoDoc"
 | 
						|
 | 
						|
	// Doc has a comment
 | 
						|
	Doc = "Doc"
 | 
						|
 | 
						|
	NoVal
 | 
						|
)`))
 | 
						|
	want := `const (
 | 
						|
<span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = "NoDoc"
 | 
						|
 | 
						|
<span class="comment">// Doc has a comment</span>
 | 
						|
<span id="Doc">Doc</span> = "Doc"
 | 
						|
 | 
						|
<span id="NoVal">NoVal</span>
 | 
						|
)`
 | 
						|
	if got != want {
 | 
						|
		t.Errorf("got: %s\n\nwant: %s\n", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCompositeLitLinkFields(t *testing.T) {
 | 
						|
	got := linkifySource(t, []byte(`
 | 
						|
package foo
 | 
						|
 | 
						|
type T struct {
 | 
						|
	X int
 | 
						|
}
 | 
						|
 | 
						|
var S T = T{X: 12}`))
 | 
						|
	want := `type T struct {
 | 
						|
<span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a>
 | 
						|
}
 | 
						|
var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}`
 | 
						|
	if got != want {
 | 
						|
		t.Errorf("got: %s\n\nwant: %s\n", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestFuncDeclNotLink(t *testing.T) {
 | 
						|
	// Function.
 | 
						|
	got := linkifySource(t, []byte(`
 | 
						|
package http
 | 
						|
 | 
						|
func Get(url string) (resp *Response, err error)`))
 | 
						|
	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>)`
 | 
						|
	if got != want {
 | 
						|
		t.Errorf("got: %s\n\nwant: %s\n", got, want)
 | 
						|
	}
 | 
						|
 | 
						|
	// Method.
 | 
						|
	got = linkifySource(t, []byte(`
 | 
						|
package http
 | 
						|
 | 
						|
func (h Header) Get(key string) string`))
 | 
						|
	want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>`
 | 
						|
	if got != want {
 | 
						|
		t.Errorf("got: %s\n\nwant: %s\n", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func linkifySource(t *testing.T, src []byte) string {
 | 
						|
	p := &Presentation{
 | 
						|
		DeclLinks: true,
 | 
						|
	}
 | 
						|
	fset := token.NewFileSet()
 | 
						|
	af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	var buf bytes.Buffer
 | 
						|
	pi := &PageInfo{
 | 
						|
		FSet: fset,
 | 
						|
	}
 | 
						|
	sep := ""
 | 
						|
	for _, decl := range af.Decls {
 | 
						|
		buf.WriteString(sep)
 | 
						|
		sep = "\n"
 | 
						|
		buf.WriteString(p.node_htmlFunc(pi, decl, true))
 | 
						|
	}
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func TestScanIdentifier(t *testing.T) {
 | 
						|
	tests := []struct {
 | 
						|
		in, want string
 | 
						|
	}{
 | 
						|
		{"foo bar", "foo"},
 | 
						|
		{"foo/bar", "foo"},
 | 
						|
		{" foo", ""},
 | 
						|
		{"фоо", "фоо"},
 | 
						|
		{"f123", "f123"},
 | 
						|
		{"123f", ""},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		got := scanIdentifier([]byte(tt.in))
 | 
						|
		if string(got) != tt.want {
 | 
						|
			t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestReplaceLeadingIndentation(t *testing.T) {
 | 
						|
	oldIndent := strings.Repeat(" ", 2)
 | 
						|
	newIndent := strings.Repeat(" ", 4)
 | 
						|
	tests := []struct {
 | 
						|
		src, want string
 | 
						|
	}{
 | 
						|
		{"  foo\n    bar\n  baz", "    foo\n      bar\n    baz"},
 | 
						|
		{"  '`'\n  '`'\n", "    '`'\n    '`'\n"},
 | 
						|
		{"  '\\''\n  '`'\n", "    '\\''\n    '`'\n"},
 | 
						|
		{"  \"`\"\n  \"`\"\n", "    \"`\"\n    \"`\"\n"},
 | 
						|
		{"  `foo\n  bar`", "    `foo\n      bar`"},
 | 
						|
		{"  `foo\\`\n  bar", "    `foo\\`\n    bar"},
 | 
						|
		{"  '\\`'`foo\n  bar", "    '\\`'`foo\n      bar"},
 | 
						|
		{
 | 
						|
			"  if true {\n    foo := `One\n    \tTwo\nThree`\n  }\n",
 | 
						|
			"    if true {\n      foo := `One\n        \tTwo\n    Three`\n    }\n",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tc := range tests {
 | 
						|
		if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want {
 | 
						|
			t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n",
 | 
						|
				tc.src, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSrcBreadcrumbFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		path string
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{"src/", `<span class="text-muted">src/</span>`},
 | 
						|
		{"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
 | 
						|
		{"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
 | 
						|
	} {
 | 
						|
		if got := srcBreadcrumbFunc(tc.path); got != tc.want {
 | 
						|
			t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSrcToPkgLinkFunc(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		path string
 | 
						|
		want string
 | 
						|
	}{
 | 
						|
		{"src/", `<a href="/pkg">Index</a>`},
 | 
						|
		{"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
 | 
						|
		{"pkg/", `<a href="/pkg">Index</a>`},
 | 
						|
		{"pkg/LICENSE", `<a href="/pkg">Index</a>`},
 | 
						|
	} {
 | 
						|
		if got := srcToPkgLinkFunc(tc.path); got != tc.want {
 | 
						|
			t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestFilterOutBuildAnnotations(t *testing.T) {
 | 
						|
	// TODO: simplify this by using a multiline string once we stop
 | 
						|
	// using go vet from 1.10 on the build dashboard.
 | 
						|
	// https://golang.org/issue/26627
 | 
						|
	src := []byte("// +build !foo\n" +
 | 
						|
		"// +build !anothertag\n" +
 | 
						|
		"\n" +
 | 
						|
		"// non-tag comment\n" +
 | 
						|
		"\n" +
 | 
						|
		"package foo\n" +
 | 
						|
		"\n" +
 | 
						|
		"func bar() int {\n" +
 | 
						|
		"	return 42\n" +
 | 
						|
		"}\n")
 | 
						|
 | 
						|
	fset := token.NewFileSet()
 | 
						|
	af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	var found bool
 | 
						|
	for _, cg := range af.Comments {
 | 
						|
		if strings.HasPrefix(cg.Text(), "+build ") {
 | 
						|
			found = true
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if !found {
 | 
						|
		t.Errorf("TestFilterOutBuildAnnotations is broken: missing build tag in test input")
 | 
						|
	}
 | 
						|
 | 
						|
	found = false
 | 
						|
	for _, cg := range filterOutBuildAnnotations(af.Comments) {
 | 
						|
		if strings.HasPrefix(cg.Text(), "+build ") {
 | 
						|
			t.Errorf("filterOutBuildAnnotations failed to filter build tag")
 | 
						|
		}
 | 
						|
 | 
						|
		if strings.Contains(cg.Text(), "non-tag comment") {
 | 
						|
			found = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if !found {
 | 
						|
		t.Errorf("filterOutBuildAnnotations should not remove non-build tag comment")
 | 
						|
	}
 | 
						|
}
 |