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

View as plain text