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  }