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

View as plain text