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  	"internal/testenv"
    11  	"log"
    12  	"os"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"strings"
    17  	"testing"
    18  
    19  	"cmd/go/internal/cfg"
    20  )
    21  
    22  func TestMain(m *testing.M) {
    23  	// Clear GOPATH so we don't access the user's own packages in the test.
    24  	cfg.BuildContext.GOPATH = ""
    25  	testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
    26  
    27  	// Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was
    28  	// built with -trimpath). dirsInit would identify it using 'go env GOROOT',
    29  	// but we can't be sure that the 'go' in $PATH is the right one either.
    30  	cfg.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: "cmd/go/internal/doc/testdata", dir: testdataDir},
    41  		Dir{importPath: "cmd/go/internal/doc/testdata/nested", dir: filepath.Join(testdataDir, "nested")},
    42  		Dir{importPath: "cmd/go/internal/doc/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  	// Package with -ex.
   754  	{
   755  		"package with -ex",
   756  		[]string{`-ex`, p},
   757  		[]string{
   758  			`func ExampleExportedFunc\(\)`,
   759  			`func ExampleExportedType\(\)`,
   760  			`func Example\(\)`,
   761  			`func Example_multiline\(\)`,
   762  			`func Example_playable\(\)`,
   763  		},
   764  		nil,
   765  	},
   766  
   767  	// Type with -ex.
   768  	{
   769  		"type with -ex",
   770  		[]string{`-ex`, p, `ExportedType`},
   771  		[]string{
   772  			`func ExampleExportedType\(\)`,
   773  			`func ExampleExportedType_ExportedMethod\(\)`,
   774  		},
   775  		nil,
   776  	},
   777  
   778  	// Function with -ex.
   779  	{
   780  		"function with -ex",
   781  		[]string{`-ex`, p, `ExportedFunc`},
   782  		[]string{
   783  			`func ExampleExportedFunc\(\)`,
   784  			`Function example.`,
   785  			`func ExampleExportedFunc_two\(\)`,
   786  			`Function example two.`,
   787  		},
   788  		nil,
   789  	},
   790  
   791  	// Method with -ex.
   792  	{
   793  		"method with -ex",
   794  		[]string{`-ex`, p, `ExportedType.ExportedMethod`},
   795  		[]string{
   796  			`func ExampleExportedType_ExportedMethod\(\)`,
   797  			`Method example.`,
   798  		},
   799  		nil,
   800  	},
   801  
   802  	// Package example.
   803  	{
   804  		"package example",
   805  		[]string{p, `Example`},
   806  		[]string{
   807  			`fmt.Println\("Package example output"\)`,
   808  			`Output: Package example output`,
   809  		},
   810  		[]string{`func Example\(\)`},
   811  	},
   812  
   813  	// Multiline output example.
   814  	{
   815  		"multiline output example",
   816  		[]string{p, `Example_multiline`},
   817  		[]string{
   818  			`fmt.Println\("Multiline\\nexample\\noutput"\)`,
   819  			"Output: \nMultiline\nexample\noutput",
   820  		},
   821  		[]string{`Output: Multiline example output`},
   822  	},
   823  
   824  	// Type example.
   825  	{
   826  		"type example",
   827  		[]string{p, `ExampleExportedType`},
   828  		[]string{
   829  			`fmt.Println\("Type example output"\)`,
   830  			`Output: Type example output`,
   831  		},
   832  		[]string{`func ExampleExportedType\(\)`},
   833  	},
   834  
   835  	// Function example.
   836  	{
   837  		"function example",
   838  		[]string{p, `ExampleExportedFunc`},
   839  		[]string{
   840  			`fmt.Println\("Function example output"\)`,
   841  			`Output: Function example output`,
   842  		},
   843  		[]string{
   844  			`func ExampleExportedFunc\(\)`,
   845  			`fmt.Println\("Function example two output"\)`,
   846  			`Output: Function example two output`,
   847  		},
   848  	},
   849  
   850  	// Function example two.
   851  	{
   852  		"function example",
   853  		[]string{p, `ExampleExportedFunc_two`},
   854  		[]string{
   855  			`fmt.Println\("Function example two output"\)`,
   856  			`Output: Function example two output`,
   857  		},
   858  		[]string{
   859  			`func ExampleExportedFunc_two\(\)`,
   860  			`fmt.Println\("Function example output"\)`,
   861  			`Output: Function example output`,
   862  		},
   863  	},
   864  
   865  	// Method example.
   866  	{
   867  		"method example",
   868  		[]string{p, `ExampleExportedType_ExportedMethod`},
   869  		[]string{
   870  			`fmt.Println\("Method example output"\)`,
   871  			`Output: Method example output`,
   872  		},
   873  		[]string{`func ExampleExportedType_ExportedMethod\(\)`},
   874  	},
   875  
   876  	// Playable example.
   877  	{
   878  		"playable example",
   879  		[]string{p, `Example_playable`},
   880  		[]string{
   881  			`package main`,
   882  			`func main\(\) {`,
   883  			`fmt.Println\("Playable example output"\)`,
   884  			`}`,
   885  			`Output: Playable example output`,
   886  		},
   887  		[]string{`func Example_playable\(\)`},
   888  	},
   889  
   890  	// Case matching off.
   891  	{
   892  		"case matching off",
   893  		[]string{p, `casematch`},
   894  		[]string{
   895  			`CaseMatch`,
   896  			`Casematch`,
   897  		},
   898  		nil,
   899  	},
   900  
   901  	// Case matching on.
   902  	{
   903  		"case matching on",
   904  		[]string{"-c", p, `Casematch`},
   905  		[]string{
   906  			`Casematch`,
   907  		},
   908  		[]string{
   909  			`CaseMatch`,
   910  		},
   911  	},
   912  
   913  	// Merging comments with -src.
   914  	{
   915  		"merge comments with -src A",
   916  		[]string{"-src", p + "/merge", `A`},
   917  		[]string{
   918  			`A doc`,
   919  			`func A`,
   920  			`A comment`,
   921  		},
   922  		[]string{
   923  			`Package A doc`,
   924  			`Package B doc`,
   925  			`B doc`,
   926  			`B comment`,
   927  			`B doc`,
   928  		},
   929  	},
   930  	{
   931  		"merge comments with -src B",
   932  		[]string{"-src", p + "/merge", `B`},
   933  		[]string{
   934  			`B doc`,
   935  			`func B`,
   936  			`B comment`,
   937  		},
   938  		[]string{
   939  			`Package A doc`,
   940  			`Package B doc`,
   941  			`A doc`,
   942  			`A comment`,
   943  			`A doc`,
   944  		},
   945  	},
   946  
   947  	// No dups with -u. Issue 21797.
   948  	{
   949  		"case matching on, no dups",
   950  		[]string{"-u", p, `duplicate`},
   951  		[]string{
   952  			`Duplicate`,
   953  			`duplicate`,
   954  		},
   955  		[]string{
   956  			"\\)\n+const", // This will appear if the const decl appears twice.
   957  		},
   958  	},
   959  	{
   960  		"non-imported: pkg.sym",
   961  		[]string{"nested.Foo"},
   962  		[]string{"Foo struct"},
   963  		nil,
   964  	},
   965  	{
   966  		"non-imported: pkg only",
   967  		[]string{"nested"},
   968  		[]string{"Foo struct"},
   969  		nil,
   970  	},
   971  	{
   972  		"non-imported: pkg sym",
   973  		[]string{"nested", "Foo"},
   974  		[]string{"Foo struct"},
   975  		nil,
   976  	},
   977  	{
   978  		"formatted doc on function",
   979  		[]string{p, "ExportedFormattedDoc"},
   980  		[]string{
   981  			`func ExportedFormattedDoc\(a int\) bool`,
   982  			`    Comment about exported function with formatting\.
   983  
   984      Example
   985  
   986          fmt\.Println\(FormattedDoc\(\)\)
   987  
   988      Text after pre-formatted block\.`,
   989  		},
   990  		nil,
   991  	},
   992  	{
   993  		"formatted doc on type field",
   994  		[]string{p, "ExportedFormattedType.ExportedField"},
   995  		[]string{
   996  			`type ExportedFormattedType struct`,
   997  			`    // Comment before exported field with formatting\.
   998      //[ ]
   999      // Example
  1000      //[ ]
  1001      //     a\.ExportedField = 123
  1002      //[ ]
  1003      // Text after pre-formatted block\.`,
  1004  			`ExportedField int`,
  1005  		},
  1006  		[]string{"ignore:directive"},
  1007  	},
  1008  	{
  1009  		"formatted doc on entire type",
  1010  		[]string{p, "ExportedFormattedType"},
  1011  		[]string{
  1012  			`type ExportedFormattedType struct`,
  1013  			`	// Comment before exported field with formatting\.
  1014  	//
  1015  	// Example
  1016  	//
  1017  	//	a\.ExportedField = 123
  1018  	//
  1019  	// Text after pre-formatted block\.`,
  1020  			`ExportedField int`,
  1021  		},
  1022  		[]string{"ignore:directive"},
  1023  	},
  1024  	{
  1025  		"formatted doc on entire type with -all",
  1026  		[]string{"-all", p, "ExportedFormattedType"},
  1027  		[]string{
  1028  			`type ExportedFormattedType struct`,
  1029  			`	// Comment before exported field with formatting\.
  1030  	//
  1031  	// Example
  1032  	//
  1033  	//	a\.ExportedField = 123
  1034  	//
  1035  	// Text after pre-formatted block\.`,
  1036  			`ExportedField int`,
  1037  		},
  1038  		[]string{"ignore:directive"},
  1039  	},
  1040  }
  1041  
  1042  func TestDoc(t *testing.T) {
  1043  	maybeSkip(t)
  1044  	defer log.SetOutput(log.Writer())
  1045  	for _, test := range tests {
  1046  		var b bytes.Buffer
  1047  		var flagSet flag.FlagSet
  1048  		var logbuf bytes.Buffer
  1049  		log.SetOutput(&logbuf)
  1050  		err := do(t.Context(), &b, &flagSet, test.args)
  1051  		if err != nil {
  1052  			t.Fatalf("%s %v: %s\n", test.name, test.args, err)
  1053  		}
  1054  		if logbuf.Len() > 0 {
  1055  			t.Errorf("%s %v: unexpected log messages:\n%s", test.name, test.args, logbuf.Bytes())
  1056  		}
  1057  		output := b.Bytes()
  1058  		failed := false
  1059  		for j, yes := range test.yes {
  1060  			re, err := regexp.Compile(yes)
  1061  			if err != nil {
  1062  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
  1063  			}
  1064  			if !re.Match(output) {
  1065  				t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
  1066  				failed = true
  1067  			}
  1068  		}
  1069  		for j, no := range test.no {
  1070  			re, err := regexp.Compile(no)
  1071  			if err != nil {
  1072  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
  1073  			}
  1074  			if re.Match(output) {
  1075  				t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
  1076  				failed = true
  1077  			}
  1078  		}
  1079  		if bytes.Count(output, []byte("TYPES\n")) > 1 {
  1080  			t.Fatalf("%s: repeating headers", test.name)
  1081  		}
  1082  		if failed {
  1083  			t.Logf("\n%s", output)
  1084  		}
  1085  	}
  1086  }
  1087  
  1088  // Test the code to try multiple packages. Our test case is
  1089  //
  1090  //	go doc rand.Float64
  1091  //
  1092  // This needs to find math/rand.Float64; however crypto/rand, which doesn't
  1093  // have the symbol, usually appears first in the directory listing.
  1094  func TestMultiplePackages(t *testing.T) {
  1095  	if testing.Short() {
  1096  		t.Skip("scanning file system takes too long")
  1097  	}
  1098  	maybeSkip(t)
  1099  	var b bytes.Buffer // We don't care about the output.
  1100  	// Make sure crypto/rand does not have the symbol.
  1101  	{
  1102  		var flagSet flag.FlagSet
  1103  		err := do(t.Context(), &b, &flagSet, []string{"crypto/rand.float64"})
  1104  		if err == nil {
  1105  			t.Errorf("expected error from crypto/rand.float64")
  1106  		} else if !strings.Contains(err.Error(), "no symbol float64") {
  1107  			t.Errorf("unexpected error %q from crypto/rand.float64", err)
  1108  		}
  1109  	}
  1110  	// Make sure math/rand does have the symbol.
  1111  	{
  1112  		var flagSet flag.FlagSet
  1113  		err := do(t.Context(), &b, &flagSet, []string{"math/rand.float64"})
  1114  		if err != nil {
  1115  			t.Errorf("unexpected error %q from math/rand.float64", err)
  1116  		}
  1117  	}
  1118  	// Try the shorthand.
  1119  	{
  1120  		var flagSet flag.FlagSet
  1121  		err := do(t.Context(), &b, &flagSet, []string{"rand.float64"})
  1122  		if err != nil {
  1123  			t.Errorf("unexpected error %q from rand.float64", err)
  1124  		}
  1125  	}
  1126  	// Now try a missing symbol. We should see both packages in the error.
  1127  	{
  1128  		var flagSet flag.FlagSet
  1129  		err := do(t.Context(), &b, &flagSet, []string{"rand.doesnotexit"})
  1130  		if err == nil {
  1131  			t.Errorf("expected error from rand.doesnotexit")
  1132  		} else {
  1133  			errStr := err.Error()
  1134  			if !strings.Contains(errStr, "no symbol") {
  1135  				t.Errorf("error %q should contain 'no symbol", errStr)
  1136  			}
  1137  			if !strings.Contains(errStr, "crypto/rand") {
  1138  				t.Errorf("error %q should contain crypto/rand", errStr)
  1139  			}
  1140  			if !strings.Contains(errStr, "math/rand") {
  1141  				t.Errorf("error %q should contain math/rand", errStr)
  1142  			}
  1143  		}
  1144  	}
  1145  }
  1146  
  1147  // Test the code to look up packages when given two args. First test case is
  1148  //
  1149  //	go doc binary BigEndian
  1150  //
  1151  // This needs to find encoding/binary.BigEndian, which means
  1152  // finding the package encoding/binary given only "binary".
  1153  // Second case is
  1154  //
  1155  //	go doc rand Float64
  1156  //
  1157  // which again needs to find math/rand and not give up after crypto/rand,
  1158  // which has no such function.
  1159  func TestTwoArgLookup(t *testing.T) {
  1160  	if testing.Short() {
  1161  		t.Skip("scanning file system takes too long")
  1162  	}
  1163  	maybeSkip(t)
  1164  	var b bytes.Buffer // We don't care about the output.
  1165  	{
  1166  		var flagSet flag.FlagSet
  1167  		err := do(t.Context(), &b, &flagSet, []string{"binary", "BigEndian"})
  1168  		if err != nil {
  1169  			t.Errorf("unexpected error %q from binary BigEndian", err)
  1170  		}
  1171  	}
  1172  	{
  1173  		var flagSet flag.FlagSet
  1174  		err := do(t.Context(), &b, &flagSet, []string{"rand", "Float64"})
  1175  		if err != nil {
  1176  			t.Errorf("unexpected error %q from rand Float64", err)
  1177  		}
  1178  	}
  1179  	{
  1180  		var flagSet flag.FlagSet
  1181  		err := do(t.Context(), &b, &flagSet, []string{"bytes", "Foo"})
  1182  		if err == nil {
  1183  			t.Errorf("expected error from bytes Foo")
  1184  		} else if !strings.Contains(err.Error(), "no symbol Foo") {
  1185  			t.Errorf("unexpected error %q from bytes Foo", err)
  1186  		}
  1187  	}
  1188  	{
  1189  		var flagSet flag.FlagSet
  1190  		err := do(t.Context(), &b, &flagSet, []string{"nosuchpackage", "Foo"})
  1191  		if err == nil {
  1192  			// actually present in the user's filesystem
  1193  		} else if !strings.Contains(err.Error(), "no such package") {
  1194  			t.Errorf("unexpected error %q from nosuchpackage Foo", err)
  1195  		}
  1196  	}
  1197  }
  1198  
  1199  // Test the code to look up packages when the first argument starts with "./".
  1200  // Our test case is in effect "cd src/text; doc ./template". This should get
  1201  // text/template but before Issue 23383 was fixed would give html/template.
  1202  func TestDotSlashLookup(t *testing.T) {
  1203  	if testing.Short() {
  1204  		t.Skip("scanning file system takes too long")
  1205  	}
  1206  	maybeSkip(t)
  1207  	t.Chdir(filepath.Join(cfg.GOROOT, "src", "text"))
  1208  
  1209  	var b strings.Builder
  1210  	var flagSet flag.FlagSet
  1211  	err := do(t.Context(), &b, &flagSet, []string{"./template"})
  1212  	if err != nil {
  1213  		t.Errorf("unexpected error %q from ./template", err)
  1214  	}
  1215  	// The output should contain information about the text/template package.
  1216  	const want = `package template // import "text/template"`
  1217  	output := b.String()
  1218  	if !strings.HasPrefix(output, want) {
  1219  		t.Fatalf("wrong package: %.*q...", len(want), output)
  1220  	}
  1221  }
  1222  
  1223  // Test that we don't print spurious package clauses
  1224  // when there should be no output at all. Issue 37969.
  1225  func TestNoPackageClauseWhenNoMatch(t *testing.T) {
  1226  	maybeSkip(t)
  1227  	var b strings.Builder
  1228  	var flagSet flag.FlagSet
  1229  	err := do(t.Context(), &b, &flagSet, []string{"template.ZZZ"})
  1230  	// Expect an error.
  1231  	if err == nil {
  1232  		t.Error("expect an error for template.zzz")
  1233  	}
  1234  	// And the output should not contain any package clauses.
  1235  	const dontWant = `package template // import `
  1236  	output := b.String()
  1237  	if strings.Contains(output, dontWant) {
  1238  		t.Fatalf("improper package clause printed:\n%s", output)
  1239  	}
  1240  }
  1241  
  1242  type trimTest struct {
  1243  	path   string
  1244  	prefix string
  1245  	result string
  1246  	ok     bool
  1247  }
  1248  
  1249  var trimTests = []trimTest{
  1250  	{"", "", "", true},
  1251  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
  1252  	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
  1253  	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
  1254  	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
  1255  }
  1256  
  1257  func TestTrim(t *testing.T) {
  1258  	for _, test := range trimTests {
  1259  		result, ok := trim(test.path, test.prefix)
  1260  		if ok != test.ok {
  1261  			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
  1262  			continue
  1263  		}
  1264  		if result != test.result {
  1265  			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
  1266  			continue
  1267  		}
  1268  	}
  1269  }
  1270  

View as plain text