Source file src/cmd/go/internal/doc/doc_test.go

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

View as plain text