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