...
Run Format

Source file src/cmd/doc/doc_test.go

Documentation: cmd/doc

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func TestMain(m *testing.M) {
    19  	// Clear GOPATH so we don't access the user's own packages in the test.
    20  	buildCtx.GOPATH = ""
    21  	testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
    22  
    23  	// Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test.
    24  	// Normally testdata directories are ignored, but sending it to dirs.scan directly is
    25  	// a hack that works around the check.
    26  	testdataDir, err := filepath.Abs("testdata")
    27  	if err != nil {
    28  		panic(err)
    29  	}
    30  	dirsInit(Dir{"testdata", testdataDir}, Dir{"testdata/nested", filepath.Join(testdataDir, "nested")}, Dir{"testdata/nested/nested", filepath.Join(testdataDir, "nested", "nested")})
    31  
    32  	os.Exit(m.Run())
    33  }
    34  
    35  func maybeSkip(t *testing.T) {
    36  	if strings.HasPrefix(runtime.GOOS, "nacl") {
    37  		t.Skip("nacl does not have a full file tree")
    38  	}
    39  	if runtime.GOOS == "darwin" && strings.HasPrefix(runtime.GOARCH, "arm") {
    40  		t.Skip("darwin/arm does not have a full file tree")
    41  	}
    42  }
    43  
    44  type isDotSlashTest struct {
    45  	str    string
    46  	result bool
    47  }
    48  
    49  var isDotSlashTests = []isDotSlashTest{
    50  	{``, false},
    51  	{`x`, false},
    52  	{`...`, false},
    53  	{`.../`, false},
    54  	{`...\`, false},
    55  
    56  	{`.`, true},
    57  	{`./`, true},
    58  	{`.\`, true},
    59  	{`./x`, true},
    60  	{`.\x`, true},
    61  
    62  	{`..`, true},
    63  	{`../`, true},
    64  	{`..\`, true},
    65  	{`../x`, true},
    66  	{`..\x`, true},
    67  }
    68  
    69  func TestIsDotSlashPath(t *testing.T) {
    70  	for _, test := range isDotSlashTests {
    71  		if result := isDotSlash(test.str); result != test.result {
    72  			t.Errorf("isDotSlash(%q) = %t; expected %t", test.str, result, test.result)
    73  		}
    74  	}
    75  }
    76  
    77  type test struct {
    78  	name string
    79  	args []string // Arguments to "[go] doc".
    80  	yes  []string // Regular expressions that should match.
    81  	no   []string // Regular expressions that should not match.
    82  }
    83  
    84  const p = "cmd/doc/testdata"
    85  
    86  var tests = []test{
    87  	// Sanity check.
    88  	{
    89  		"sanity check",
    90  		[]string{p},
    91  		[]string{`type ExportedType struct`},
    92  		nil,
    93  	},
    94  
    95  	// Package dump includes import, package statement.
    96  	{
    97  		"package clause",
    98  		[]string{p},
    99  		[]string{`package pkg.*cmd/doc/testdata`},
   100  		nil,
   101  	},
   102  
   103  	// Constants.
   104  	// Package dump
   105  	{
   106  		"full package",
   107  		[]string{p},
   108  		[]string{
   109  			`Package comment`,
   110  			`const ExportedConstant = 1`,                                   // Simple constant.
   111  			`const ConstOne = 1`,                                           // First entry in constant block.
   112  			`const ConstFive ...`,                                          // From block starting with unexported constant.
   113  			`var ExportedVariable = 1`,                                     // Simple variable.
   114  			`var VarOne = 1`,                                               // First entry in variable block.
   115  			`func ExportedFunc\(a int\) bool`,                              // Function.
   116  			`func ReturnUnexported\(\) unexportedType`,                     // Function with unexported return type.
   117  			`type ExportedType struct{ ... }`,                              // Exported type.
   118  			`const ExportedTypedConstant ExportedType = iota`,              // Typed constant.
   119  			`const ExportedTypedConstant_unexported unexportedType`,        // Typed constant, exported for unexported type.
   120  			`const ConstLeft2 uint64 ...`,                                  // Typed constant using unexported iota.
   121  			`const ConstGroup1 unexportedType = iota ...`,                  // Typed constant using unexported type.
   122  			`const ConstGroup4 ExportedType = ExportedType{}`,              // Typed constant using exported type.
   123  			`const MultiLineConst = ...`,                                   // Multi line constant.
   124  			`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`,  // Multi line variable.
   125  			`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
   126  			`var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`,  // Long list of arguments.
   127  			`type T1 = T2`,                                                 // Type alias
   128  		},
   129  		[]string{
   130  			`const internalConstant = 2`,        // No internal constants.
   131  			`var internalVariable = 2`,          // No internal variables.
   132  			`func internalFunc(a int) bool`,     // No internal functions.
   133  			`Comment about exported constant`,   // No comment for single constant.
   134  			`Comment about exported variable`,   // No comment for single variable.
   135  			`Comment about block of constants.`, // No comment for constant block.
   136  			`Comment about block of variables.`, // No comment for variable block.
   137  			`Comment before ConstOne`,           // No comment for first entry in constant block.
   138  			`Comment before VarOne`,             // No comment for first entry in variable block.
   139  			`ConstTwo = 2`,                      // No second entry in constant block.
   140  			`VarTwo = 2`,                        // No second entry in variable block.
   141  			`VarFive = 5`,                       // From block starting with unexported variable.
   142  			`type unexportedType`,               // No unexported type.
   143  			`unexportedTypedConstant`,           // No unexported typed constant.
   144  			`\bField`,                           // No fields.
   145  			`Method`,                            // No methods.
   146  			`someArgument[5-8]`,                 // No truncated arguments.
   147  			`type T1 T2`,                        // Type alias does not display as type declaration.
   148  		},
   149  	},
   150  	// Package dump -u
   151  	{
   152  		"full package with u",
   153  		[]string{`-u`, p},
   154  		[]string{
   155  			`const ExportedConstant = 1`,               // Simple constant.
   156  			`const internalConstant = 2`,               // Internal constants.
   157  			`func internalFunc\(a int\) bool`,          // Internal functions.
   158  			`func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
   159  		},
   160  		[]string{
   161  			`Comment about exported constant`,  // No comment for simple constant.
   162  			`Comment about block of constants`, // No comment for constant block.
   163  			`Comment about internal function`,  // No comment for internal function.
   164  			`MultiLine(String|Method|Field)`,   // No data from multi line portions.
   165  		},
   166  	},
   167  
   168  	// Single constant.
   169  	{
   170  		"single constant",
   171  		[]string{p, `ExportedConstant`},
   172  		[]string{
   173  			`Comment about exported constant`, // Include comment.
   174  			`const ExportedConstant = 1`,
   175  		},
   176  		nil,
   177  	},
   178  	// Single constant -u.
   179  	{
   180  		"single constant with -u",
   181  		[]string{`-u`, p, `internalConstant`},
   182  		[]string{
   183  			`Comment about internal constant`, // Include comment.
   184  			`const internalConstant = 2`,
   185  		},
   186  		nil,
   187  	},
   188  	// Block of constants.
   189  	{
   190  		"block of constants",
   191  		[]string{p, `ConstTwo`},
   192  		[]string{
   193  			`Comment before ConstOne.\n.*ConstOne = 1`,    // First...
   194  			`ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up.
   195  			`Comment about block of constants`,            // Comment does too.
   196  		},
   197  		[]string{
   198  			`constThree`, // No unexported constant.
   199  		},
   200  	},
   201  	// Block of constants -u.
   202  	{
   203  		"block of constants with -u",
   204  		[]string{"-u", p, `constThree`},
   205  		[]string{
   206  			`constThree = 3.*Comment on line with constThree`,
   207  		},
   208  		nil,
   209  	},
   210  	// Block of constants with carryover type from unexported field.
   211  	{
   212  		"block of constants with carryover type",
   213  		[]string{p, `ConstLeft2`},
   214  		[]string{
   215  			`ConstLeft2, constRight2 uint64`,
   216  			`constLeft3, ConstRight3`,
   217  			`ConstLeft4, ConstRight4`,
   218  		},
   219  		nil,
   220  	},
   221  	// Block of constants -u with carryover type from unexported field.
   222  	{
   223  		"block of constants with carryover type",
   224  		[]string{"-u", p, `ConstLeft2`},
   225  		[]string{
   226  			`_, _ uint64 = 2 \* iota, 1 << iota`,
   227  			`constLeft1, constRight1`,
   228  			`ConstLeft2, constRight2`,
   229  			`constLeft3, ConstRight3`,
   230  			`ConstLeft4, ConstRight4`,
   231  		},
   232  		nil,
   233  	},
   234  
   235  	// Single variable.
   236  	{
   237  		"single variable",
   238  		[]string{p, `ExportedVariable`},
   239  		[]string{
   240  			`ExportedVariable`, // Include comment.
   241  			`var ExportedVariable = 1`,
   242  		},
   243  		nil,
   244  	},
   245  	// Single variable -u.
   246  	{
   247  		"single variable with -u",
   248  		[]string{`-u`, p, `internalVariable`},
   249  		[]string{
   250  			`Comment about internal variable`, // Include comment.
   251  			`var internalVariable = 2`,
   252  		},
   253  		nil,
   254  	},
   255  	// Block of variables.
   256  	{
   257  		"block of variables",
   258  		[]string{p, `VarTwo`},
   259  		[]string{
   260  			`Comment before VarOne.\n.*VarOne = 1`,    // First...
   261  			`VarTwo = 2.*Comment on line with VarTwo`, // And second show up.
   262  			`Comment about block of variables`,        // Comment does too.
   263  		},
   264  		[]string{
   265  			`varThree= 3`, // No unexported variable.
   266  		},
   267  	},
   268  	// Block of variables -u.
   269  	{
   270  		"block of variables with -u",
   271  		[]string{"-u", p, `varThree`},
   272  		[]string{
   273  			`varThree = 3.*Comment on line with varThree`,
   274  		},
   275  		nil,
   276  	},
   277  
   278  	// Function.
   279  	{
   280  		"function",
   281  		[]string{p, `ExportedFunc`},
   282  		[]string{
   283  			`Comment about exported function`, // Include comment.
   284  			`func ExportedFunc\(a int\) bool`,
   285  		},
   286  		nil,
   287  	},
   288  	// Function -u.
   289  	{
   290  		"function with -u",
   291  		[]string{"-u", p, `internalFunc`},
   292  		[]string{
   293  			`Comment about internal function`, // Include comment.
   294  			`func internalFunc\(a int\) bool`,
   295  		},
   296  		nil,
   297  	},
   298  
   299  	// Type.
   300  	{
   301  		"type",
   302  		[]string{p, `ExportedType`},
   303  		[]string{
   304  			`Comment about exported type`, // Include comment.
   305  			`type ExportedType struct`,    // Type definition.
   306  			`Comment before exported field.*\n.*ExportedField +int` +
   307  				`.*Comment on line with exported field.`,
   308  			`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
   309  			`Has unexported fields`,
   310  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   311  			`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
   312  			`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
   313  			`io.Reader.*Comment on line with embedded Reader.`,
   314  		},
   315  		[]string{
   316  			`unexportedField`,                // No unexported field.
   317  			`int.*embedded`,                  // No unexported embedded field.
   318  			`Comment about exported method.`, // No comment about exported method.
   319  			`unexportedMethod`,               // No unexported method.
   320  			`unexportedTypedConstant`,        // No unexported constant.
   321  			`error`,                          // No embedded error.
   322  		},
   323  	},
   324  	// Type T1 dump (alias).
   325  	{
   326  		"type T1",
   327  		[]string{p + ".T1"},
   328  		[]string{
   329  			`type T1 = T2`,
   330  		},
   331  		[]string{
   332  			`type T1 T2`,
   333  			`type ExportedType`,
   334  		},
   335  	},
   336  	// Type -u with unexported fields.
   337  	{
   338  		"type with unexported fields and -u",
   339  		[]string{"-u", p, `ExportedType`},
   340  		[]string{
   341  			`Comment about exported type`, // Include comment.
   342  			`type ExportedType struct`,    // Type definition.
   343  			`Comment before exported field.*\n.*ExportedField +int`,
   344  			`unexportedField.*int.*Comment on line with unexported field.`,
   345  			`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
   346  			`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`,
   347  			`\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field.`,
   348  			`unexportedType.*Comment on line with unexported embedded field.`,
   349  			`\*unexportedType.*Comment on line with unexported embedded \*field.`,
   350  			`io.Reader.*Comment on line with embedded Reader.`,
   351  			`error.*Comment on line with embedded error.`,
   352  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   353  			`unexportedTypedConstant`,
   354  		},
   355  		[]string{
   356  			`Has unexported fields`,
   357  		},
   358  	},
   359  	// Unexported type with -u.
   360  	{
   361  		"unexported type with -u",
   362  		[]string{"-u", p, `unexportedType`},
   363  		[]string{
   364  			`Comment about unexported type`, // Include comment.
   365  			`type unexportedType int`,       // Type definition.
   366  			`func \(unexportedType\) ExportedMethod\(\) bool`,
   367  			`func \(unexportedType\) unexportedMethod\(\) bool`,
   368  			`ExportedTypedConstant_unexported unexportedType = iota`,
   369  			`const unexportedTypedConstant unexportedType = 1`,
   370  		},
   371  		nil,
   372  	},
   373  
   374  	// Interface.
   375  	{
   376  		"interface type",
   377  		[]string{p, `ExportedInterface`},
   378  		[]string{
   379  			`Comment about exported interface`, // Include comment.
   380  			`type ExportedInterface interface`, // Interface definition.
   381  			`Comment before exported method.*\n.*ExportedMethod\(\)` +
   382  				`.*Comment on line with exported method`,
   383  			`io.Reader.*Comment on line with embedded Reader.`,
   384  			`error.*Comment on line with embedded error.`,
   385  			`Has unexported methods`,
   386  		},
   387  		[]string{
   388  			`unexportedField`,               // No unexported field.
   389  			`Comment about exported method`, // No comment about exported method.
   390  			`unexportedMethod`,              // No unexported method.
   391  			`unexportedTypedConstant`,       // No unexported constant.
   392  		},
   393  	},
   394  	// Interface -u with unexported methods.
   395  	{
   396  		"interface type with unexported methods and -u",
   397  		[]string{"-u", p, `ExportedInterface`},
   398  		[]string{
   399  			`Comment about exported interface`, // Include comment.
   400  			`type ExportedInterface interface`, // Interface definition.
   401  			`Comment before exported method.*\n.*ExportedMethod\(\)` +
   402  				`.*Comment on line with exported method`,
   403  			`unexportedMethod\(\).*Comment on line with unexported method.`,
   404  			`io.Reader.*Comment on line with embedded Reader.`,
   405  			`error.*Comment on line with embedded error.`,
   406  		},
   407  		[]string{
   408  			`Has unexported methods`,
   409  		},
   410  	},
   411  
   412  	// Interface method.
   413  	{
   414  		"interface method",
   415  		[]string{p, `ExportedInterface.ExportedMethod`},
   416  		[]string{
   417  			`Comment before exported method.*\n.*ExportedMethod\(\)` +
   418  				`.*Comment on line with exported method`,
   419  		},
   420  		[]string{
   421  			`Comment about exported interface.`,
   422  		},
   423  	},
   424  
   425  	// Method.
   426  	{
   427  		"method",
   428  		[]string{p, `ExportedType.ExportedMethod`},
   429  		[]string{
   430  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   431  			`Comment about exported method.`,
   432  		},
   433  		nil,
   434  	},
   435  	// Method  with -u.
   436  	{
   437  		"method with -u",
   438  		[]string{"-u", p, `ExportedType.unexportedMethod`},
   439  		[]string{
   440  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   441  			`Comment about unexported method.`,
   442  		},
   443  		nil,
   444  	},
   445  
   446  	// Field.
   447  	{
   448  		"field",
   449  		[]string{p, `ExportedType.ExportedField`},
   450  		[]string{
   451  			`type ExportedType struct`,
   452  			`ExportedField int`,
   453  			`Comment before exported field.`,
   454  			`Comment on line with exported field.`,
   455  			`other fields elided`,
   456  		},
   457  		nil,
   458  	},
   459  
   460  	// Field with -u.
   461  	{
   462  		"method with -u",
   463  		[]string{"-u", p, `ExportedType.unexportedField`},
   464  		[]string{
   465  			`unexportedField int`,
   466  			`Comment on line with unexported field.`,
   467  		},
   468  		nil,
   469  	},
   470  
   471  	// Field of struct with only one field.
   472  	{
   473  		"single-field struct",
   474  		[]string{p, `ExportedStructOneField.OnlyField`},
   475  		[]string{`the only field`},
   476  		[]string{`other fields elided`},
   477  	},
   478  
   479  	// Case matching off.
   480  	{
   481  		"case matching off",
   482  		[]string{p, `casematch`},
   483  		[]string{
   484  			`CaseMatch`,
   485  			`Casematch`,
   486  		},
   487  		nil,
   488  	},
   489  
   490  	// Case matching on.
   491  	{
   492  		"case matching on",
   493  		[]string{"-c", p, `Casematch`},
   494  		[]string{
   495  			`Casematch`,
   496  		},
   497  		[]string{
   498  			`CaseMatch`,
   499  		},
   500  	},
   501  
   502  	// No dups with -u. Issue 21797.
   503  	{
   504  		"case matching on, no dups",
   505  		[]string{"-u", p, `duplicate`},
   506  		[]string{
   507  			`Duplicate`,
   508  			`duplicate`,
   509  		},
   510  		[]string{
   511  			"\\)\n+const", // This will appear if the const decl appears twice.
   512  		},
   513  	},
   514  	{
   515  		"non-imported: pkg.sym",
   516  		[]string{"nested.Foo"},
   517  		[]string{"Foo struct"},
   518  		nil,
   519  	},
   520  	{
   521  		"non-imported: pkg only",
   522  		[]string{"nested"},
   523  		[]string{"Foo struct"},
   524  		nil,
   525  	},
   526  	{
   527  		"non-imported: pkg sym",
   528  		[]string{"nested", "Foo"},
   529  		[]string{"Foo struct"},
   530  		nil,
   531  	},
   532  }
   533  
   534  func TestDoc(t *testing.T) {
   535  	maybeSkip(t)
   536  	for _, test := range tests {
   537  		var b bytes.Buffer
   538  		var flagSet flag.FlagSet
   539  		err := do(&b, &flagSet, test.args)
   540  		if err != nil {
   541  			t.Fatalf("%s %v: %s\n", test.name, test.args, err)
   542  		}
   543  		output := b.Bytes()
   544  		failed := false
   545  		for j, yes := range test.yes {
   546  			re, err := regexp.Compile(yes)
   547  			if err != nil {
   548  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
   549  			}
   550  			if !re.Match(output) {
   551  				t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
   552  				failed = true
   553  			}
   554  		}
   555  		for j, no := range test.no {
   556  			re, err := regexp.Compile(no)
   557  			if err != nil {
   558  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
   559  			}
   560  			if re.Match(output) {
   561  				t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
   562  				failed = true
   563  			}
   564  		}
   565  		if failed {
   566  			t.Logf("\n%s", output)
   567  		}
   568  	}
   569  }
   570  
   571  // Test the code to try multiple packages. Our test case is
   572  //	go doc rand.Float64
   573  // This needs to find math/rand.Float64; however crypto/rand, which doesn't
   574  // have the symbol, usually appears first in the directory listing.
   575  func TestMultiplePackages(t *testing.T) {
   576  	if testing.Short() {
   577  		t.Skip("scanning file system takes too long")
   578  	}
   579  	maybeSkip(t)
   580  	var b bytes.Buffer // We don't care about the output.
   581  	// Make sure crypto/rand does not have the symbol.
   582  	{
   583  		var flagSet flag.FlagSet
   584  		err := do(&b, &flagSet, []string{"crypto/rand.float64"})
   585  		if err == nil {
   586  			t.Errorf("expected error from crypto/rand.float64")
   587  		} else if !strings.Contains(err.Error(), "no symbol float64") {
   588  			t.Errorf("unexpected error %q from crypto/rand.float64", err)
   589  		}
   590  	}
   591  	// Make sure math/rand does have the symbol.
   592  	{
   593  		var flagSet flag.FlagSet
   594  		err := do(&b, &flagSet, []string{"math/rand.float64"})
   595  		if err != nil {
   596  			t.Errorf("unexpected error %q from math/rand.float64", err)
   597  		}
   598  	}
   599  	// Try the shorthand.
   600  	{
   601  		var flagSet flag.FlagSet
   602  		err := do(&b, &flagSet, []string{"rand.float64"})
   603  		if err != nil {
   604  			t.Errorf("unexpected error %q from rand.float64", err)
   605  		}
   606  	}
   607  	// Now try a missing symbol. We should see both packages in the error.
   608  	{
   609  		var flagSet flag.FlagSet
   610  		err := do(&b, &flagSet, []string{"rand.doesnotexit"})
   611  		if err == nil {
   612  			t.Errorf("expected error from rand.doesnotexit")
   613  		} else {
   614  			errStr := err.Error()
   615  			if !strings.Contains(errStr, "no symbol") {
   616  				t.Errorf("error %q should contain 'no symbol", errStr)
   617  			}
   618  			if !strings.Contains(errStr, "crypto/rand") {
   619  				t.Errorf("error %q should contain crypto/rand", errStr)
   620  			}
   621  			if !strings.Contains(errStr, "math/rand") {
   622  				t.Errorf("error %q should contain math/rand", errStr)
   623  			}
   624  		}
   625  	}
   626  }
   627  
   628  // Test the code to look up packages when given two args. First test case is
   629  //	go doc binary BigEndian
   630  // This needs to find encoding/binary.BigEndian, which means
   631  // finding the package encoding/binary given only "binary".
   632  // Second case is
   633  //	go doc rand Float64
   634  // which again needs to find math/rand and not give up after crypto/rand,
   635  // which has no such function.
   636  func TestTwoArgLookup(t *testing.T) {
   637  	if testing.Short() {
   638  		t.Skip("scanning file system takes too long")
   639  	}
   640  	maybeSkip(t)
   641  	var b bytes.Buffer // We don't care about the output.
   642  	{
   643  		var flagSet flag.FlagSet
   644  		err := do(&b, &flagSet, []string{"binary", "BigEndian"})
   645  		if err != nil {
   646  			t.Errorf("unexpected error %q from binary BigEndian", err)
   647  		}
   648  	}
   649  	{
   650  		var flagSet flag.FlagSet
   651  		err := do(&b, &flagSet, []string{"rand", "Float64"})
   652  		if err != nil {
   653  			t.Errorf("unexpected error %q from rand Float64", err)
   654  		}
   655  	}
   656  	{
   657  		var flagSet flag.FlagSet
   658  		err := do(&b, &flagSet, []string{"bytes", "Foo"})
   659  		if err == nil {
   660  			t.Errorf("expected error from bytes Foo")
   661  		} else if !strings.Contains(err.Error(), "no symbol Foo") {
   662  			t.Errorf("unexpected error %q from bytes Foo", err)
   663  		}
   664  	}
   665  	{
   666  		var flagSet flag.FlagSet
   667  		err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"})
   668  		if err == nil {
   669  			// actually present in the user's filesystem
   670  		} else if !strings.Contains(err.Error(), "no such package") {
   671  			t.Errorf("unexpected error %q from nosuchpackage Foo", err)
   672  		}
   673  	}
   674  }
   675  
   676  // Test the code to look up packages when the first argument starts with "./".
   677  // Our test case is in effect "cd src/text; doc ./template". This should get
   678  // text/template but before Issue 23383 was fixed would give html/template.
   679  func TestDotSlashLookup(t *testing.T) {
   680  	if testing.Short() {
   681  		t.Skip("scanning file system takes too long")
   682  	}
   683  	maybeSkip(t)
   684  	where := pwd()
   685  	defer func() {
   686  		if err := os.Chdir(where); err != nil {
   687  			t.Fatal(err)
   688  		}
   689  	}()
   690  	if err := os.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")); err != nil {
   691  		t.Fatal(err)
   692  	}
   693  	var b bytes.Buffer
   694  	var flagSet flag.FlagSet
   695  	err := do(&b, &flagSet, []string{"./template"})
   696  	if err != nil {
   697  		t.Errorf("unexpected error %q from ./template", err)
   698  	}
   699  	// The output should contain information about the text/template package.
   700  	const want = `package template // import "text/template"`
   701  	output := b.String()
   702  	if !strings.HasPrefix(output, want) {
   703  		t.Fatalf("wrong package: %.*q...", len(want), output)
   704  	}
   705  }
   706  
   707  type trimTest struct {
   708  	path   string
   709  	prefix string
   710  	result string
   711  	ok     bool
   712  }
   713  
   714  var trimTests = []trimTest{
   715  	{"", "", "", true},
   716  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
   717  	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
   718  	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
   719  	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
   720  }
   721  
   722  func TestTrim(t *testing.T) {
   723  	for _, test := range trimTests {
   724  		result, ok := trim(test.path, test.prefix)
   725  		if ok != test.ok {
   726  			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
   727  			continue
   728  		}
   729  		if result != test.result {
   730  			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
   731  			continue
   732  		}
   733  	}
   734  }
   735  

View as plain text