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