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