github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/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 }, 75 []string{ 76 `const internalConstant = 2`, // No internal constants. 77 `var internalVariable = 2`, // No internal variables. 78 `func internalFunc(a int) bool`, // No internal functions. 79 `Comment about exported constant`, // No comment for single constant. 80 `Comment about exported variable`, // No comment for single variable. 81 `Comment about block of constants.`, // No comment for constant block. 82 `Comment about block of variables.`, // No comment for variable block. 83 `Comment before ConstOne`, // No comment for first entry in constant block. 84 `Comment before VarOne`, // No comment for first entry in variable block. 85 `ConstTwo = 2`, // No second entry in constant block. 86 `VarTwo = 2`, // No second entry in variable block. 87 `VarFive = 5`, // From block starting with unexported variable. 88 `type unexportedType`, // No unexported type. 89 `unexportedTypedConstant`, // No unexported typed constant. 90 `Field`, // No fields. 91 `Method`, // No methods. 92 }, 93 }, 94 // Package dump -u 95 { 96 "full package with u", 97 []string{`-u`, p}, 98 []string{ 99 `const ExportedConstant = 1`, // Simple constant. 100 `const internalConstant = 2`, // Internal constants. 101 `func internalFunc\(a int\) bool`, // Internal functions. 102 `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type. 103 }, 104 []string{ 105 `Comment about exported constant`, // No comment for simple constant. 106 `Comment about block of constants`, // No comment for constant block. 107 `Comment about internal function`, // No comment for internal function. 108 `MultiLine(String|Method|Field)`, // No data from multi line portions. 109 }, 110 }, 111 112 // Single constant. 113 { 114 "single constant", 115 []string{p, `ExportedConstant`}, 116 []string{ 117 `Comment about exported constant`, // Include comment. 118 `const ExportedConstant = 1`, 119 }, 120 nil, 121 }, 122 // Single constant -u. 123 { 124 "single constant with -u", 125 []string{`-u`, p, `internalConstant`}, 126 []string{ 127 `Comment about internal constant`, // Include comment. 128 `const internalConstant = 2`, 129 }, 130 nil, 131 }, 132 // Block of constants. 133 { 134 "block of constants", 135 []string{p, `ConstTwo`}, 136 []string{ 137 `Comment before ConstOne.\n.*ConstOne = 1`, // First... 138 `ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up. 139 `Comment about block of constants`, // Comment does too. 140 }, 141 []string{ 142 `constThree`, // No unexported constant. 143 }, 144 }, 145 // Block of constants -u. 146 { 147 "block of constants with -u", 148 []string{"-u", p, `constThree`}, 149 []string{ 150 `constThree = 3.*Comment on line with constThree`, 151 }, 152 nil, 153 }, 154 // Block of constants with carryover type from unexported field. 155 { 156 "block of constants with carryover type", 157 []string{p, `ConstLeft2`}, 158 []string{ 159 `ConstLeft2, constRight2 uint64`, 160 `constLeft3, ConstRight3`, 161 `ConstLeft4, ConstRight4`, 162 }, 163 nil, 164 }, 165 // Block of constants -u with carryover type from unexported field. 166 { 167 "block of constants with carryover type", 168 []string{"-u", p, `ConstLeft2`}, 169 []string{ 170 `_, _ uint64 = 2 \* iota, 1 << iota`, 171 `constLeft1, constRight1`, 172 `ConstLeft2, constRight2`, 173 `constLeft3, ConstRight3`, 174 `ConstLeft4, ConstRight4`, 175 }, 176 nil, 177 }, 178 179 // Single variable. 180 { 181 "single variable", 182 []string{p, `ExportedVariable`}, 183 []string{ 184 `ExportedVariable`, // Include comment. 185 `var ExportedVariable = 1`, 186 }, 187 nil, 188 }, 189 // Single variable -u. 190 { 191 "single variable with -u", 192 []string{`-u`, p, `internalVariable`}, 193 []string{ 194 `Comment about internal variable`, // Include comment. 195 `var internalVariable = 2`, 196 }, 197 nil, 198 }, 199 // Block of variables. 200 { 201 "block of variables", 202 []string{p, `VarTwo`}, 203 []string{ 204 `Comment before VarOne.\n.*VarOne = 1`, // First... 205 `VarTwo = 2.*Comment on line with VarTwo`, // And second show up. 206 `Comment about block of variables`, // Comment does too. 207 }, 208 []string{ 209 `varThree= 3`, // No unexported variable. 210 }, 211 }, 212 // Block of variables -u. 213 { 214 "block of variables with -u", 215 []string{"-u", p, `varThree`}, 216 []string{ 217 `varThree = 3.*Comment on line with varThree`, 218 }, 219 nil, 220 }, 221 222 // Function. 223 { 224 "function", 225 []string{p, `ExportedFunc`}, 226 []string{ 227 `Comment about exported function`, // Include comment. 228 `func ExportedFunc\(a int\) bool`, 229 }, 230 nil, 231 }, 232 // Function -u. 233 { 234 "function with -u", 235 []string{"-u", p, `internalFunc`}, 236 []string{ 237 `Comment about internal function`, // Include comment. 238 `func internalFunc\(a int\) bool`, 239 }, 240 nil, 241 }, 242 243 // Type. 244 { 245 "type", 246 []string{p, `ExportedType`}, 247 []string{ 248 `Comment about exported type`, // Include comment. 249 `type ExportedType struct`, // Type definition. 250 `Comment before exported field.*\n.*ExportedField +int` + 251 `.*Comment on line with exported field.`, 252 `ExportedEmbeddedType.*Comment on line with exported embedded field.`, 253 `Has unexported fields`, 254 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 255 `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. 256 `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor. 257 `io.Reader.*Comment on line with embedded Reader.`, 258 }, 259 []string{ 260 `unexportedField`, // No unexported field. 261 `int.*embedded`, // No unexported embedded field. 262 `Comment about exported method.`, // No comment about exported method. 263 `unexportedMethod`, // No unexported method. 264 `unexportedTypedConstant`, // No unexported constant. 265 `error`, // No embedded error. 266 }, 267 }, 268 // Type -u with unexported fields. 269 { 270 "type with unexported fields and -u", 271 []string{"-u", p, `ExportedType`}, 272 []string{ 273 `Comment about exported type`, // Include comment. 274 `type ExportedType struct`, // Type definition. 275 `Comment before exported field.*\n.*ExportedField +int`, 276 `unexportedField.*int.*Comment on line with unexported field.`, 277 `ExportedEmbeddedType.*Comment on line with exported embedded field.`, 278 `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`, 279 `unexportedType.*Comment on line with unexported embedded field.`, 280 `\*unexportedType.*Comment on line with unexported embedded \*field.`, 281 `io.Reader.*Comment on line with embedded Reader.`, 282 `error.*Comment on line with embedded error.`, 283 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 284 `unexportedTypedConstant`, 285 }, 286 []string{ 287 `Has unexported fields`, 288 }, 289 }, 290 // Unexported type with -u. 291 { 292 "unexported type with -u", 293 []string{"-u", p, `unexportedType`}, 294 []string{ 295 `Comment about unexported type`, // Include comment. 296 `type unexportedType int`, // Type definition. 297 `func \(unexportedType\) ExportedMethod\(\) bool`, 298 `func \(unexportedType\) unexportedMethod\(\) bool`, 299 `ExportedTypedConstant_unexported unexportedType = iota`, 300 `const unexportedTypedConstant unexportedType = 1`, 301 }, 302 nil, 303 }, 304 305 // Interface. 306 { 307 "interface type", 308 []string{p, `ExportedInterface`}, 309 []string{ 310 `Comment about exported interface`, // Include comment. 311 `type ExportedInterface interface`, // Interface definition. 312 `Comment before exported method.*\n.*ExportedMethod\(\)` + 313 `.*Comment on line with exported method`, 314 `io.Reader.*Comment on line with embedded Reader.`, 315 `error.*Comment on line with embedded error.`, 316 `Has unexported methods`, 317 }, 318 []string{ 319 `unexportedField`, // No unexported field. 320 `Comment about exported method`, // No comment about exported method. 321 `unexportedMethod`, // No unexported method. 322 `unexportedTypedConstant`, // No unexported constant. 323 }, 324 }, 325 // Interface -u with unexported methods. 326 { 327 "interface type with unexported methods and -u", 328 []string{"-u", p, `ExportedInterface`}, 329 []string{ 330 `Comment about exported interface`, // Include comment. 331 `type ExportedInterface interface`, // Interface definition. 332 `Comment before exported method.*\n.*ExportedMethod\(\)` + 333 `.*Comment on line with exported method`, 334 `unexportedMethod\(\).*Comment on line with unexported method.`, 335 `io.Reader.*Comment on line with embedded Reader.`, 336 `error.*Comment on line with embedded error.`, 337 }, 338 []string{ 339 `Has unexported methods`, 340 }, 341 }, 342 343 // Interface method. 344 { 345 "interface method", 346 []string{p, `ExportedInterface.ExportedMethod`}, 347 []string{ 348 `Comment before exported method.*\n.*ExportedMethod\(\)` + 349 `.*Comment on line with exported method`, 350 }, 351 []string{ 352 `Comment about exported interface.`, 353 }, 354 }, 355 356 // Method. 357 { 358 "method", 359 []string{p, `ExportedType.ExportedMethod`}, 360 []string{ 361 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 362 `Comment about exported method.`, 363 }, 364 nil, 365 }, 366 // Method with -u. 367 { 368 "method with -u", 369 []string{"-u", p, `ExportedType.unexportedMethod`}, 370 []string{ 371 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 372 `Comment about unexported method.`, 373 }, 374 nil, 375 }, 376 377 // Case matching off. 378 { 379 "case matching off", 380 []string{p, `casematch`}, 381 []string{ 382 `CaseMatch`, 383 `Casematch`, 384 }, 385 nil, 386 }, 387 388 // Case matching on. 389 { 390 "case matching on", 391 []string{"-c", p, `Casematch`}, 392 []string{ 393 `Casematch`, 394 }, 395 []string{ 396 `CaseMatch`, 397 }, 398 }, 399 } 400 401 func TestDoc(t *testing.T) { 402 maybeSkip(t) 403 for _, test := range tests { 404 var b bytes.Buffer 405 var flagSet flag.FlagSet 406 err := do(&b, &flagSet, test.args) 407 if err != nil { 408 t.Fatalf("%s: %s\n", test.name, err) 409 } 410 output := b.Bytes() 411 failed := false 412 for j, yes := range test.yes { 413 re, err := regexp.Compile(yes) 414 if err != nil { 415 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err) 416 } 417 if !re.Match(output) { 418 t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes) 419 failed = true 420 } 421 } 422 for j, no := range test.no { 423 re, err := regexp.Compile(no) 424 if err != nil { 425 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err) 426 } 427 if re.Match(output) { 428 t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no) 429 failed = true 430 } 431 } 432 if failed { 433 t.Logf("\n%s", output) 434 } 435 } 436 } 437 438 // Test the code to try multiple packages. Our test case is 439 // go doc rand.Float64 440 // This needs to find math/rand.Float64; however crypto/rand, which doesn't 441 // have the symbol, usually appears first in the directory listing. 442 func TestMultiplePackages(t *testing.T) { 443 if testing.Short() { 444 t.Skip("scanning file system takes too long") 445 } 446 maybeSkip(t) 447 var b bytes.Buffer // We don't care about the output. 448 // Make sure crypto/rand does not have the symbol. 449 { 450 var flagSet flag.FlagSet 451 err := do(&b, &flagSet, []string{"crypto/rand.float64"}) 452 if err == nil { 453 t.Errorf("expected error from crypto/rand.float64") 454 } else if !strings.Contains(err.Error(), "no symbol float64") { 455 t.Errorf("unexpected error %q from crypto/rand.float64", err) 456 } 457 } 458 // Make sure math/rand does have the symbol. 459 { 460 var flagSet flag.FlagSet 461 err := do(&b, &flagSet, []string{"math/rand.float64"}) 462 if err != nil { 463 t.Errorf("unexpected error %q from math/rand.float64", err) 464 } 465 } 466 // Try the shorthand. 467 { 468 var flagSet flag.FlagSet 469 err := do(&b, &flagSet, []string{"rand.float64"}) 470 if err != nil { 471 t.Errorf("unexpected error %q from rand.float64", err) 472 } 473 } 474 // Now try a missing symbol. We should see both packages in the error. 475 { 476 var flagSet flag.FlagSet 477 err := do(&b, &flagSet, []string{"rand.doesnotexit"}) 478 if err == nil { 479 t.Errorf("expected error from rand.doesnotexit") 480 } else { 481 errStr := err.Error() 482 if !strings.Contains(errStr, "no symbol") { 483 t.Errorf("error %q should contain 'no symbol", errStr) 484 } 485 if !strings.Contains(errStr, "crypto/rand") { 486 t.Errorf("error %q should contain crypto/rand", errStr) 487 } 488 if !strings.Contains(errStr, "math/rand") { 489 t.Errorf("error %q should contain math/rand", errStr) 490 } 491 } 492 } 493 } 494 495 type trimTest struct { 496 path string 497 prefix string 498 result string 499 ok bool 500 } 501 502 var trimTests = []trimTest{ 503 {"", "", "", true}, 504 {"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, 505 {"/usr/gopher/bar", "/usr/gopher", "bar", true}, 506 {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false}, 507 {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false}, 508 } 509 510 func TestTrim(t *testing.T) { 511 for _, test := range trimTests { 512 result, ok := trim(test.path, test.prefix) 513 if ok != test.ok { 514 t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok) 515 continue 516 } 517 if result != test.result { 518 t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result) 519 continue 520 } 521 } 522 }