github.com/stingnevermore/go@v0.0.0-20180120041312-3810f5bfed72/src/cmd/doc/doc_test.go (about) 1 // Copyright 2015 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 main 6 7 import ( 8 "bytes" 9 "flag" 10 "regexp" 11 "runtime" 12 "strings" 13 "testing" 14 ) 15 16 func maybeSkip(t *testing.T) { 17 if strings.HasPrefix(runtime.GOOS, "nacl") { 18 t.Skip("nacl does not have a full file tree") 19 } 20 if runtime.GOOS == "darwin" && strings.HasPrefix(runtime.GOARCH, "arm") { 21 t.Skip("darwin/arm does not have a full file tree") 22 } 23 } 24 25 type test struct { 26 name string 27 args []string // Arguments to "[go] doc". 28 yes []string // Regular expressions that should match. 29 no []string // Regular expressions that should not match. 30 } 31 32 const p = "cmd/doc/testdata" 33 34 var tests = []test{ 35 // Sanity check. 36 { 37 "sanity check", 38 []string{p}, 39 []string{`type ExportedType struct`}, 40 nil, 41 }, 42 43 // Package dump includes import, package statement. 44 { 45 "package clause", 46 []string{p}, 47 []string{`package pkg.*cmd/doc/testdata`}, 48 nil, 49 }, 50 51 // Constants. 52 // Package dump 53 { 54 "full package", 55 []string{p}, 56 []string{ 57 `Package comment`, 58 `const ExportedConstant = 1`, // Simple constant. 59 `const ConstOne = 1`, // First entry in constant block. 60 `const ConstFive ...`, // From block starting with unexported constant. 61 `var ExportedVariable = 1`, // Simple variable. 62 `var VarOne = 1`, // First entry in variable block. 63 `func ExportedFunc\(a int\) bool`, // Function. 64 `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type. 65 `type ExportedType struct{ ... }`, // Exported type. 66 `const ExportedTypedConstant ExportedType = iota`, // Typed constant. 67 `const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type. 68 `const ConstLeft2 uint64 ...`, // Typed constant using unexported iota. 69 `const ConstGroup1 unexportedType = iota ...`, // Typed constant using unexported type. 70 `const ConstGroup4 ExportedType = ExportedType{}`, // Typed constant using exported type. 71 `const MultiLineConst = ...`, // Multi line constant. 72 `var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable. 73 `func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function. 74 `var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`, // Long list of arguments. 75 `type T1 = T2`, // Type alias 76 }, 77 []string{ 78 `const internalConstant = 2`, // No internal constants. 79 `var internalVariable = 2`, // No internal variables. 80 `func internalFunc(a int) bool`, // No internal functions. 81 `Comment about exported constant`, // No comment for single constant. 82 `Comment about exported variable`, // No comment for single variable. 83 `Comment about block of constants.`, // No comment for constant block. 84 `Comment about block of variables.`, // No comment for variable block. 85 `Comment before ConstOne`, // No comment for first entry in constant block. 86 `Comment before VarOne`, // No comment for first entry in variable block. 87 `ConstTwo = 2`, // No second entry in constant block. 88 `VarTwo = 2`, // No second entry in variable block. 89 `VarFive = 5`, // From block starting with unexported variable. 90 `type unexportedType`, // No unexported type. 91 `unexportedTypedConstant`, // No unexported typed constant. 92 `\bField`, // No fields. 93 `Method`, // No methods. 94 `someArgument[5-8]`, // No truncated arguments. 95 `type T1 T2`, // Type alias does not display as type declaration. 96 }, 97 }, 98 // Package dump -u 99 { 100 "full package with u", 101 []string{`-u`, p}, 102 []string{ 103 `const ExportedConstant = 1`, // Simple constant. 104 `const internalConstant = 2`, // Internal constants. 105 `func internalFunc\(a int\) bool`, // Internal functions. 106 `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type. 107 }, 108 []string{ 109 `Comment about exported constant`, // No comment for simple constant. 110 `Comment about block of constants`, // No comment for constant block. 111 `Comment about internal function`, // No comment for internal function. 112 `MultiLine(String|Method|Field)`, // No data from multi line portions. 113 }, 114 }, 115 116 // Single constant. 117 { 118 "single constant", 119 []string{p, `ExportedConstant`}, 120 []string{ 121 `Comment about exported constant`, // Include comment. 122 `const ExportedConstant = 1`, 123 }, 124 nil, 125 }, 126 // Single constant -u. 127 { 128 "single constant with -u", 129 []string{`-u`, p, `internalConstant`}, 130 []string{ 131 `Comment about internal constant`, // Include comment. 132 `const internalConstant = 2`, 133 }, 134 nil, 135 }, 136 // Block of constants. 137 { 138 "block of constants", 139 []string{p, `ConstTwo`}, 140 []string{ 141 `Comment before ConstOne.\n.*ConstOne = 1`, // First... 142 `ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up. 143 `Comment about block of constants`, // Comment does too. 144 }, 145 []string{ 146 `constThree`, // No unexported constant. 147 }, 148 }, 149 // Block of constants -u. 150 { 151 "block of constants with -u", 152 []string{"-u", p, `constThree`}, 153 []string{ 154 `constThree = 3.*Comment on line with constThree`, 155 }, 156 nil, 157 }, 158 // Block of constants with carryover type from unexported field. 159 { 160 "block of constants with carryover type", 161 []string{p, `ConstLeft2`}, 162 []string{ 163 `ConstLeft2, constRight2 uint64`, 164 `constLeft3, ConstRight3`, 165 `ConstLeft4, ConstRight4`, 166 }, 167 nil, 168 }, 169 // Block of constants -u with carryover type from unexported field. 170 { 171 "block of constants with carryover type", 172 []string{"-u", p, `ConstLeft2`}, 173 []string{ 174 `_, _ uint64 = 2 \* iota, 1 << iota`, 175 `constLeft1, constRight1`, 176 `ConstLeft2, constRight2`, 177 `constLeft3, ConstRight3`, 178 `ConstLeft4, ConstRight4`, 179 }, 180 nil, 181 }, 182 183 // Single variable. 184 { 185 "single variable", 186 []string{p, `ExportedVariable`}, 187 []string{ 188 `ExportedVariable`, // Include comment. 189 `var ExportedVariable = 1`, 190 }, 191 nil, 192 }, 193 // Single variable -u. 194 { 195 "single variable with -u", 196 []string{`-u`, p, `internalVariable`}, 197 []string{ 198 `Comment about internal variable`, // Include comment. 199 `var internalVariable = 2`, 200 }, 201 nil, 202 }, 203 // Block of variables. 204 { 205 "block of variables", 206 []string{p, `VarTwo`}, 207 []string{ 208 `Comment before VarOne.\n.*VarOne = 1`, // First... 209 `VarTwo = 2.*Comment on line with VarTwo`, // And second show up. 210 `Comment about block of variables`, // Comment does too. 211 }, 212 []string{ 213 `varThree= 3`, // No unexported variable. 214 }, 215 }, 216 // Block of variables -u. 217 { 218 "block of variables with -u", 219 []string{"-u", p, `varThree`}, 220 []string{ 221 `varThree = 3.*Comment on line with varThree`, 222 }, 223 nil, 224 }, 225 226 // Function. 227 { 228 "function", 229 []string{p, `ExportedFunc`}, 230 []string{ 231 `Comment about exported function`, // Include comment. 232 `func ExportedFunc\(a int\) bool`, 233 }, 234 nil, 235 }, 236 // Function -u. 237 { 238 "function with -u", 239 []string{"-u", p, `internalFunc`}, 240 []string{ 241 `Comment about internal function`, // Include comment. 242 `func internalFunc\(a int\) bool`, 243 }, 244 nil, 245 }, 246 247 // Type. 248 { 249 "type", 250 []string{p, `ExportedType`}, 251 []string{ 252 `Comment about exported type`, // Include comment. 253 `type ExportedType struct`, // Type definition. 254 `Comment before exported field.*\n.*ExportedField +int` + 255 `.*Comment on line with exported field.`, 256 `ExportedEmbeddedType.*Comment on line with exported embedded field.`, 257 `Has unexported fields`, 258 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 259 `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. 260 `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor. 261 `io.Reader.*Comment on line with embedded Reader.`, 262 }, 263 []string{ 264 `unexportedField`, // No unexported field. 265 `int.*embedded`, // No unexported embedded field. 266 `Comment about exported method.`, // No comment about exported method. 267 `unexportedMethod`, // No unexported method. 268 `unexportedTypedConstant`, // No unexported constant. 269 `error`, // No embedded error. 270 }, 271 }, 272 // Type T1 dump (alias). 273 { 274 "type T1", 275 []string{p + ".T1"}, 276 []string{ 277 `type T1 = T2`, 278 }, 279 []string{ 280 `type T1 T2`, 281 `type ExportedType`, 282 }, 283 }, 284 // Type -u with unexported fields. 285 { 286 "type with unexported fields and -u", 287 []string{"-u", p, `ExportedType`}, 288 []string{ 289 `Comment about exported type`, // Include comment. 290 `type ExportedType struct`, // Type definition. 291 `Comment before exported field.*\n.*ExportedField +int`, 292 `unexportedField.*int.*Comment on line with unexported field.`, 293 `ExportedEmbeddedType.*Comment on line with exported embedded field.`, 294 `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`, 295 `\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field.`, 296 `unexportedType.*Comment on line with unexported embedded field.`, 297 `\*unexportedType.*Comment on line with unexported embedded \*field.`, 298 `io.Reader.*Comment on line with embedded Reader.`, 299 `error.*Comment on line with embedded error.`, 300 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 301 `unexportedTypedConstant`, 302 }, 303 []string{ 304 `Has unexported fields`, 305 }, 306 }, 307 // Unexported type with -u. 308 { 309 "unexported type with -u", 310 []string{"-u", p, `unexportedType`}, 311 []string{ 312 `Comment about unexported type`, // Include comment. 313 `type unexportedType int`, // Type definition. 314 `func \(unexportedType\) ExportedMethod\(\) bool`, 315 `func \(unexportedType\) unexportedMethod\(\) bool`, 316 `ExportedTypedConstant_unexported unexportedType = iota`, 317 `const unexportedTypedConstant unexportedType = 1`, 318 }, 319 nil, 320 }, 321 322 // Interface. 323 { 324 "interface type", 325 []string{p, `ExportedInterface`}, 326 []string{ 327 `Comment about exported interface`, // Include comment. 328 `type ExportedInterface interface`, // Interface definition. 329 `Comment before exported method.*\n.*ExportedMethod\(\)` + 330 `.*Comment on line with exported method`, 331 `io.Reader.*Comment on line with embedded Reader.`, 332 `error.*Comment on line with embedded error.`, 333 `Has unexported methods`, 334 }, 335 []string{ 336 `unexportedField`, // No unexported field. 337 `Comment about exported method`, // No comment about exported method. 338 `unexportedMethod`, // No unexported method. 339 `unexportedTypedConstant`, // No unexported constant. 340 }, 341 }, 342 // Interface -u with unexported methods. 343 { 344 "interface type with unexported methods and -u", 345 []string{"-u", p, `ExportedInterface`}, 346 []string{ 347 `Comment about exported interface`, // Include comment. 348 `type ExportedInterface interface`, // Interface definition. 349 `Comment before exported method.*\n.*ExportedMethod\(\)` + 350 `.*Comment on line with exported method`, 351 `unexportedMethod\(\).*Comment on line with unexported method.`, 352 `io.Reader.*Comment on line with embedded Reader.`, 353 `error.*Comment on line with embedded error.`, 354 }, 355 []string{ 356 `Has unexported methods`, 357 }, 358 }, 359 360 // Interface method. 361 { 362 "interface method", 363 []string{p, `ExportedInterface.ExportedMethod`}, 364 []string{ 365 `Comment before exported method.*\n.*ExportedMethod\(\)` + 366 `.*Comment on line with exported method`, 367 }, 368 []string{ 369 `Comment about exported interface.`, 370 }, 371 }, 372 373 // Method. 374 { 375 "method", 376 []string{p, `ExportedType.ExportedMethod`}, 377 []string{ 378 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 379 `Comment about exported method.`, 380 }, 381 nil, 382 }, 383 // Method with -u. 384 { 385 "method with -u", 386 []string{"-u", p, `ExportedType.unexportedMethod`}, 387 []string{ 388 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 389 `Comment about unexported method.`, 390 }, 391 nil, 392 }, 393 394 // Field. 395 { 396 "field", 397 []string{p, `ExportedType.ExportedField`}, 398 []string{ 399 `type ExportedType struct`, 400 `ExportedField int`, 401 `Comment before exported field.`, 402 `Comment on line with exported field.`, 403 `other fields elided`, 404 }, 405 nil, 406 }, 407 408 // Field with -u. 409 { 410 "method with -u", 411 []string{"-u", p, `ExportedType.unexportedField`}, 412 []string{ 413 `unexportedField int`, 414 `Comment on line with unexported field.`, 415 }, 416 nil, 417 }, 418 419 // Field of struct with only one field. 420 { 421 "single-field struct", 422 []string{p, `ExportedStructOneField.OnlyField`}, 423 []string{`the only field`}, 424 []string{`other fields elided`}, 425 }, 426 427 // Case matching off. 428 { 429 "case matching off", 430 []string{p, `casematch`}, 431 []string{ 432 `CaseMatch`, 433 `Casematch`, 434 }, 435 nil, 436 }, 437 438 // Case matching on. 439 { 440 "case matching on", 441 []string{"-c", p, `Casematch`}, 442 []string{ 443 `Casematch`, 444 }, 445 []string{ 446 `CaseMatch`, 447 }, 448 }, 449 450 // No dups with -u. Issue 21797. 451 { 452 "case matching on, no dups", 453 []string{"-u", p, `duplicate`}, 454 []string{ 455 `Duplicate`, 456 `duplicate`, 457 }, 458 []string{ 459 "\\)\n+const", // This will appear if the const decl appears twice. 460 }, 461 }, 462 } 463 464 func TestDoc(t *testing.T) { 465 maybeSkip(t) 466 for _, test := range tests { 467 var b bytes.Buffer 468 var flagSet flag.FlagSet 469 err := do(&b, &flagSet, test.args) 470 if err != nil { 471 t.Fatalf("%s: %s\n", test.name, err) 472 } 473 output := b.Bytes() 474 failed := false 475 for j, yes := range test.yes { 476 re, err := regexp.Compile(yes) 477 if err != nil { 478 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err) 479 } 480 if !re.Match(output) { 481 t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes) 482 failed = true 483 } 484 } 485 for j, no := range test.no { 486 re, err := regexp.Compile(no) 487 if err != nil { 488 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err) 489 } 490 if re.Match(output) { 491 t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no) 492 failed = true 493 } 494 } 495 if failed { 496 t.Logf("\n%s", output) 497 } 498 } 499 } 500 501 // Test the code to try multiple packages. Our test case is 502 // go doc rand.Float64 503 // This needs to find math/rand.Float64; however crypto/rand, which doesn't 504 // have the symbol, usually appears first in the directory listing. 505 func TestMultiplePackages(t *testing.T) { 506 if testing.Short() { 507 t.Skip("scanning file system takes too long") 508 } 509 maybeSkip(t) 510 var b bytes.Buffer // We don't care about the output. 511 // Make sure crypto/rand does not have the symbol. 512 { 513 var flagSet flag.FlagSet 514 err := do(&b, &flagSet, []string{"crypto/rand.float64"}) 515 if err == nil { 516 t.Errorf("expected error from crypto/rand.float64") 517 } else if !strings.Contains(err.Error(), "no symbol float64") { 518 t.Errorf("unexpected error %q from crypto/rand.float64", err) 519 } 520 } 521 // Make sure math/rand does have the symbol. 522 { 523 var flagSet flag.FlagSet 524 err := do(&b, &flagSet, []string{"math/rand.float64"}) 525 if err != nil { 526 t.Errorf("unexpected error %q from math/rand.float64", err) 527 } 528 } 529 // Try the shorthand. 530 { 531 var flagSet flag.FlagSet 532 err := do(&b, &flagSet, []string{"rand.float64"}) 533 if err != nil { 534 t.Errorf("unexpected error %q from rand.float64", err) 535 } 536 } 537 // Now try a missing symbol. We should see both packages in the error. 538 { 539 var flagSet flag.FlagSet 540 err := do(&b, &flagSet, []string{"rand.doesnotexit"}) 541 if err == nil { 542 t.Errorf("expected error from rand.doesnotexit") 543 } else { 544 errStr := err.Error() 545 if !strings.Contains(errStr, "no symbol") { 546 t.Errorf("error %q should contain 'no symbol", errStr) 547 } 548 if !strings.Contains(errStr, "crypto/rand") { 549 t.Errorf("error %q should contain crypto/rand", errStr) 550 } 551 if !strings.Contains(errStr, "math/rand") { 552 t.Errorf("error %q should contain math/rand", errStr) 553 } 554 } 555 } 556 } 557 558 // Test the code to look up packages when given two args. First test case is 559 // go doc binary BigEndian 560 // This needs to find encoding/binary.BigEndian, which means 561 // finding the package encoding/binary given only "binary". 562 // Second case is 563 // go doc rand Float64 564 // which again needs to find math/rand and not give up after crypto/rand, 565 // which has no such function. 566 func TestTwoArgLookup(t *testing.T) { 567 if testing.Short() { 568 t.Skip("scanning file system takes too long") 569 } 570 maybeSkip(t) 571 var b bytes.Buffer // We don't care about the output. 572 { 573 var flagSet flag.FlagSet 574 err := do(&b, &flagSet, []string{"binary", "BigEndian"}) 575 if err != nil { 576 t.Errorf("unexpected error %q from binary BigEndian", err) 577 } 578 } 579 { 580 var flagSet flag.FlagSet 581 err := do(&b, &flagSet, []string{"rand", "Float64"}) 582 if err != nil { 583 t.Errorf("unexpected error %q from rand Float64", err) 584 } 585 } 586 { 587 var flagSet flag.FlagSet 588 err := do(&b, &flagSet, []string{"bytes", "Foo"}) 589 if err == nil { 590 t.Errorf("expected error from bytes Foo") 591 } else if !strings.Contains(err.Error(), "no symbol Foo") { 592 t.Errorf("unexpected error %q from bytes Foo", err) 593 } 594 } 595 { 596 var flagSet flag.FlagSet 597 err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"}) 598 if err == nil { 599 // actually present in the user's filesystem 600 } else if !strings.Contains(err.Error(), "no such package") { 601 t.Errorf("unexpected error %q from nosuchpackage Foo", err) 602 } 603 } 604 } 605 606 type trimTest struct { 607 path string 608 prefix string 609 result string 610 ok bool 611 } 612 613 var trimTests = []trimTest{ 614 {"", "", "", true}, 615 {"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, 616 {"/usr/gopher/bar", "/usr/gopher", "bar", true}, 617 {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false}, 618 {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false}, 619 } 620 621 func TestTrim(t *testing.T) { 622 for _, test := range trimTests { 623 result, ok := trim(test.path, test.prefix) 624 if ok != test.ok { 625 t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok) 626 continue 627 } 628 if result != test.result { 629 t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result) 630 continue 631 } 632 } 633 }