github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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 `Field`, // 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 `unexportedType.*Comment on line with unexported embedded field.`, 296 `\*unexportedType.*Comment on line with unexported embedded \*field.`, 297 `io.Reader.*Comment on line with embedded Reader.`, 298 `error.*Comment on line with embedded error.`, 299 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 300 `unexportedTypedConstant`, 301 }, 302 []string{ 303 `Has unexported fields`, 304 }, 305 }, 306 // Unexported type with -u. 307 { 308 "unexported type with -u", 309 []string{"-u", p, `unexportedType`}, 310 []string{ 311 `Comment about unexported type`, // Include comment. 312 `type unexportedType int`, // Type definition. 313 `func \(unexportedType\) ExportedMethod\(\) bool`, 314 `func \(unexportedType\) unexportedMethod\(\) bool`, 315 `ExportedTypedConstant_unexported unexportedType = iota`, 316 `const unexportedTypedConstant unexportedType = 1`, 317 }, 318 nil, 319 }, 320 321 // Interface. 322 { 323 "interface type", 324 []string{p, `ExportedInterface`}, 325 []string{ 326 `Comment about exported interface`, // Include comment. 327 `type ExportedInterface interface`, // Interface definition. 328 `Comment before exported method.*\n.*ExportedMethod\(\)` + 329 `.*Comment on line with exported method`, 330 `io.Reader.*Comment on line with embedded Reader.`, 331 `error.*Comment on line with embedded error.`, 332 `Has unexported methods`, 333 }, 334 []string{ 335 `unexportedField`, // No unexported field. 336 `Comment about exported method`, // No comment about exported method. 337 `unexportedMethod`, // No unexported method. 338 `unexportedTypedConstant`, // No unexported constant. 339 }, 340 }, 341 // Interface -u with unexported methods. 342 { 343 "interface type with unexported methods and -u", 344 []string{"-u", p, `ExportedInterface`}, 345 []string{ 346 `Comment about exported interface`, // Include comment. 347 `type ExportedInterface interface`, // Interface definition. 348 `Comment before exported method.*\n.*ExportedMethod\(\)` + 349 `.*Comment on line with exported method`, 350 `unexportedMethod\(\).*Comment on line with unexported method.`, 351 `io.Reader.*Comment on line with embedded Reader.`, 352 `error.*Comment on line with embedded error.`, 353 }, 354 []string{ 355 `Has unexported methods`, 356 }, 357 }, 358 359 // Interface method. 360 { 361 "interface method", 362 []string{p, `ExportedInterface.ExportedMethod`}, 363 []string{ 364 `Comment before exported method.*\n.*ExportedMethod\(\)` + 365 `.*Comment on line with exported method`, 366 }, 367 []string{ 368 `Comment about exported interface.`, 369 }, 370 }, 371 372 // Method. 373 { 374 "method", 375 []string{p, `ExportedType.ExportedMethod`}, 376 []string{ 377 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 378 `Comment about exported method.`, 379 }, 380 nil, 381 }, 382 // Method with -u. 383 { 384 "method with -u", 385 []string{"-u", p, `ExportedType.unexportedMethod`}, 386 []string{ 387 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 388 `Comment about unexported method.`, 389 }, 390 nil, 391 }, 392 393 // Field. 394 { 395 "field", 396 []string{p, `ExportedType.ExportedField`}, 397 []string{ 398 `ExportedField int`, 399 `Comment before exported field.`, 400 `Comment on line with exported field.`, 401 }, 402 nil, 403 }, 404 405 // Field with -u. 406 { 407 "method with -u", 408 []string{"-u", p, `ExportedType.unexportedField`}, 409 []string{ 410 `unexportedField int`, 411 `Comment on line with unexported field.`, 412 }, 413 nil, 414 }, 415 416 // Case matching off. 417 { 418 "case matching off", 419 []string{p, `casematch`}, 420 []string{ 421 `CaseMatch`, 422 `Casematch`, 423 }, 424 nil, 425 }, 426 427 // Case matching on. 428 { 429 "case matching on", 430 []string{"-c", p, `Casematch`}, 431 []string{ 432 `Casematch`, 433 }, 434 []string{ 435 `CaseMatch`, 436 }, 437 }, 438 } 439 440 func TestDoc(t *testing.T) { 441 maybeSkip(t) 442 for _, test := range tests { 443 var b bytes.Buffer 444 var flagSet flag.FlagSet 445 err := do(&b, &flagSet, test.args) 446 if err != nil { 447 t.Fatalf("%s: %s\n", test.name, err) 448 } 449 output := b.Bytes() 450 failed := false 451 for j, yes := range test.yes { 452 re, err := regexp.Compile(yes) 453 if err != nil { 454 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err) 455 } 456 if !re.Match(output) { 457 t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes) 458 failed = true 459 } 460 } 461 for j, no := range test.no { 462 re, err := regexp.Compile(no) 463 if err != nil { 464 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err) 465 } 466 if re.Match(output) { 467 t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no) 468 failed = true 469 } 470 } 471 if failed { 472 t.Logf("\n%s", output) 473 } 474 } 475 } 476 477 // Test the code to try multiple packages. Our test case is 478 // go doc rand.Float64 479 // This needs to find math/rand.Float64; however crypto/rand, which doesn't 480 // have the symbol, usually appears first in the directory listing. 481 func TestMultiplePackages(t *testing.T) { 482 if testing.Short() { 483 t.Skip("scanning file system takes too long") 484 } 485 maybeSkip(t) 486 var b bytes.Buffer // We don't care about the output. 487 // Make sure crypto/rand does not have the symbol. 488 { 489 var flagSet flag.FlagSet 490 err := do(&b, &flagSet, []string{"crypto/rand.float64"}) 491 if err == nil { 492 t.Errorf("expected error from crypto/rand.float64") 493 } else if !strings.Contains(err.Error(), "no symbol float64") { 494 t.Errorf("unexpected error %q from crypto/rand.float64", err) 495 } 496 } 497 // Make sure math/rand does have the symbol. 498 { 499 var flagSet flag.FlagSet 500 err := do(&b, &flagSet, []string{"math/rand.float64"}) 501 if err != nil { 502 t.Errorf("unexpected error %q from math/rand.float64", err) 503 } 504 } 505 // Try the shorthand. 506 { 507 var flagSet flag.FlagSet 508 err := do(&b, &flagSet, []string{"rand.float64"}) 509 if err != nil { 510 t.Errorf("unexpected error %q from rand.float64", err) 511 } 512 } 513 // Now try a missing symbol. We should see both packages in the error. 514 { 515 var flagSet flag.FlagSet 516 err := do(&b, &flagSet, []string{"rand.doesnotexit"}) 517 if err == nil { 518 t.Errorf("expected error from rand.doesnotexit") 519 } else { 520 errStr := err.Error() 521 if !strings.Contains(errStr, "no symbol") { 522 t.Errorf("error %q should contain 'no symbol", errStr) 523 } 524 if !strings.Contains(errStr, "crypto/rand") { 525 t.Errorf("error %q should contain crypto/rand", errStr) 526 } 527 if !strings.Contains(errStr, "math/rand") { 528 t.Errorf("error %q should contain math/rand", errStr) 529 } 530 } 531 } 532 } 533 534 type trimTest struct { 535 path string 536 prefix string 537 result string 538 ok bool 539 } 540 541 var trimTests = []trimTest{ 542 {"", "", "", true}, 543 {"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, 544 {"/usr/gopher/bar", "/usr/gopher", "bar", true}, 545 {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false}, 546 {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false}, 547 } 548 549 func TestTrim(t *testing.T) { 550 for _, test := range trimTests { 551 result, ok := trim(test.path, test.prefix) 552 if ok != test.ok { 553 t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok) 554 continue 555 } 556 if result != test.result { 557 t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result) 558 continue 559 } 560 } 561 }