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> = "NoDoc" 178 179 <span class="comment">// Doc has a comment</span> 180 <span id="Doc">Doc</span> = "Doc" 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 }