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 "os" 11 "os/exec" 12 "regexp" 13 "runtime" 14 "testing" 15 ) 16 17 const ( 18 dataDir = "testdata" 19 binary = "testdoc" 20 ) 21 22 type test struct { 23 name string 24 args []string // Arguments to "[go] doc". 25 yes []string // Regular expressions that should match. 26 no []string // Regular expressions that should not match. 27 } 28 29 const p = "cmd/doc/testdata" 30 31 var tests = []test{ 32 // Sanity check. 33 { 34 "sanity check", 35 []string{p}, 36 []string{`type ExportedType struct`}, 37 nil, 38 }, 39 40 // Package dump includes import, package statement. 41 { 42 "package clause", 43 []string{p}, 44 []string{`package pkg.*cmd/doc/testdata`}, 45 nil, 46 }, 47 48 // Constants. 49 // Package dump 50 { 51 "full package", 52 []string{p}, 53 []string{ 54 `Package comment`, 55 `const ExportedConstant = 1`, // Simple constant. 56 `const ConstOne = 1`, // First entry in constant block. 57 `var ExportedVariable = 1`, // Simple variable. 58 `var VarOne = 1`, // First entry in variable block. 59 `func ExportedFunc\(a int\) bool`, // Function. 60 `type ExportedType struct { ... }`, // Exported type. 61 `const ExportedTypedConstant ExportedType = iota`, // Typed constant. 62 `const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type. 63 }, 64 []string{ 65 `const internalConstant = 2`, // No internal constants. 66 `var internalVariable = 2`, // No internal variables. 67 `func internalFunc(a int) bool`, // No internal functions. 68 `Comment about exported constant`, // No comment for single constant. 69 `Comment about exported variable`, // No comment for single variable. 70 `Comment about block of constants.`, // No comment for constant block. 71 `Comment about block of variables.`, // No comment for variable block. 72 `Comment before ConstOne`, // No comment for first entry in constant block. 73 `Comment before VarOne`, // No comment for first entry in variable block. 74 `ConstTwo = 2`, // No second entry in constant block. 75 `VarTwo = 2`, // No second entry in variable block. 76 `type unexportedType`, // No unexported type. 77 `unexportedTypedConstant`, // No unexported typed constant. 78 `Field`, // No fields. 79 `Method`, // No methods. 80 }, 81 }, 82 // Package dump -u 83 { 84 "full package with u", 85 []string{`-u`, p}, 86 []string{ 87 `const ExportedConstant = 1`, // Simple constant. 88 `const internalConstant = 2`, // Internal constants. 89 `func internalFunc\(a int\) bool`, // Internal functions. 90 }, 91 []string{ 92 `Comment about exported constant`, // No comment for simple constant. 93 `Comment about block of constants`, // No comment for constant block. 94 `Comment about internal function`, // No comment for internal function. 95 }, 96 }, 97 98 // Single constant. 99 { 100 "single constant", 101 []string{p, `ExportedConstant`}, 102 []string{ 103 `Comment about exported constant`, // Include comment. 104 `const ExportedConstant = 1`, 105 }, 106 nil, 107 }, 108 // Single constant -u. 109 { 110 "single constant with -u", 111 []string{`-u`, p, `internalConstant`}, 112 []string{ 113 `Comment about internal constant`, // Include comment. 114 `const internalConstant = 2`, 115 }, 116 nil, 117 }, 118 // Block of constants. 119 { 120 "block of constants", 121 []string{p, `ConstTwo`}, 122 []string{ 123 `Comment before ConstOne.\n.*ConstOne = 1`, // First... 124 `ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up. 125 `Comment about block of constants`, // Comment does too. 126 }, 127 []string{ 128 `constThree`, // No unexported constant. 129 }, 130 }, 131 // Block of constants -u. 132 { 133 "block of constants with -u", 134 []string{"-u", p, `constThree`}, 135 []string{ 136 `constThree = 3.*Comment on line with constThree`, 137 }, 138 nil, 139 }, 140 141 // Single variable. 142 { 143 "single variable", 144 []string{p, `ExportedVariable`}, 145 []string{ 146 `ExportedVariable`, // Include comment. 147 `var ExportedVariable = 1`, 148 }, 149 nil, 150 }, 151 // Single variable -u. 152 { 153 "single variable with -u", 154 []string{`-u`, p, `internalVariable`}, 155 []string{ 156 `Comment about internal variable`, // Include comment. 157 `var internalVariable = 2`, 158 }, 159 nil, 160 }, 161 // Block of variables. 162 { 163 "block of variables", 164 []string{p, `VarTwo`}, 165 []string{ 166 `Comment before VarOne.\n.*VarOne = 1`, // First... 167 `VarTwo = 2.*Comment on line with VarTwo`, // And second show up. 168 `Comment about block of variables`, // Comment does too. 169 }, 170 []string{ 171 `varThree= 3`, // No unexported variable. 172 }, 173 }, 174 // Block of variables -u. 175 { 176 "block of variables with -u", 177 []string{"-u", p, `varThree`}, 178 []string{ 179 `varThree = 3.*Comment on line with varThree`, 180 }, 181 nil, 182 }, 183 184 // Function. 185 { 186 "function", 187 []string{p, `ExportedFunc`}, 188 []string{ 189 `Comment about exported function`, // Include comment. 190 `func ExportedFunc\(a int\) bool`, 191 }, 192 nil, 193 }, 194 // Function -u. 195 { 196 "function with -u", 197 []string{"-u", p, `internalFunc`}, 198 []string{ 199 `Comment about internal function`, // Include comment. 200 `func internalFunc\(a int\) bool`, 201 }, 202 nil, 203 }, 204 205 // Type. 206 { 207 "type", 208 []string{p, `ExportedType`}, 209 []string{ 210 `Comment about exported type`, // Include comment. 211 `type ExportedType struct`, // Type definition. 212 `Comment before exported field.*\n.*ExportedField +int`, 213 `Has unexported fields`, 214 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 215 `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. 216 `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor. 217 }, 218 []string{ 219 `unexportedField`, // No unexported field. 220 `Comment about exported method.`, // No comment about exported method. 221 `unexportedMethod`, // No unexported method. 222 `unexportedTypedConstant`, // No unexported constant. 223 }, 224 }, 225 // Type -u with unexported fields. 226 { 227 "type with unexported fields and -u", 228 []string{"-u", p, `ExportedType`}, 229 []string{ 230 `Comment about exported type`, // Include comment. 231 `type ExportedType struct`, // Type definition. 232 `Comment before exported field.*\n.*ExportedField +int`, 233 `unexportedField int.*Comment on line with unexported field.`, 234 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 235 `unexportedTypedConstant`, 236 }, 237 []string{ 238 `Has unexported fields`, 239 }, 240 }, 241 // Unexported type with -u. 242 { 243 "unexported type with -u", 244 []string{"-u", p, `unexportedType`}, 245 []string{ 246 `Comment about unexported type`, // Include comment. 247 `type unexportedType int`, // Type definition. 248 `func \(unexportedType\) ExportedMethod\(\) bool`, 249 `func \(unexportedType\) unexportedMethod\(\) bool`, 250 `ExportedTypedConstant_unexported unexportedType = iota`, 251 `const unexportedTypedConstant unexportedType = 1`, 252 }, 253 nil, 254 }, 255 256 // Method. 257 { 258 "method", 259 []string{p, `ExportedType.ExportedMethod`}, 260 []string{ 261 `func \(ExportedType\) ExportedMethod\(a int\) bool`, 262 `Comment about exported method.`, 263 }, 264 nil, 265 }, 266 // Method with -u. 267 { 268 "method with -u", 269 []string{"-u", p, `ExportedType.unexportedMethod`}, 270 []string{ 271 `func \(ExportedType\) unexportedMethod\(a int\) bool`, 272 `Comment about unexported method.`, 273 }, 274 nil, 275 }, 276 277 // Case matching off. 278 { 279 "case matching off", 280 []string{p, `casematch`}, 281 []string{ 282 `CaseMatch`, 283 `Casematch`, 284 }, 285 nil, 286 }, 287 288 // Case matching on. 289 { 290 "case matching on", 291 []string{"-c", p, `Casematch`}, 292 []string{ 293 `Casematch`, 294 }, 295 []string{ 296 `CaseMatch`, 297 }, 298 }, 299 } 300 301 func TestDoc(t *testing.T) { 302 if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { 303 t.Skip("TODO: on darwin/arm, test fails: no such package cmd/doc/testdata") 304 } 305 for _, test := range tests { 306 var b bytes.Buffer 307 var flagSet flag.FlagSet 308 err := do(&b, &flagSet, test.args) 309 if err != nil { 310 t.Fatalf("%s: %s\n", test.name, err) 311 } 312 output := b.Bytes() 313 failed := false 314 for j, yes := range test.yes { 315 re, err := regexp.Compile(yes) 316 if err != nil { 317 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err) 318 } 319 if !re.Match(output) { 320 t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes) 321 failed = true 322 } 323 } 324 for j, no := range test.no { 325 re, err := regexp.Compile(no) 326 if err != nil { 327 t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err) 328 } 329 if re.Match(output) { 330 t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no) 331 failed = true 332 } 333 } 334 if failed { 335 t.Logf("\n%s", output) 336 } 337 } 338 } 339 340 // run runs the command, but calls t.Fatal if there is an error. 341 func run(c *exec.Cmd, t *testing.T) []byte { 342 output, err := c.CombinedOutput() 343 if err != nil { 344 os.Stdout.Write(output) 345 t.Fatal(err) 346 } 347 return output 348 } 349