Home | History | Annotate | Download | only in doc
      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