blob: 33dbe3f619b1731fee9a029b5d207ae44dca0a67 [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"
13)
14
15func TestPkgLinkFunc(t *testing.T) {
16 for _, tc := range []struct {
17 path string
18 want string
19 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040020 {"/src/fmt", "pkg/fmt"},
21 {"src/fmt", "pkg/fmt"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050022 {"/fmt", "pkg/fmt"},
Brad Garcia936715c2014-06-27 10:25:57 -040023 {"fmt", "pkg/fmt"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050024 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070025 if got := pkgLinkFunc(tc.path); got != tc.want {
26 t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050027 }
28 }
29}
30
31func TestSrcPosLinkFunc(t *testing.T) {
32 for _, tc := range []struct {
33 src string
34 line int
35 low int
36 high int
37 want string
38 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040039 {"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"},
40 {"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
41 {"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"},
42 {"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
43 {"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
44 {"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
45 {"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050046 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070047 if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want {
48 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 -050049 }
50 }
51}
52
53func TestSrcLinkFunc(t *testing.T) {
54 for _, tc := range []struct {
55 src string
56 want string
57 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040058 {"/src/fmt/print.go", "/src/fmt/print.go"},
59 {"src/fmt/print.go", "/src/fmt/print.go"},
60 {"/fmt/print.go", "/src/fmt/print.go"},
61 {"fmt/print.go", "/src/fmt/print.go"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050062 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070063 if got := srcLinkFunc(tc.src); got != tc.want {
64 t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
Brad Garciaff7cfaf2013-12-04 10:37:01 -050065 }
66 }
67}
68
69func TestQueryLinkFunc(t *testing.T) {
70 for _, tc := range []struct {
71 src string
72 query string
73 line int
74 want string
75 }{
Alan Donovan6c93dbf2014-09-10 09:02:54 -040076 {"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"},
77 {"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"},
78 {"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"},
79 {"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050080 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070081 if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want {
82 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 -050083 }
84 }
85}
86
87func TestDocLinkFunc(t *testing.T) {
88 for _, tc := range []struct {
89 src string
90 ident string
91 want string
92 }{
Brad Garcia936715c2014-06-27 10:25:57 -040093 {"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
94 {"fmt", "EOF", "/pkg/fmt/#EOF"},
Brad Garciaff7cfaf2013-12-04 10:37:01 -050095 } {
Robert Griesemer42a4cd32014-04-07 12:54:28 -070096 if got := docLinkFunc(tc.src, tc.ident); got != tc.want {
97 t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want)
98 }
99 }
100}
101
102func TestSanitizeFunc(t *testing.T) {
103 for _, tc := range []struct {
104 src string
105 want string
106 }{
107 {},
108 {"foo", "foo"},
109 {"func f()", "func f()"},
110 {"func f(a int,)", "func f(a int)"},
111 {"func f(a int,\n)", "func f(a int)"},
112 {"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"},
113 {" ( a, b, c ) ", "(a, b, c)"},
114 {"( a, b, c int, foo bar , )", "(a, b, c int, foo bar)"},
115 {"{ a, b}", "{a, b}"},
116 {"[ a, b]", "[a, b]"},
117 } {
118 if got := sanitizeFunc(tc.src); got != tc.want {
119 t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want)
Brad Garciaff7cfaf2013-12-04 10:37:01 -0500120 }
121 }
122}
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000123
124// Test that we add <span id="StructName.FieldName"> elements
125// to the HTML of struct fields.
126func TestStructFieldsIDAttributes(t *testing.T) {
Jay Conrode1bdc762017-02-10 16:20:20 -0500127 got := linkifySource(t, []byte(`
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000128package foo
129
130type T struct {
Brad Fitzpatricke5f9a3d2016-11-30 23:49:04 +0000131 NoDoc string
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000132
Brad Fitzpatricke5f9a3d2016-11-30 23:49:04 +0000133 // Doc has a comment.
134 Doc string
135
136 // Opt, if non-nil, is an option.
137 Opt *int
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000138
139 // Опция - другое поле.
140 Опция bool
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000141}
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000142`))
143 want := `type T struct {
144<span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
145
146<span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
147Doc <a href="/pkg/builtin/#string">string</a>
148
149<span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
150Opt *<a href="/pkg/builtin/#int">int</a>
151
152<span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
153Опция <a href="/pkg/builtin/#bool">bool</a>
154}`
155 if got != want {
156 t.Errorf("got: %s\n\nwant: %s\n", got, want)
157 }
158}
159
Jay Conrod3bcb6ef2017-04-11 15:25:53 -0400160// Test that we add <span id="ConstName"> elements to the HTML
161// of definitions in const and var specs.
162func TestValueSpecIDAttributes(t *testing.T) {
163 got := linkifySource(t, []byte(`
164package foo
165
166const (
167 NoDoc string = "NoDoc"
168
169 // Doc has a comment
170 Doc = "Doc"
171
172 NoVal
173)`))
174 want := `const (
175<span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = &#34;NoDoc&#34;
176
177<span class="comment">// Doc has a comment</span>
178<span id="Doc">Doc</span> = &#34;Doc&#34;
179
180<span id="NoVal">NoVal</span>
181)`
182 if got != want {
183 t.Errorf("got: %s\n\nwant: %s\n", got, want)
184 }
185}
186
Jay Conrode1bdc762017-02-10 16:20:20 -0500187func TestCompositeLitLinkFields(t *testing.T) {
188 got := linkifySource(t, []byte(`
189package foo
190
191type T struct {
192 X int
193}
194
195var S T = T{X: 12}`))
196 want := `type T struct {
197<span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a>
198}
Jay Conrod3bcb6ef2017-04-11 15:25:53 -0400199var <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 -0500200 if got != want {
201 t.Errorf("got: %s\n\nwant: %s\n", got, want)
202 }
203}
204
Masahiro Furudatece129152017-05-07 04:40:42 +0900205func TestFuncDeclNotLink(t *testing.T) {
206 // Function.
207 got := linkifySource(t, []byte(`
208package http
209
210func Get(url string) (resp *Response, err error)`))
211 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>)`
212 if got != want {
213 t.Errorf("got: %s\n\nwant: %s\n", got, want)
214 }
215
216 // Method.
217 got = linkifySource(t, []byte(`
218package http
219
220func (h Header) Get(key string) string`))
221 want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>`
222 if got != want {
223 t.Errorf("got: %s\n\nwant: %s\n", got, want)
224 }
225}
226
Jay Conrode1bdc762017-02-10 16:20:20 -0500227func linkifySource(t *testing.T, src []byte) string {
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000228 p := &Presentation{
229 DeclLinks: true,
230 }
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000231 fset := token.NewFileSet()
232 af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
233 if err != nil {
234 t.Fatal(err)
235 }
Jay Conrode1bdc762017-02-10 16:20:20 -0500236 var buf bytes.Buffer
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000237 pi := &PageInfo{
238 FSet: fset,
239 }
Jay Conrode1bdc762017-02-10 16:20:20 -0500240 sep := ""
241 for _, decl := range af.Decls {
242 buf.WriteString(sep)
243 sep = "\n"
244 buf.WriteString(p.node_htmlFunc(pi, decl, true))
245 }
246 return buf.String()
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000247}
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000248
Brad Fitzpatrick61efd712017-01-20 18:35:00 +0000249func TestScanIdentifier(t *testing.T) {
250 tests := []struct {
251 in, want string
252 }{
253 {"foo bar", "foo"},
254 {"foo/bar", "foo"},
255 {" foo", ""},
256 {"фоо", "фоо"},
257 {"f123", "f123"},
258 {"123f", ""},
259 }
260 for _, tt := range tests {
261 got := scanIdentifier([]byte(tt.in))
262 if string(got) != tt.want {
263 t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
264 }
Brad Fitzpatrick0f65b312016-11-29 23:53:36 +0000265 }
266}
Jay Conrod00f7cd52017-02-07 17:34:43 -0500267
268func TestReplaceLeadingIndentation(t *testing.T) {
269 oldIndent := strings.Repeat(" ", 2)
270 newIndent := strings.Repeat(" ", 4)
271 tests := []struct {
272 src, want string
273 }{
274 {" foo\n bar\n baz", " foo\n bar\n baz"},
275 {" '`'\n '`'\n", " '`'\n '`'\n"},
276 {" '\\''\n '`'\n", " '\\''\n '`'\n"},
277 {" \"`\"\n \"`\"\n", " \"`\"\n \"`\"\n"},
278 {" `foo\n bar`", " `foo\n bar`"},
279 {" `foo\\`\n bar", " `foo\\`\n bar"},
280 {" '\\`'`foo\n bar", " '\\`'`foo\n bar"},
281 {
282 " if true {\n foo := `One\n \tTwo\nThree`\n }\n",
283 " if true {\n foo := `One\n \tTwo\n Three`\n }\n",
284 },
285 }
286 for _, tc := range tests {
287 if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want {
288 t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n",
289 tc.src, got, tc.want)
290 }
291 }
292}
Sina Siadat5128de72016-09-16 17:12:50 +0430293
294func TestSrcBreadcrumbFunc(t *testing.T) {
295 for _, tc := range []struct {
296 path string
297 want string
298 }{
299 {"src/", `<span class="text-muted">src/</span>`},
300 {"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
301 {"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
302 } {
303 if got := srcBreadcrumbFunc(tc.path); got != tc.want {
304 t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
305 }
306 }
307}
308
309func TestSrcToPkgLinkFunc(t *testing.T) {
310 for _, tc := range []struct {
311 path string
312 want string
313 }{
314 {"src/", `<a href="/pkg">Index</a>`},
315 {"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
Andrew Bonventrea237aba2017-08-03 18:01:02 -0400316 {"pkg/", `<a href="/pkg">Index</a>`},
317 {"pkg/LICENSE", `<a href="/pkg">Index</a>`},
Sina Siadat5128de72016-09-16 17:12:50 +0430318 } {
319 if got := srcToPkgLinkFunc(tc.path); got != tc.want {
320 t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
321 }
322 }
323}
Mostyn Bramley-Moore9e9f7f62018-07-31 17:27:32 +0000324
325func TestFilterOutBuildAnnotations(t *testing.T) {
326 // TODO: simplify this by using a multiline string once we stop
327 // using go vet from 1.10 on the build dashboard.
328 // https://golang.org/issue/26627
329 src := []byte("// +build !foo\n" +
330 "// +build !anothertag\n" +
331 "\n" +
332 "// non-tag comment\n" +
333 "\n" +
334 "package foo\n" +
335 "\n" +
336 "func bar() int {\n" +
337 " return 42\n" +
338 "}\n")
339
340 fset := token.NewFileSet()
341 af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
342 if err != nil {
343 t.Fatal(err)
344 }
345
346 var found bool
347 for _, cg := range af.Comments {
348 if strings.HasPrefix(cg.Text(), "+build ") {
349 found = true
350 break
351 }
352 }
353 if !found {
354 t.Errorf("TestFilterOutBuildAnnotations is broken: missing build tag in test input")
355 }
356
357 found = false
358 for _, cg := range filterOutBuildAnnotations(af.Comments) {
359 if strings.HasPrefix(cg.Text(), "+build ") {
360 t.Errorf("filterOutBuildAnnotations failed to filter build tag")
361 }
362
363 if strings.Contains(cg.Text(), "non-tag comment") {
364 found = true
365 }
366 }
367 if !found {
368 t.Errorf("filterOutBuildAnnotations should not remove non-build tag comment")
369 }
370}