...

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  	"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/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/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/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  
   633  	// Interface method.
   634  	{
   635  		"interface method",
   636  		[]string{p, `ExportedInterface.ExportedMethod`},
   637  		[]string{
   638  			`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\(\)` +
   639  				`.*Comment on line with exported method`,
   640  		},
   641  		[]string{
   642  			`Comment about exported interface`,
   643  		},
   644  	},
   645  	// Interface method at package level.
   646  	{
   647  		"interface method at package level",
   648  		[]string{p, `ExportedMethod`},
   649  		[]string{
   650  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   651  			`Comment about exported method`,
   652  		},
   653  		[]string{
   654  			`Comment before exported method.*\n.*ExportedMethod\(\)` +
   655  				`.*Comment on line with exported method`,
   656  		},
   657  	},
   658  
   659  	// Method.
   660  	{
   661  		"method",
   662  		[]string{p, `ExportedType.ExportedMethod`},
   663  		[]string{
   664  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   665  			`Comment about exported method`,
   666  		},
   667  		nil,
   668  	},
   669  	// Method  with -u.
   670  	{
   671  		"method with -u",
   672  		[]string{"-u", p, `ExportedType.unexportedMethod`},
   673  		[]string{
   674  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   675  			`Comment about unexported method`,
   676  		},
   677  		nil,
   678  	},
   679  	// Method with -src.
   680  	{
   681  		"method with -src",
   682  		[]string{"-src", p, `ExportedType.ExportedMethod`},
   683  		[]string{
   684  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   685  			`Comment about exported method`,
   686  			`return true != true`,
   687  		},
   688  		nil,
   689  	},
   690  
   691  	// Field.
   692  	{
   693  		"field",
   694  		[]string{p, `ExportedType.ExportedField`},
   695  		[]string{
   696  			`type ExportedType struct`,
   697  			`ExportedField int`,
   698  			`Comment before exported field`,
   699  			`Comment on line with exported field`,
   700  			`other fields elided`,
   701  		},
   702  		nil,
   703  	},
   704  
   705  	// Field with -u.
   706  	{
   707  		"method with -u",
   708  		[]string{"-u", p, `ExportedType.unexportedField`},
   709  		[]string{
   710  			`unexportedField int`,
   711  			`Comment on line with unexported field`,
   712  		},
   713  		nil,
   714  	},
   715  
   716  	// Field of struct with only one field.
   717  	{
   718  		"single-field struct",
   719  		[]string{p, `ExportedStructOneField.OnlyField`},
   720  		[]string{`the only field`},
   721  		[]string{`other fields elided`},
   722  	},
   723  
   724  	// Case matching off.
   725  	{
   726  		"case matching off",
   727  		[]string{p, `casematch`},
   728  		[]string{
   729  			`CaseMatch`,
   730  			`Casematch`,
   731  		},
   732  		nil,
   733  	},
   734  
   735  	// Case matching on.
   736  	{
   737  		"case matching on",
   738  		[]string{"-c", p, `Casematch`},
   739  		[]string{
   740  			`Casematch`,
   741  		},
   742  		[]string{
   743  			`CaseMatch`,
   744  		},
   745  	},
   746  
   747  	// Merging comments with -src.
   748  	{
   749  		"merge comments with -src A",
   750  		[]string{"-src", p + "/merge", `A`},
   751  		[]string{
   752  			`A doc`,
   753  			`func A`,
   754  			`A comment`,
   755  		},
   756  		[]string{
   757  			`Package A doc`,
   758  			`Package B doc`,
   759  			`B doc`,
   760  			`B comment`,
   761  			`B doc`,
   762  		},
   763  	},
   764  	{
   765  		"merge comments with -src B",
   766  		[]string{"-src", p + "/merge", `B`},
   767  		[]string{
   768  			`B doc`,
   769  			`func B`,
   770  			`B comment`,
   771  		},
   772  		[]string{
   773  			`Package A doc`,
   774  			`Package B doc`,
   775  			`A doc`,
   776  			`A comment`,
   777  			`A doc`,
   778  		},
   779  	},
   780  
   781  	// No dups with -u. Issue 21797.
   782  	{
   783  		"case matching on, no dups",
   784  		[]string{"-u", p, `duplicate`},
   785  		[]string{
   786  			`Duplicate`,
   787  			`duplicate`,
   788  		},
   789  		[]string{
   790  			"\\)\n+const", // This will appear if the const decl appears twice.
   791  		},
   792  	},
   793  	{
   794  		"non-imported: pkg.sym",
   795  		[]string{"nested.Foo"},
   796  		[]string{"Foo struct"},
   797  		nil,
   798  	},
   799  	{
   800  		"non-imported: pkg only",
   801  		[]string{"nested"},
   802  		[]string{"Foo struct"},
   803  		nil,
   804  	},
   805  	{
   806  		"non-imported: pkg sym",
   807  		[]string{"nested", "Foo"},
   808  		[]string{"Foo struct"},
   809  		nil,
   810  	},
   811  	{
   812  		"formatted doc on function",
   813  		[]string{p, "ExportedFormattedDoc"},
   814  		[]string{
   815  			`func ExportedFormattedDoc\(a int\) bool`,
   816  			`    Comment about exported function with formatting\.
   817  
   818      Example
   819  
   820          fmt\.Println\(FormattedDoc\(\)\)
   821  
   822      Text after pre-formatted block\.`,
   823  		},
   824  		nil,
   825  	},
   826  	{
   827  		"formatted doc on type field",
   828  		[]string{p, "ExportedFormattedType.ExportedField"},
   829  		[]string{
   830  			`type ExportedFormattedType struct`,
   831  			`    // Comment before exported field with formatting\.
   832      //[ ]
   833      // Example
   834      //[ ]
   835      //     a\.ExportedField = 123
   836      //[ ]
   837      // Text after pre-formatted block\.`,
   838  			`ExportedField int`,
   839  		},
   840  		[]string{"ignore:directive"},
   841  	},
   842  	{
   843  		"formatted doc on entire type",
   844  		[]string{p, "ExportedFormattedType"},
   845  		[]string{
   846  			`type ExportedFormattedType struct`,
   847  			`	// Comment before exported field with formatting\.
   848  	//
   849  	// Example
   850  	//
   851  	//	a\.ExportedField = 123
   852  	//
   853  	// Text after pre-formatted block\.`,
   854  			`ExportedField int`,
   855  		},
   856  		[]string{"ignore:directive"},
   857  	},
   858  	{
   859  		"formatted doc on entire type with -all",
   860  		[]string{"-all", p, "ExportedFormattedType"},
   861  		[]string{
   862  			`type ExportedFormattedType struct`,
   863  			`	// Comment before exported field with formatting\.
   864  	//
   865  	// Example
   866  	//
   867  	//	a\.ExportedField = 123
   868  	//
   869  	// Text after pre-formatted block\.`,
   870  			`ExportedField int`,
   871  		},
   872  		[]string{"ignore:directive"},
   873  	},
   874  }
   875  
   876  func TestDoc(t *testing.T) {
   877  	maybeSkip(t)
   878  	defer log.SetOutput(log.Writer())
   879  	for _, test := range tests {
   880  		var b bytes.Buffer
   881  		var flagSet flag.FlagSet
   882  		var logbuf bytes.Buffer
   883  		log.SetOutput(&logbuf)
   884  		err := do(&b, &flagSet, test.args)
   885  		if err != nil {
   886  			t.Fatalf("%s %v: %s\n", test.name, test.args, err)
   887  		}
   888  		if logbuf.Len() > 0 {
   889  			t.Errorf("%s %v: unexpected log messages:\n%s", test.name, test.args, logbuf.Bytes())
   890  		}
   891  		output := b.Bytes()
   892  		failed := false
   893  		for j, yes := range test.yes {
   894  			re, err := regexp.Compile(yes)
   895  			if err != nil {
   896  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
   897  			}
   898  			if !re.Match(output) {
   899  				t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
   900  				failed = true
   901  			}
   902  		}
   903  		for j, no := range test.no {
   904  			re, err := regexp.Compile(no)
   905  			if err != nil {
   906  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
   907  			}
   908  			if re.Match(output) {
   909  				t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
   910  				failed = true
   911  			}
   912  		}
   913  		if bytes.Count(output, []byte("TYPES\n")) > 1 {
   914  			t.Fatalf("%s: repeating headers", test.name)
   915  		}
   916  		if failed {
   917  			t.Logf("\n%s", output)
   918  		}
   919  	}
   920  }
   921  
   922  // Test the code to try multiple packages. Our test case is
   923  //
   924  //	go doc rand.Float64
   925  //
   926  // This needs to find math/rand.Float64; however crypto/rand, which doesn't
   927  // have the symbol, usually appears first in the directory listing.
   928  func TestMultiplePackages(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  	// Make sure crypto/rand does not have the symbol.
   935  	{
   936  		var flagSet flag.FlagSet
   937  		err := do(&b, &flagSet, []string{"crypto/rand.float64"})
   938  		if err == nil {
   939  			t.Errorf("expected error from crypto/rand.float64")
   940  		} else if !strings.Contains(err.Error(), "no symbol float64") {
   941  			t.Errorf("unexpected error %q from crypto/rand.float64", err)
   942  		}
   943  	}
   944  	// Make sure math/rand does have the symbol.
   945  	{
   946  		var flagSet flag.FlagSet
   947  		err := do(&b, &flagSet, []string{"math/rand.float64"})
   948  		if err != nil {
   949  			t.Errorf("unexpected error %q from math/rand.float64", err)
   950  		}
   951  	}
   952  	// Try the shorthand.
   953  	{
   954  		var flagSet flag.FlagSet
   955  		err := do(&b, &flagSet, []string{"rand.float64"})
   956  		if err != nil {
   957  			t.Errorf("unexpected error %q from rand.float64", err)
   958  		}
   959  	}
   960  	// Now try a missing symbol. We should see both packages in the error.
   961  	{
   962  		var flagSet flag.FlagSet
   963  		err := do(&b, &flagSet, []string{"rand.doesnotexit"})
   964  		if err == nil {
   965  			t.Errorf("expected error from rand.doesnotexit")
   966  		} else {
   967  			errStr := err.Error()
   968  			if !strings.Contains(errStr, "no symbol") {
   969  				t.Errorf("error %q should contain 'no symbol", errStr)
   970  			}
   971  			if !strings.Contains(errStr, "crypto/rand") {
   972  				t.Errorf("error %q should contain crypto/rand", errStr)
   973  			}
   974  			if !strings.Contains(errStr, "math/rand") {
   975  				t.Errorf("error %q should contain math/rand", errStr)
   976  			}
   977  		}
   978  	}
   979  }
   980  
   981  // Test the code to look up packages when given two args. First test case is
   982  //
   983  //	go doc binary BigEndian
   984  //
   985  // This needs to find encoding/binary.BigEndian, which means
   986  // finding the package encoding/binary given only "binary".
   987  // Second case is
   988  //
   989  //	go doc rand Float64
   990  //
   991  // which again needs to find math/rand and not give up after crypto/rand,
   992  // which has no such function.
   993  func TestTwoArgLookup(t *testing.T) {
   994  	if testing.Short() {
   995  		t.Skip("scanning file system takes too long")
   996  	}
   997  	maybeSkip(t)
   998  	var b bytes.Buffer // We don't care about the output.
   999  	{
  1000  		var flagSet flag.FlagSet
  1001  		err := do(&b, &flagSet, []string{"binary", "BigEndian"})
  1002  		if err != nil {
  1003  			t.Errorf("unexpected error %q from binary BigEndian", err)
  1004  		}
  1005  	}
  1006  	{
  1007  		var flagSet flag.FlagSet
  1008  		err := do(&b, &flagSet, []string{"rand", "Float64"})
  1009  		if err != nil {
  1010  			t.Errorf("unexpected error %q from rand Float64", err)
  1011  		}
  1012  	}
  1013  	{
  1014  		var flagSet flag.FlagSet
  1015  		err := do(&b, &flagSet, []string{"bytes", "Foo"})
  1016  		if err == nil {
  1017  			t.Errorf("expected error from bytes Foo")
  1018  		} else if !strings.Contains(err.Error(), "no symbol Foo") {
  1019  			t.Errorf("unexpected error %q from bytes Foo", err)
  1020  		}
  1021  	}
  1022  	{
  1023  		var flagSet flag.FlagSet
  1024  		err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"})
  1025  		if err == nil {
  1026  			// actually present in the user's filesystem
  1027  		} else if !strings.Contains(err.Error(), "no such package") {
  1028  			t.Errorf("unexpected error %q from nosuchpackage Foo", err)
  1029  		}
  1030  	}
  1031  }
  1032  
  1033  // Test the code to look up packages when the first argument starts with "./".
  1034  // Our test case is in effect "cd src/text; doc ./template". This should get
  1035  // text/template but before Issue 23383 was fixed would give html/template.
  1036  func TestDotSlashLookup(t *testing.T) {
  1037  	if testing.Short() {
  1038  		t.Skip("scanning file system takes too long")
  1039  	}
  1040  	maybeSkip(t)
  1041  	where, err := os.Getwd()
  1042  	if err != nil {
  1043  		t.Fatal(err)
  1044  	}
  1045  	defer func() {
  1046  		if err := os.Chdir(where); err != nil {
  1047  			t.Fatal(err)
  1048  		}
  1049  	}()
  1050  	if err := os.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")); err != nil {
  1051  		t.Fatal(err)
  1052  	}
  1053  	var b strings.Builder
  1054  	var flagSet flag.FlagSet
  1055  	err = do(&b, &flagSet, []string{"./template"})
  1056  	if err != nil {
  1057  		t.Errorf("unexpected error %q from ./template", err)
  1058  	}
  1059  	// The output should contain information about the text/template package.
  1060  	const want = `package template // import "text/template"`
  1061  	output := b.String()
  1062  	if !strings.HasPrefix(output, want) {
  1063  		t.Fatalf("wrong package: %.*q...", len(want), output)
  1064  	}
  1065  }
  1066  
  1067  // Test that we don't print spurious package clauses
  1068  // when there should be no output at all. Issue 37969.
  1069  func TestNoPackageClauseWhenNoMatch(t *testing.T) {
  1070  	maybeSkip(t)
  1071  	var b strings.Builder
  1072  	var flagSet flag.FlagSet
  1073  	err := do(&b, &flagSet, []string{"template.ZZZ"})
  1074  	// Expect an error.
  1075  	if err == nil {
  1076  		t.Error("expect an error for template.zzz")
  1077  	}
  1078  	// And the output should not contain any package clauses.
  1079  	const dontWant = `package template // import `
  1080  	output := b.String()
  1081  	if strings.Contains(output, dontWant) {
  1082  		t.Fatalf("improper package clause printed:\n%s", output)
  1083  	}
  1084  }
  1085  
  1086  type trimTest struct {
  1087  	path   string
  1088  	prefix string
  1089  	result string
  1090  	ok     bool
  1091  }
  1092  
  1093  var trimTests = []trimTest{
  1094  	{"", "", "", true},
  1095  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
  1096  	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
  1097  	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
  1098  	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
  1099  }
  1100  
  1101  func TestTrim(t *testing.T) {
  1102  	for _, test := range trimTests {
  1103  		result, ok := trim(test.path, test.prefix)
  1104  		if ok != test.ok {
  1105  			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
  1106  			continue
  1107  		}
  1108  		if result != test.result {
  1109  			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
  1110  			continue
  1111  		}
  1112  	}
  1113  }
  1114  

View as plain text