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