blob: 7f3470ed077a581cbe9024d5419133301a238376 [file] [log] [blame]
Brad Garciaff7cfaf2013-12-04 10:37:01 -05001// 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
5package godoc
6
7import (
Jay Conrode1bdc762017-02-10 16:20:20 -05008 "bytes"
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +00009 "go/parser"
10 "go/token"
Jay Conrod00f7cd52017-02-07 17:34:43 -050011 "strings"
Brad Garciaff7cfaf2013-12-04 10:37:01 -050012 "testing"
aarzillic6fca022022-02-01 18:45:42 +010013
14 "golang.org/x/tools/internal/typeparams"
Brad Garciaff7cfaf2013-12-04 10:37:01 -050015)
16
17func TestPkgLinkFunc(t *testing.T) {
18 for _, tc := range []struct {
19 path string
20 want string
21 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040022 {"/src/fmt", "pkg/fmt"},
23 {"src/fmt", "pkg/fmt"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050024 {"/fmt", "pkg/fmt"},
Brad Garcia936715c2014-06-27 10:25:57 -040025 {"fmt", "pkg/fmt"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050026 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070027 if got := pkgLinkFunc(tc.path); got != tc.want {
28 t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050029 }
30 }
31}
32
33func 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 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040041 {"/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"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050048 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070049 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)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050051 }
52 }
53}
54
55func TestSrcLinkFunc(t *testing.T) {
56 for _, tc := range []struct {
57 src string
58 want string
59 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040060 {"/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"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050064 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070065 if got := srcLinkFunc(tc.src); got != tc.want {
66 t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050067 }
68 }
69}
70
71func TestQueryLinkFunc(t *testing.T) {
72 for _, tc := range []struct {
73 src string
74 query string
75 line int
76 want string
77 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040078 {"/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"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050082 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070083 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)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050085 }
86 }
87}
88
89func TestDocLinkFunc(t *testing.T) {
90 for _, tc := range []struct {
91 src string
92 ident string
93 want string
94 }{
Brad Garcia936715c2014-06-27 10:25:57 -040095 {"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
96 {"fmt", "EOF", "/pkg/fmt/#EOF"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050097 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070098 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
104func 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)
Brad Garciaff7cfaf2013-12-04 10:37:01 -0500122 }
123 }
124}
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000125
126// Test that we add <span id="StructName.FieldName"> elements
127// to the HTML of struct fields.
128func TestStructFieldsIDAttributes(t *testing.T) {
Jay Conrode1bdc762017-02-10 16:20:20 -0500129 got := linkifySource(t, []byte(`
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000130package foo
131
132type T struct {
Brad Fitzpatricke5f9a3d2016-11-30 23:49:04 +0000133 NoDoc string
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000134
Brad Fitzpatricke5f9a3d2016-11-30 23:49:04 +0000135 // Doc has a comment.
136 Doc string
137
138 // Opt, if non-nil, is an option.
139 Opt *int
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000140
141 // Опция - другое поле.
142 Опция bool
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000143}
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000144`))
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>
149Doc <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>
152Opt *<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
Jay Conrod3bcb6ef2017-04-11 15:25:53 -0400162// Test that we add <span id="ConstName"> elements to the HTML
163// of definitions in const and var specs.
164func TestValueSpecIDAttributes(t *testing.T) {
165 got := linkifySource(t, []byte(`
166package foo
167
168const (
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
Jay Conrode1bdc762017-02-10 16:20:20 -0500189func TestCompositeLitLinkFields(t *testing.T) {
190 got := linkifySource(t, []byte(`
191package foo
192
193type T struct {
194 X int
195}
196
197var 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}
Jay Conrod3bcb6ef2017-04-11 15:25:53 -0400201var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}`
Jay Conrode1bdc762017-02-10 16:20:20 -0500202 if got != want {
203 t.Errorf("got: %s\n\nwant: %s\n", got, want)
204 }
205}
206
Masahiro Furudatece129152017-05-07 04:40:42 +0900207func TestFuncDeclNotLink(t *testing.T) {
208 // Function.
209 got := linkifySource(t, []byte(`
210package http
211
212func 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(`
220package http
221
222func (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
Jay Conrode1bdc762017-02-10 16:20:20 -0500229func linkifySource(t *testing.T, src []byte) string {
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000230 p := &Presentation{
231 DeclLinks: true,
232 }
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000233 fset := token.NewFileSet()
234 af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
235 if err != nil {
236 t.Fatal(err)
237 }
Jay Conrode1bdc762017-02-10 16:20:20 -0500238 var buf bytes.Buffer
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000239 pi := &PageInfo{
240 FSet: fset,
241 }
Jay Conrode1bdc762017-02-10 16:20:20 -0500242 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()
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000249}
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000250
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000251func 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 }
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000267 }
268}
Jay Conrod00f7cd52017-02-07 17:34:43 -0500269
270func 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}
Sina Siadat5128de72016-09-16 17:12:50 +0430295
296func 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
311func 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>`},
Andrew Bonventrea237aba2017-08-03 18:01:02 -0400318 {"pkg/", `<a href="/pkg">Index</a>`},
319 {"pkg/LICENSE", `<a href="/pkg">Index</a>`},
Sina Siadat5128de72016-09-16 17:12:50 +0430320 } {
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}
Mostyn Bramley-Moore9e9f7f62018-07-31 17:27:32 +0000326
327func 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}
aarzillic6fca022022-02-01 18:45:42 +0100373
374func 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(`
380package foo
381
382type T struct {
383 field *T
384}
385
386type ParametricStruct[T any] struct {
387 field *T
388}
389
390func F1[T any](arg T) { }
391
392func F2(arg T) { }
393
394func (*ParametricStruct[T]) M(arg T) { }
395
396func (*T) M(arg T) { }
397
398type ParametricStruct2[T1, T2 any] struct {
399 a T1
400 b T2
401}
402
403func (*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}
411type ParametricStruct[T <a href="/pkg/builtin/#any">any</a>] struct {
412<span id="ParametricStruct.field"></span>field *T
413}
414func F1[T <a href="/pkg/builtin/#any">any</a>](arg T) {}
415func F2(arg <a href="#T">T</a>) {}
416func (*<a href="#ParametricStruct">ParametricStruct</a>[T]) M(arg T) {}
417func (*<a href="#T">T</a>) M(arg <a href="#T">T</a>) {}
418type 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}
422func (*<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}