Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/doc/doc_test.go

Documentation: cmd/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  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func TestMain(m *testing.M) {
    19  	// Clear GOPATH so we don't access the user's own packages in the test.
    20  	buildCtx.GOPATH = ""
    21  	testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
    22  
    23  	// Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test.
    24  	// Normally testdata directories are ignored, but sending it to dirs.scan directly is
    25  	// a hack that works around the check.
    26  	testdataDir, err := filepath.Abs("testdata")
    27  	if err != nil {
    28  		panic(err)
    29  	}
    30  	dirsInit(
    31  		Dir{importPath: "testdata", dir: testdataDir},
    32  		Dir{importPath: "testdata/nested", dir: filepath.Join(testdataDir, "nested")},
    33  		Dir{importPath: "testdata/nested/nested", dir: filepath.Join(testdataDir, "nested", "nested")})
    34  
    35  	os.Exit(m.Run())
    36  }
    37  
    38  func maybeSkip(t *testing.T) {
    39  	if runtime.GOOS == "ios" {
    40  		t.Skip("iOS does not have a full file tree")
    41  	}
    42  }
    43  
    44  type isDotSlashTest struct {
    45  	str    string
    46  	result bool
    47  }
    48  
    49  var isDotSlashTests = []isDotSlashTest{
    50  	{``, false},
    51  	{`x`, false},
    52  	{`...`, false},
    53  	{`.../`, false},
    54  	{`...\`, false},
    55  
    56  	{`.`, true},
    57  	{`./`, true},
    58  	{`.\`, true},
    59  	{`./x`, true},
    60  	{`.\x`, true},
    61  
    62  	{`..`, true},
    63  	{`../`, true},
    64  	{`..\`, true},
    65  	{`../x`, true},
    66  	{`..\x`, true},
    67  }
    68  
    69  func TestIsDotSlashPath(t *testing.T) {
    70  	for _, test := range isDotSlashTests {
    71  		if result := isDotSlash(test.str); result != test.result {
    72  			t.Errorf("isDotSlash(%q) = %t; expected %t", test.str, result, test.result)
    73  		}
    74  	}
    75  }
    76  
    77  type test struct {
    78  	name string
    79  	args []string // Arguments to "[go] doc".
    80  	yes  []string // Regular expressions that should match.
    81  	no   []string // Regular expressions that should not match.
    82  }
    83  
    84  const p = "cmd/doc/testdata"
    85  
    86  var tests = []test{
    87  	// Sanity check.
    88  	{
    89  		"sanity check",
    90  		[]string{p},
    91  		[]string{`type ExportedType struct`},
    92  		nil,
    93  	},
    94  
    95  	// Package dump includes import, package statement.
    96  	{
    97  		"package clause",
    98  		[]string{p},
    99  		[]string{`package pkg.*cmd/doc/testdata`},
   100  		nil,
   101  	},
   102  
   103  	// Constants.
   104  	// Package dump
   105  	{
   106  		"full package",
   107  		[]string{p},
   108  		[]string{
   109  			`Package comment`,
   110  			`const ExportedConstant = 1`,                                   // Simple constant.
   111  			`const ConstOne = 1`,                                           // First entry in constant block.
   112  			`const ConstFive ...`,                                          // From block starting with unexported constant.
   113  			`var ExportedVariable = 1`,                                     // Simple variable.
   114  			`var VarOne = 1`,                                               // First entry in variable block.
   115  			`func ExportedFunc\(a int\) bool`,                              // Function.
   116  			`func ReturnUnexported\(\) unexportedType`,                     // Function with unexported return type.
   117  			`type ExportedType struct{ ... }`,                              // Exported type.
   118  			`const ExportedTypedConstant ExportedType = iota`,              // Typed constant.
   119  			`const ExportedTypedConstant_unexported unexportedType`,        // Typed constant, exported for unexported type.
   120  			`const ConstLeft2 uint64 ...`,                                  // Typed constant using unexported iota.
   121  			`const ConstGroup1 unexportedType = iota ...`,                  // Typed constant using unexported type.
   122  			`const ConstGroup4 ExportedType = ExportedType{}`,              // Typed constant using exported type.
   123  			`const MultiLineConst = ...`,                                   // Multi line constant.
   124  			`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`,  // Multi line variable.
   125  			`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
   126  			`var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`,  // Long list of arguments.
   127  			`type T1 = T2`,                                                 // Type alias
   128  		},
   129  		[]string{
   130  			`const internalConstant = 2`,       // No internal constants.
   131  			`var internalVariable = 2`,         // No internal variables.
   132  			`func internalFunc(a int) bool`,    // No internal functions.
   133  			`Comment about exported constant`,  // No comment for single constant.
   134  			`Comment about exported variable`,  // No comment for single variable.
   135  			`Comment about block of constants`, // No comment for constant block.
   136  			`Comment about block of variables`, // No comment for variable block.
   137  			`Comment before ConstOne`,          // No comment for first entry in constant block.
   138  			`Comment before VarOne`,            // No comment for first entry in variable block.
   139  			`ConstTwo = 2`,                     // No second entry in constant block.
   140  			`VarTwo = 2`,                       // No second entry in variable block.
   141  			`VarFive = 5`,                      // From block starting with unexported variable.
   142  			`type unexportedType`,              // No unexported type.
   143  			`unexportedTypedConstant`,          // No unexported typed constant.
   144  			`\bField`,                          // No fields.
   145  			`Method`,                           // No methods.
   146  			`someArgument[5-8]`,                // No truncated arguments.
   147  			`type T1 T2`,                       // Type alias does not display as type declaration.
   148  		},
   149  	},
   150  	// Package dump -all
   151  	{
   152  		"full package",
   153  		[]string{"-all", p},
   154  		[]string{
   155  			`package pkg .*import`,
   156  			`Package comment`,
   157  			`CONSTANTS`,
   158  			`Comment before ConstOne`,
   159  			`ConstOne = 1`,
   160  			`ConstTwo = 2 // Comment on line with ConstTwo`,
   161  			`ConstFive`,
   162  			`ConstSix`,
   163  			`Const block where first entry is unexported`,
   164  			`ConstLeft2, constRight2 uint64`,
   165  			`constLeft3, ConstRight3`,
   166  			`ConstLeft4, ConstRight4`,
   167  			`Duplicate = iota`,
   168  			`const CaseMatch = 1`,
   169  			`const Casematch = 2`,
   170  			`const ExportedConstant = 1`,
   171  			`const MultiLineConst = `,
   172  			`MultiLineString1`,
   173  			`VARIABLES`,
   174  			`Comment before VarOne`,
   175  			`VarOne = 1`,
   176  			`Comment about block of variables`,
   177  			`VarFive = 5`,
   178  			`var ExportedVariable = 1`,
   179  			`var ExportedVarOfUnExported unexportedType`,
   180  			`var LongLine = newLongLine\(`,
   181  			`var MultiLineVar = map\[struct {`,
   182  			`FUNCTIONS`,
   183  			`func ExportedFunc\(a int\) bool`,
   184  			`Comment about exported function`,
   185  			`func MultiLineFunc\(x interface`,
   186  			`func ReturnUnexported\(\) unexportedType`,
   187  			`TYPES`,
   188  			`type ExportedInterface interface`,
   189  			`type ExportedStructOneField struct`,
   190  			`type ExportedType struct`,
   191  			`Comment about exported type`,
   192  			`const ConstGroup4 ExportedType = ExportedType`,
   193  			`ExportedTypedConstant ExportedType = iota`,
   194  			`Constants tied to ExportedType`,
   195  			`func ExportedTypeConstructor\(\) \*ExportedType`,
   196  			`Comment about constructor for exported type`,
   197  			`func ReturnExported\(\) ExportedType`,
   198  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   199  			`Comment about exported method`,
   200  			`type T1 = T2`,
   201  			`type T2 int`,
   202  		},
   203  		[]string{
   204  			`constThree`,
   205  			`_, _ uint64 = 2 \* iota, 1 << iota`,
   206  			`constLeft1, constRight1`,
   207  			`duplicate`,
   208  			`varFour`,
   209  			`func internalFunc`,
   210  			`unexportedField`,
   211  			`func \(unexportedType\)`,
   212  		},
   213  	},
   214  	// Package with just the package declaration. Issue 31457.
   215  	{
   216  		"only package declaration",
   217  		[]string{"-all", p + "/nested/empty"},
   218  		[]string{`package empty .*import`},
   219  		nil,
   220  	},
   221  	// Package dump -short
   222  	{
   223  		"full package with -short",
   224  		[]string{`-short`, p},
   225  		[]string{
   226  			`const ExportedConstant = 1`,               // Simple constant.
   227  			`func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
   228  		},
   229  		[]string{
   230  			`MultiLine(String|Method|Field)`, // No data from multi line portions.
   231  		},
   232  	},
   233  	// Package dump -u
   234  	{
   235  		"full package with u",
   236  		[]string{`-u`, p},
   237  		[]string{
   238  			`const ExportedConstant = 1`,               // Simple constant.
   239  			`const internalConstant = 2`,               // Internal constants.
   240  			`func internalFunc\(a int\) bool`,          // Internal functions.
   241  			`func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
   242  		},
   243  		[]string{
   244  			`Comment about exported constant`,  // No comment for simple constant.
   245  			`Comment about block of constants`, // No comment for constant block.
   246  			`Comment about internal function`,  // No comment for internal function.
   247  			`MultiLine(String|Method|Field)`,   // No data from multi line portions.
   248  		},
   249  	},
   250  	// Package dump -u -all
   251  	{
   252  		"full package",
   253  		[]string{"-u", "-all", p},
   254  		[]string{
   255  			`package pkg .*import`,
   256  			`Package comment`,
   257  			`CONSTANTS`,
   258  			`Comment before ConstOne`,
   259  			`ConstOne += 1`,
   260  			`ConstTwo += 2 // Comment on line with ConstTwo`,
   261  			`constThree = 3 // Comment on line with constThree`,
   262  			`ConstFive`,
   263  			`const internalConstant += 2`,
   264  			`Comment about internal constant`,
   265  			`VARIABLES`,
   266  			`Comment before VarOne`,
   267  			`VarOne += 1`,
   268  			`Comment about block of variables`,
   269  			`varFour += 4`,
   270  			`VarFive += 5`,
   271  			`varSix += 6`,
   272  			`var ExportedVariable = 1`,
   273  			`var LongLine = newLongLine\(`,
   274  			`var MultiLineVar = map\[struct {`,
   275  			`var internalVariable = 2`,
   276  			`Comment about internal variable`,
   277  			`FUNCTIONS`,
   278  			`func ExportedFunc\(a int\) bool`,
   279  			`Comment about exported function`,
   280  			`func MultiLineFunc\(x interface`,
   281  			`func internalFunc\(a int\) bool`,
   282  			`Comment about internal function`,
   283  			`func newLongLine\(ss .*string\)`,
   284  			`TYPES`,
   285  			`type ExportedType struct`,
   286  			`type T1 = T2`,
   287  			`type T2 int`,
   288  			`type unexportedType int`,
   289  			`Comment about unexported type`,
   290  			`ConstGroup1 unexportedType = iota`,
   291  			`ConstGroup2`,
   292  			`ConstGroup3`,
   293  			`ExportedTypedConstant_unexported unexportedType = iota`,
   294  			`Constants tied to unexportedType`,
   295  			`const unexportedTypedConstant unexportedType = 1`,
   296  			`func ReturnUnexported\(\) unexportedType`,
   297  			`func \(unexportedType\) ExportedMethod\(\) bool`,
   298  			`func \(unexportedType\) unexportedMethod\(\) bool`,
   299  		},
   300  		nil,
   301  	},
   302  
   303  	// Single constant.
   304  	{
   305  		"single constant",
   306  		[]string{p, `ExportedConstant`},
   307  		[]string{
   308  			`Comment about exported constant`, // Include comment.
   309  			`const ExportedConstant = 1`,
   310  		},
   311  		nil,
   312  	},
   313  	// Single constant -u.
   314  	{
   315  		"single constant with -u",
   316  		[]string{`-u`, p, `internalConstant`},
   317  		[]string{
   318  			`Comment about internal constant`, // Include comment.
   319  			`const internalConstant = 2`,
   320  		},
   321  		nil,
   322  	},
   323  	// Block of constants.
   324  	{
   325  		"block of constants",
   326  		[]string{p, `ConstTwo`},
   327  		[]string{
   328  			`Comment before ConstOne.\n.*ConstOne = 1`,    // First...
   329  			`ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up.
   330  			`Comment about block of constants`,            // Comment does too.
   331  		},
   332  		[]string{
   333  			`constThree`, // No unexported constant.
   334  		},
   335  	},
   336  	// Block of constants -u.
   337  	{
   338  		"block of constants with -u",
   339  		[]string{"-u", p, `constThree`},
   340  		[]string{
   341  			`constThree = 3.*Comment on line with constThree`,
   342  		},
   343  		nil,
   344  	},
   345  	// Block of constants -src.
   346  	{
   347  		"block of constants with -src",
   348  		[]string{"-src", p, `ConstTwo`},
   349  		[]string{
   350  			`Comment about block of constants`, // Top comment.
   351  			`ConstOne.*=.*1`,                   // Each constant seen.
   352  			`ConstTwo.*=.*2.*Comment on line with ConstTwo`,
   353  			`constThree`, // Even unexported constants.
   354  		},
   355  		nil,
   356  	},
   357  	// Block of constants with carryover type from unexported field.
   358  	{
   359  		"block of constants with carryover type",
   360  		[]string{p, `ConstLeft2`},
   361  		[]string{
   362  			`ConstLeft2, constRight2 uint64`,
   363  			`constLeft3, ConstRight3`,
   364  			`ConstLeft4, ConstRight4`,
   365  		},
   366  		nil,
   367  	},
   368  	// Block of constants -u with carryover type from unexported field.
   369  	{
   370  		"block of constants with carryover type",
   371  		[]string{"-u", p, `ConstLeft2`},
   372  		[]string{
   373  			`_, _ uint64 = 2 \* iota, 1 << iota`,
   374  			`constLeft1, constRight1`,
   375  			`ConstLeft2, constRight2`,
   376  			`constLeft3, ConstRight3`,
   377  			`ConstLeft4, ConstRight4`,
   378  		},
   379  		nil,
   380  	},
   381  
   382  	// Single variable.
   383  	{
   384  		"single variable",
   385  		[]string{p, `ExportedVariable`},
   386  		[]string{
   387  			`ExportedVariable`, // Include comment.
   388  			`var ExportedVariable = 1`,
   389  		},
   390  		nil,
   391  	},
   392  	// Single variable -u.
   393  	{
   394  		"single variable with -u",
   395  		[]string{`-u`, p, `internalVariable`},
   396  		[]string{
   397  			`Comment about internal variable`, // Include comment.
   398  			`var internalVariable = 2`,
   399  		},
   400  		nil,
   401  	},
   402  	// Block of variables.
   403  	{
   404  		"block of variables",
   405  		[]string{p, `VarTwo`},
   406  		[]string{
   407  			`Comment before VarOne.\n.*VarOne = 1`,    // First...
   408  			`VarTwo = 2.*Comment on line with VarTwo`, // And second show up.
   409  			`Comment about block of variables`,        // Comment does too.
   410  		},
   411  		[]string{
   412  			`varThree= 3`, // No unexported variable.
   413  		},
   414  	},
   415  	// Block of variables -u.
   416  	{
   417  		"block of variables with -u",
   418  		[]string{"-u", p, `varThree`},
   419  		[]string{
   420  			`varThree = 3.*Comment on line with varThree`,
   421  		},
   422  		nil,
   423  	},
   424  
   425  	// Function.
   426  	{
   427  		"function",
   428  		[]string{p, `ExportedFunc`},
   429  		[]string{
   430  			`Comment about exported function`, // Include comment.
   431  			`func ExportedFunc\(a int\) bool`,
   432  		},
   433  		nil,
   434  	},
   435  	// Function -u.
   436  	{
   437  		"function with -u",
   438  		[]string{"-u", p, `internalFunc`},
   439  		[]string{
   440  			`Comment about internal function`, // Include comment.
   441  			`func internalFunc\(a int\) bool`,
   442  		},
   443  		nil,
   444  	},
   445  	// Function with -src.
   446  	{
   447  		"function with -src",
   448  		[]string{"-src", p, `ExportedFunc`},
   449  		[]string{
   450  			`Comment about exported function`, // Include comment.
   451  			`func ExportedFunc\(a int\) bool`,
   452  			`return true != false`, // Include body.
   453  		},
   454  		nil,
   455  	},
   456  
   457  	// Type.
   458  	{
   459  		"type",
   460  		[]string{p, `ExportedType`},
   461  		[]string{
   462  			`Comment about exported type`, // Include comment.
   463  			`type ExportedType struct`,    // Type definition.
   464  			`Comment before exported field.*\n.*ExportedField +int` +
   465  				`.*Comment on line with exported field`,
   466  			`ExportedEmbeddedType.*Comment on line with exported embedded field`,
   467  			`Has unexported fields`,
   468  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   469  			`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
   470  			`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
   471  			`io.Reader.*Comment on line with embedded Reader`,
   472  		},
   473  		[]string{
   474  			`unexportedField`,               // No unexported field.
   475  			`int.*embedded`,                 // No unexported embedded field.
   476  			`Comment about exported method`, // No comment about exported method.
   477  			`unexportedMethod`,              // No unexported method.
   478  			`unexportedTypedConstant`,       // No unexported constant.
   479  			`error`,                         // No embedded error.
   480  		},
   481  	},
   482  	// Type with -src. Will see unexported fields.
   483  	{
   484  		"type",
   485  		[]string{"-src", p, `ExportedType`},
   486  		[]string{
   487  			`Comment about exported type`, // Include comment.
   488  			`type ExportedType struct`,    // Type definition.
   489  			`Comment before exported field`,
   490  			`ExportedField.*Comment on line with exported field`,
   491  			`ExportedEmbeddedType.*Comment on line with exported embedded field`,
   492  			`unexportedType.*Comment on line with unexported embedded field`,
   493  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   494  			`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
   495  			`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
   496  			`io.Reader.*Comment on line with embedded Reader`,
   497  		},
   498  		[]string{
   499  			`Comment about exported method`, // No comment about exported method.
   500  			`unexportedMethod`,              // No unexported method.
   501  			`unexportedTypedConstant`,       // No unexported constant.
   502  		},
   503  	},
   504  	// Type -all.
   505  	{
   506  		"type",
   507  		[]string{"-all", p, `ExportedType`},
   508  		[]string{
   509  			`type ExportedType struct {`,                        // Type definition as source.
   510  			`Comment about exported type`,                       // Include comment afterwards.
   511  			`const ConstGroup4 ExportedType = ExportedType\{\}`, // Related constants.
   512  			`ExportedTypedConstant ExportedType = iota`,
   513  			`Constants tied to ExportedType`,
   514  			`func ExportedTypeConstructor\(\) \*ExportedType`,
   515  			`Comment about constructor for exported type.`,
   516  			`func ReturnExported\(\) ExportedType`,
   517  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   518  			`Comment about exported method.`,
   519  			`func \(ExportedType\) Uncommented\(a int\) bool\n\n`, // Ensure line gap after method with no comment
   520  		},
   521  		[]string{
   522  			`unexportedType`,
   523  		},
   524  	},
   525  	// Type T1 dump (alias).
   526  	{
   527  		"type T1",
   528  		[]string{p + ".T1"},
   529  		[]string{
   530  			`type T1 = T2`,
   531  		},
   532  		[]string{
   533  			`type T1 T2`,
   534  			`type ExportedType`,
   535  		},
   536  	},
   537  	// Type -u with unexported fields.
   538  	{
   539  		"type with unexported fields and -u",
   540  		[]string{"-u", p, `ExportedType`},
   541  		[]string{
   542  			`Comment about exported type`, // Include comment.
   543  			`type ExportedType struct`,    // Type definition.
   544  			`Comment before exported field.*\n.*ExportedField +int`,
   545  			`unexportedField.*int.*Comment on line with unexported field`,
   546  			`ExportedEmbeddedType.*Comment on line with exported embedded field`,
   547  			`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field`,
   548  			`\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field`,
   549  			`unexportedType.*Comment on line with unexported embedded field`,
   550  			`\*unexportedType.*Comment on line with unexported embedded \*field`,
   551  			`io.Reader.*Comment on line with embedded Reader`,
   552  			`error.*Comment on line with embedded error`,
   553  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   554  			`unexportedTypedConstant`,
   555  		},
   556  		[]string{
   557  			`Has unexported fields`,
   558  		},
   559  	},
   560  	// Unexported type with -u.
   561  	{
   562  		"unexported type with -u",
   563  		[]string{"-u", p, `unexportedType`},
   564  		[]string{
   565  			`Comment about unexported type`, // Include comment.
   566  			`type unexportedType int`,       // Type definition.
   567  			`func \(unexportedType\) ExportedMethod\(\) bool`,
   568  			`func \(unexportedType\) unexportedMethod\(\) bool`,
   569  			`ExportedTypedConstant_unexported unexportedType = iota`,
   570  			`const unexportedTypedConstant unexportedType = 1`,
   571  		},
   572  		nil,
   573  	},
   574  
   575  	// Interface.
   576  	{
   577  		"interface type",
   578  		[]string{p, `ExportedInterface`},
   579  		[]string{
   580  			`Comment about exported interface`, // Include comment.
   581  			`type ExportedInterface interface`, // Interface definition.
   582  			`Comment before exported method.\n.*//\n.*//	// Code block showing how to use ExportedMethod\n.*//	func DoSomething\(\) error {\n.*//		ExportedMethod\(\)\n.*//		return nil\n.*//	}\n.*//.*\n.*ExportedMethod\(\)` +
   583  				`.*Comment on line with exported method`,
   584  			`io.Reader.*Comment on line with embedded Reader`,
   585  			`error.*Comment on line with embedded error`,
   586  			`Has unexported methods`,
   587  		},
   588  		[]string{
   589  			`unexportedField`,               // No unexported field.
   590  			`Comment about exported method`, // No comment about exported method.
   591  			`unexportedMethod`,              // No unexported method.
   592  			`unexportedTypedConstant`,       // No unexported constant.
   593  		},
   594  	},
   595  	// Interface -u with unexported methods.
   596  	{
   597  		"interface type with unexported methods and -u",
   598  		[]string{"-u", p, `ExportedInterface`},
   599  		[]string{
   600  			`Comment about exported interface`, // Include comment.
   601  			`type ExportedInterface interface`, // Interface definition.
   602  			`Comment before exported method.\n.*//\n.*//	// Code block showing how to use ExportedMethod\n.*//	func DoSomething\(\) error {\n.*//		ExportedMethod\(\)\n.*//		return nil\n.*//	}\n.*//.*\n.*ExportedMethod\(\)` + `.*Comment on line with exported method`,
   603  			`unexportedMethod\(\).*Comment on line with unexported method`,
   604  			`io.Reader.*Comment on line with embedded Reader`,
   605  			`error.*Comment on line with embedded error`,
   606  		},
   607  		[]string{
   608  			`Has unexported methods`,
   609  		},
   610  	},
   611  
   612  	// Interface method.
   613  	{
   614  		"interface method",
   615  		[]string{p, `ExportedInterface.ExportedMethod`},
   616  		[]string{
   617  			`Comment before exported method.\n.*//\n.*//	// Code block showing how to use ExportedMethod\n.*//	func DoSomething\(\) error {\n.*//		ExportedMethod\(\)\n.*//		return nil\n.*//	}\n.*//.*\n.*ExportedMethod\(\)` +
   618  				`.*Comment on line with exported method`,
   619  		},
   620  		[]string{
   621  			`Comment about exported interface`,
   622  		},
   623  	},
   624  	// Interface method at package level.
   625  	{
   626  		"interface method at package level",
   627  		[]string{p, `ExportedMethod`},
   628  		[]string{
   629  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   630  			`Comment about exported method`,
   631  		},
   632  		[]string{
   633  			`Comment before exported method.*\n.*ExportedMethod\(\)` +
   634  				`.*Comment on line with exported method`,
   635  		},
   636  	},
   637  
   638  	// Method.
   639  	{
   640  		"method",
   641  		[]string{p, `ExportedType.ExportedMethod`},
   642  		[]string{
   643  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   644  			`Comment about exported method`,
   645  		},
   646  		nil,
   647  	},
   648  	// Method  with -u.
   649  	{
   650  		"method with -u",
   651  		[]string{"-u", p, `ExportedType.unexportedMethod`},
   652  		[]string{
   653  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   654  			`Comment about unexported method`,
   655  		},
   656  		nil,
   657  	},
   658  	// Method with -src.
   659  	{
   660  		"method with -src",
   661  		[]string{"-src", p, `ExportedType.ExportedMethod`},
   662  		[]string{
   663  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   664  			`Comment about exported method`,
   665  			`return true != true`,
   666  		},
   667  		nil,
   668  	},
   669  
   670  	// Field.
   671  	{
   672  		"field",
   673  		[]string{p, `ExportedType.ExportedField`},
   674  		[]string{
   675  			`type ExportedType struct`,
   676  			`ExportedField int`,
   677  			`Comment before exported field`,
   678  			`Comment on line with exported field`,
   679  			`other fields elided`,
   680  		},
   681  		nil,
   682  	},
   683  
   684  	// Field with -u.
   685  	{
   686  		"method with -u",
   687  		[]string{"-u", p, `ExportedType.unexportedField`},
   688  		[]string{
   689  			`unexportedField int`,
   690  			`Comment on line with unexported field`,
   691  		},
   692  		nil,
   693  	},
   694  
   695  	// Field of struct with only one field.
   696  	{
   697  		"single-field struct",
   698  		[]string{p, `ExportedStructOneField.OnlyField`},
   699  		[]string{`the only field`},
   700  		[]string{`other fields elided`},
   701  	},
   702  
   703  	// Case matching off.
   704  	{
   705  		"case matching off",
   706  		[]string{p, `casematch`},
   707  		[]string{
   708  			`CaseMatch`,
   709  			`Casematch`,
   710  		},
   711  		nil,
   712  	},
   713  
   714  	// Case matching on.
   715  	{
   716  		"case matching on",
   717  		[]string{"-c", p, `Casematch`},
   718  		[]string{
   719  			`Casematch`,
   720  		},
   721  		[]string{
   722  			`CaseMatch`,
   723  		},
   724  	},
   725  
   726  	// Merging comments with -src.
   727  	{
   728  		"merge comments with -src A",
   729  		[]string{"-src", p + "/merge", `A`},
   730  		[]string{
   731  			`A doc`,
   732  			`func A`,
   733  			`A comment`,
   734  		},
   735  		[]string{
   736  			`Package A doc`,
   737  			`Package B doc`,
   738  			`B doc`,
   739  			`B comment`,
   740  			`B doc`,
   741  		},
   742  	},
   743  	{
   744  		"merge comments with -src B",
   745  		[]string{"-src", p + "/merge", `B`},
   746  		[]string{
   747  			`B doc`,
   748  			`func B`,
   749  			`B comment`,
   750  		},
   751  		[]string{
   752  			`Package A doc`,
   753  			`Package B doc`,
   754  			`A doc`,
   755  			`A comment`,
   756  			`A doc`,
   757  		},
   758  	},
   759  
   760  	// No dups with -u. Issue 21797.
   761  	{
   762  		"case matching on, no dups",
   763  		[]string{"-u", p, `duplicate`},
   764  		[]string{
   765  			`Duplicate`,
   766  			`duplicate`,
   767  		},
   768  		[]string{
   769  			"\\)\n+const", // This will appear if the const decl appears twice.
   770  		},
   771  	},
   772  	{
   773  		"non-imported: pkg.sym",
   774  		[]string{"nested.Foo"},
   775  		[]string{"Foo struct"},
   776  		nil,
   777  	},
   778  	{
   779  		"non-imported: pkg only",
   780  		[]string{"nested"},
   781  		[]string{"Foo struct"},
   782  		nil,
   783  	},
   784  	{
   785  		"non-imported: pkg sym",
   786  		[]string{"nested", "Foo"},
   787  		[]string{"Foo struct"},
   788  		nil,
   789  	},
   790  	{
   791  		"formatted doc on function",
   792  		[]string{p, "ExportedFormattedDoc"},
   793  		[]string{
   794  			`func ExportedFormattedDoc\(a int\) bool`,
   795  			`    Comment about exported function with formatting\.
   796  
   797      Example
   798  
   799          fmt\.Println\(FormattedDoc\(\)\)
   800  
   801      Text after pre-formatted block\.`,
   802  		},
   803  		nil,
   804  	},
   805  	{
   806  		"formatted doc on type field",
   807  		[]string{p, "ExportedFormattedType.ExportedField"},
   808  		[]string{
   809  			`type ExportedFormattedType struct`,
   810  			`    // Comment before exported field with formatting\.
   811      //[ ]
   812      // Example
   813      //[ ]
   814      //     a\.ExportedField = 123
   815      //[ ]
   816      // Text after pre-formatted block\.`,
   817  			`ExportedField int`,
   818  		},
   819  		nil,
   820  	},
   821  }
   822  
   823  func TestDoc(t *testing.T) {
   824  	maybeSkip(t)
   825  	for _, test := range tests {
   826  		var b bytes.Buffer
   827  		var flagSet flag.FlagSet
   828  		err := do(&b, &flagSet, test.args)
   829  		if err != nil {
   830  			t.Fatalf("%s %v: %s\n", test.name, test.args, err)
   831  		}
   832  		output := b.Bytes()
   833  		failed := false
   834  		for j, yes := range test.yes {
   835  			re, err := regexp.Compile(yes)
   836  			if err != nil {
   837  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
   838  			}
   839  			if !re.Match(output) {
   840  				t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
   841  				failed = true
   842  			}
   843  		}
   844  		for j, no := range test.no {
   845  			re, err := regexp.Compile(no)
   846  			if err != nil {
   847  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
   848  			}
   849  			if re.Match(output) {
   850  				t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
   851  				failed = true
   852  			}
   853  		}
   854  		if bytes.Count(output, []byte("TYPES\n")) > 1 {
   855  			t.Fatalf("%s: repeating headers", test.name)
   856  		}
   857  		if failed {
   858  			t.Logf("\n%s", output)
   859  		}
   860  	}
   861  }
   862  
   863  // Test the code to try multiple packages. Our test case is
   864  //	go doc rand.Float64
   865  // This needs to find math/rand.Float64; however crypto/rand, which doesn't
   866  // have the symbol, usually appears first in the directory listing.
   867  func TestMultiplePackages(t *testing.T) {
   868  	if testing.Short() {
   869  		t.Skip("scanning file system takes too long")
   870  	}
   871  	maybeSkip(t)
   872  	var b bytes.Buffer // We don't care about the output.
   873  	// Make sure crypto/rand does not have the symbol.
   874  	{
   875  		var flagSet flag.FlagSet
   876  		err := do(&b, &flagSet, []string{"crypto/rand.float64"})
   877  		if err == nil {
   878  			t.Errorf("expected error from crypto/rand.float64")
   879  		} else if !strings.Contains(err.Error(), "no symbol float64") {
   880  			t.Errorf("unexpected error %q from crypto/rand.float64", err)
   881  		}
   882  	}
   883  	// Make sure math/rand does have the symbol.
   884  	{
   885  		var flagSet flag.FlagSet
   886  		err := do(&b, &flagSet, []string{"math/rand.float64"})
   887  		if err != nil {
   888  			t.Errorf("unexpected error %q from math/rand.float64", err)
   889  		}
   890  	}
   891  	// Try the shorthand.
   892  	{
   893  		var flagSet flag.FlagSet
   894  		err := do(&b, &flagSet, []string{"rand.float64"})
   895  		if err != nil {
   896  			t.Errorf("unexpected error %q from rand.float64", err)
   897  		}
   898  	}
   899  	// Now try a missing symbol. We should see both packages in the error.
   900  	{
   901  		var flagSet flag.FlagSet
   902  		err := do(&b, &flagSet, []string{"rand.doesnotexit"})
   903  		if err == nil {
   904  			t.Errorf("expected error from rand.doesnotexit")
   905  		} else {
   906  			errStr := err.Error()
   907  			if !strings.Contains(errStr, "no symbol") {
   908  				t.Errorf("error %q should contain 'no symbol", errStr)
   909  			}
   910  			if !strings.Contains(errStr, "crypto/rand") {
   911  				t.Errorf("error %q should contain crypto/rand", errStr)
   912  			}
   913  			if !strings.Contains(errStr, "math/rand") {
   914  				t.Errorf("error %q should contain math/rand", errStr)
   915  			}
   916  		}
   917  	}
   918  }
   919  
   920  // Test the code to look up packages when given two args. First test case is
   921  //	go doc binary BigEndian
   922  // This needs to find encoding/binary.BigEndian, which means
   923  // finding the package encoding/binary given only "binary".
   924  // Second case is
   925  //	go doc rand Float64
   926  // which again needs to find math/rand and not give up after crypto/rand,
   927  // which has no such function.
   928  func TestTwoArgLookup(t *testing.T) {
   929  	if testing.Short() {
   930  		t.Skip("scanning file system takes too long")
   931  	}
   932  	maybeSkip(t)
   933  	var b bytes.Buffer // We don't care about the output.
   934  	{
   935  		var flagSet flag.FlagSet
   936  		err := do(&b, &flagSet, []string{"binary", "BigEndian"})
   937  		if err != nil {
   938  			t.Errorf("unexpected error %q from binary BigEndian", err)
   939  		}
   940  	}
   941  	{
   942  		var flagSet flag.FlagSet
   943  		err := do(&b, &flagSet, []string{"rand", "Float64"})
   944  		if err != nil {
   945  			t.Errorf("unexpected error %q from rand Float64", err)
   946  		}
   947  	}
   948  	{
   949  		var flagSet flag.FlagSet
   950  		err := do(&b, &flagSet, []string{"bytes", "Foo"})
   951  		if err == nil {
   952  			t.Errorf("expected error from bytes Foo")
   953  		} else if !strings.Contains(err.Error(), "no symbol Foo") {
   954  			t.Errorf("unexpected error %q from bytes Foo", err)
   955  		}
   956  	}
   957  	{
   958  		var flagSet flag.FlagSet
   959  		err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"})
   960  		if err == nil {
   961  			// actually present in the user's filesystem
   962  		} else if !strings.Contains(err.Error(), "no such package") {
   963  			t.Errorf("unexpected error %q from nosuchpackage Foo", err)
   964  		}
   965  	}
   966  }
   967  
   968  // Test the code to look up packages when the first argument starts with "./".
   969  // Our test case is in effect "cd src/text; doc ./template". This should get
   970  // text/template but before Issue 23383 was fixed would give html/template.
   971  func TestDotSlashLookup(t *testing.T) {
   972  	if testing.Short() {
   973  		t.Skip("scanning file system takes too long")
   974  	}
   975  	maybeSkip(t)
   976  	where, err := os.Getwd()
   977  	if err != nil {
   978  		t.Fatal(err)
   979  	}
   980  	defer func() {
   981  		if err := os.Chdir(where); err != nil {
   982  			t.Fatal(err)
   983  		}
   984  	}()
   985  	if err := os.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")); err != nil {
   986  		t.Fatal(err)
   987  	}
   988  	var b bytes.Buffer
   989  	var flagSet flag.FlagSet
   990  	err = do(&b, &flagSet, []string{"./template"})
   991  	if err != nil {
   992  		t.Errorf("unexpected error %q from ./template", err)
   993  	}
   994  	// The output should contain information about the text/template package.
   995  	const want = `package template // import "text/template"`
   996  	output := b.String()
   997  	if !strings.HasPrefix(output, want) {
   998  		t.Fatalf("wrong package: %.*q...", len(want), output)
   999  	}
  1000  }
  1001  
  1002  // Test that we don't print spurious package clauses
  1003  // when there should be no output at all. Issue 37969.
  1004  func TestNoPackageClauseWhenNoMatch(t *testing.T) {
  1005  	maybeSkip(t)
  1006  	var b bytes.Buffer
  1007  	var flagSet flag.FlagSet
  1008  	err := do(&b, &flagSet, []string{"template.ZZZ"})
  1009  	// Expect an error.
  1010  	if err == nil {
  1011  		t.Error("expect an error for template.zzz")
  1012  	}
  1013  	// And the output should not contain any package clauses.
  1014  	const dontWant = `package template // import `
  1015  	output := b.String()
  1016  	if strings.Contains(output, dontWant) {
  1017  		t.Fatalf("improper package clause printed:\n%s", output)
  1018  	}
  1019  }
  1020  
  1021  type trimTest struct {
  1022  	path   string
  1023  	prefix string
  1024  	result string
  1025  	ok     bool
  1026  }
  1027  
  1028  var trimTests = []trimTest{
  1029  	{"", "", "", true},
  1030  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
  1031  	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
  1032  	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
  1033  	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
  1034  }
  1035  
  1036  func TestTrim(t *testing.T) {
  1037  	for _, test := range trimTests {
  1038  		result, ok := trim(test.path, test.prefix)
  1039  		if ok != test.ok {
  1040  			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
  1041  			continue
  1042  		}
  1043  		if result != test.result {
  1044  			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
  1045  			continue
  1046  		}
  1047  	}
  1048  }
  1049  

View as plain text