github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/cmd/doc/doc_test.go (about)

     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  const (
    26  	dataDir = "testdata"
    27  	binary  = "testdoc"
    28  )
    29  
    30  type test struct {
    31  	name string
    32  	args []string // Arguments to "[go] doc".
    33  	yes  []string // Regular expressions that should match.
    34  	no   []string // Regular expressions that should not match.
    35  }
    36  
    37  const p = "cmd/doc/testdata"
    38  
    39  var tests = []test{
    40  	// Sanity check.
    41  	{
    42  		"sanity check",
    43  		[]string{p},
    44  		[]string{`type ExportedType struct`},
    45  		nil,
    46  	},
    47  
    48  	// Package dump includes import, package statement.
    49  	{
    50  		"package clause",
    51  		[]string{p},
    52  		[]string{`package pkg.*cmd/doc/testdata`},
    53  		nil,
    54  	},
    55  
    56  	// Constants.
    57  	// Package dump
    58  	{
    59  		"full package",
    60  		[]string{p},
    61  		[]string{
    62  			`Package comment`,
    63  			`const ExportedConstant = 1`,                            // Simple constant.
    64  			`const ConstOne = 1`,                                    // First entry in constant block.
    65  			`const ConstFive ...`,                                   // From block starting with unexported constant.
    66  			`var ExportedVariable = 1`,                              // Simple variable.
    67  			`var VarOne = 1`,                                        // First entry in variable block.
    68  			`func ExportedFunc\(a int\) bool`,                       // Function.
    69  			`type ExportedType struct { ... }`,                      // Exported type.
    70  			`const ExportedTypedConstant ExportedType = iota`,       // Typed constant.
    71  			`const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type.
    72  		},
    73  		[]string{
    74  			`const internalConstant = 2`,        // No internal constants.
    75  			`var internalVariable = 2`,          // No internal variables.
    76  			`func internalFunc(a int) bool`,     // No internal functions.
    77  			`Comment about exported constant`,   // No comment for single constant.
    78  			`Comment about exported variable`,   // No comment for single variable.
    79  			`Comment about block of constants.`, // No comment for constant block.
    80  			`Comment about block of variables.`, // No comment for variable block.
    81  			`Comment before ConstOne`,           // No comment for first entry in constant block.
    82  			`Comment before VarOne`,             // No comment for first entry in variable block.
    83  			`ConstTwo = 2`,                      // No second entry in constant block.
    84  			`VarTwo = 2`,                        // No second entry in variable block.
    85  			`VarFive = 5`,                       // From block starting with unexported variable.
    86  			`type unexportedType`,               // No unexported type.
    87  			`unexportedTypedConstant`,           // No unexported typed constant.
    88  			`Field`,                             // No fields.
    89  			`Method`,                            // No methods.
    90  		},
    91  	},
    92  	// Package dump -u
    93  	{
    94  		"full package with u",
    95  		[]string{`-u`, p},
    96  		[]string{
    97  			`const ExportedConstant = 1`,      // Simple constant.
    98  			`const internalConstant = 2`,      // Internal constants.
    99  			`func internalFunc\(a int\) bool`, // Internal functions.
   100  		},
   101  		[]string{
   102  			`Comment about exported constant`,  // No comment for simple constant.
   103  			`Comment about block of constants`, // No comment for constant block.
   104  			`Comment about internal function`,  // No comment for internal function.
   105  		},
   106  	},
   107  
   108  	// Single constant.
   109  	{
   110  		"single constant",
   111  		[]string{p, `ExportedConstant`},
   112  		[]string{
   113  			`Comment about exported constant`, // Include comment.
   114  			`const ExportedConstant = 1`,
   115  		},
   116  		nil,
   117  	},
   118  	// Single constant -u.
   119  	{
   120  		"single constant with -u",
   121  		[]string{`-u`, p, `internalConstant`},
   122  		[]string{
   123  			`Comment about internal constant`, // Include comment.
   124  			`const internalConstant = 2`,
   125  		},
   126  		nil,
   127  	},
   128  	// Block of constants.
   129  	{
   130  		"block of constants",
   131  		[]string{p, `ConstTwo`},
   132  		[]string{
   133  			`Comment before ConstOne.\n.*ConstOne = 1`,    // First...
   134  			`ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up.
   135  			`Comment about block of constants`,            // Comment does too.
   136  		},
   137  		[]string{
   138  			`constThree`, // No unexported constant.
   139  		},
   140  	},
   141  	// Block of constants -u.
   142  	{
   143  		"block of constants with -u",
   144  		[]string{"-u", p, `constThree`},
   145  		[]string{
   146  			`constThree = 3.*Comment on line with constThree`,
   147  		},
   148  		nil,
   149  	},
   150  
   151  	// Single variable.
   152  	{
   153  		"single variable",
   154  		[]string{p, `ExportedVariable`},
   155  		[]string{
   156  			`ExportedVariable`, // Include comment.
   157  			`var ExportedVariable = 1`,
   158  		},
   159  		nil,
   160  	},
   161  	// Single variable -u.
   162  	{
   163  		"single variable with -u",
   164  		[]string{`-u`, p, `internalVariable`},
   165  		[]string{
   166  			`Comment about internal variable`, // Include comment.
   167  			`var internalVariable = 2`,
   168  		},
   169  		nil,
   170  	},
   171  	// Block of variables.
   172  	{
   173  		"block of variables",
   174  		[]string{p, `VarTwo`},
   175  		[]string{
   176  			`Comment before VarOne.\n.*VarOne = 1`,    // First...
   177  			`VarTwo = 2.*Comment on line with VarTwo`, // And second show up.
   178  			`Comment about block of variables`,        // Comment does too.
   179  		},
   180  		[]string{
   181  			`varThree= 3`, // No unexported variable.
   182  		},
   183  	},
   184  	// Block of variables -u.
   185  	{
   186  		"block of variables with -u",
   187  		[]string{"-u", p, `varThree`},
   188  		[]string{
   189  			`varThree = 3.*Comment on line with varThree`,
   190  		},
   191  		nil,
   192  	},
   193  
   194  	// Function.
   195  	{
   196  		"function",
   197  		[]string{p, `ExportedFunc`},
   198  		[]string{
   199  			`Comment about exported function`, // Include comment.
   200  			`func ExportedFunc\(a int\) bool`,
   201  		},
   202  		nil,
   203  	},
   204  	// Function -u.
   205  	{
   206  		"function with -u",
   207  		[]string{"-u", p, `internalFunc`},
   208  		[]string{
   209  			`Comment about internal function`, // Include comment.
   210  			`func internalFunc\(a int\) bool`,
   211  		},
   212  		nil,
   213  	},
   214  
   215  	// Type.
   216  	{
   217  		"type",
   218  		[]string{p, `ExportedType`},
   219  		[]string{
   220  			`Comment about exported type`, // Include comment.
   221  			`type ExportedType struct`,    // Type definition.
   222  			`Comment before exported field.*\n.*ExportedField +int`,
   223  			`Has unexported fields`,
   224  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   225  			`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
   226  			`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
   227  		},
   228  		[]string{
   229  			`unexportedField`,                // No unexported field.
   230  			`Comment about exported method.`, // No comment about exported method.
   231  			`unexportedMethod`,               // No unexported method.
   232  			`unexportedTypedConstant`,        // No unexported constant.
   233  		},
   234  	},
   235  	// Type -u with unexported fields.
   236  	{
   237  		"type with unexported fields and -u",
   238  		[]string{"-u", p, `ExportedType`},
   239  		[]string{
   240  			`Comment about exported type`, // Include comment.
   241  			`type ExportedType struct`,    // Type definition.
   242  			`Comment before exported field.*\n.*ExportedField +int`,
   243  			`unexportedField int.*Comment on line with unexported field.`,
   244  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   245  			`unexportedTypedConstant`,
   246  		},
   247  		[]string{
   248  			`Has unexported fields`,
   249  		},
   250  	},
   251  	// Unexported type with -u.
   252  	{
   253  		"unexported type with -u",
   254  		[]string{"-u", p, `unexportedType`},
   255  		[]string{
   256  			`Comment about unexported type`, // Include comment.
   257  			`type unexportedType int`,       // Type definition.
   258  			`func \(unexportedType\) ExportedMethod\(\) bool`,
   259  			`func \(unexportedType\) unexportedMethod\(\) bool`,
   260  			`ExportedTypedConstant_unexported unexportedType = iota`,
   261  			`const unexportedTypedConstant unexportedType = 1`,
   262  		},
   263  		nil,
   264  	},
   265  
   266  	// Method.
   267  	{
   268  		"method",
   269  		[]string{p, `ExportedType.ExportedMethod`},
   270  		[]string{
   271  			`func \(ExportedType\) ExportedMethod\(a int\) bool`,
   272  			`Comment about exported method.`,
   273  		},
   274  		nil,
   275  	},
   276  	// Method  with -u.
   277  	{
   278  		"method with -u",
   279  		[]string{"-u", p, `ExportedType.unexportedMethod`},
   280  		[]string{
   281  			`func \(ExportedType\) unexportedMethod\(a int\) bool`,
   282  			`Comment about unexported method.`,
   283  		},
   284  		nil,
   285  	},
   286  
   287  	// Case matching off.
   288  	{
   289  		"case matching off",
   290  		[]string{p, `casematch`},
   291  		[]string{
   292  			`CaseMatch`,
   293  			`Casematch`,
   294  		},
   295  		nil,
   296  	},
   297  
   298  	// Case matching on.
   299  	{
   300  		"case matching on",
   301  		[]string{"-c", p, `Casematch`},
   302  		[]string{
   303  			`Casematch`,
   304  		},
   305  		[]string{
   306  			`CaseMatch`,
   307  		},
   308  	},
   309  }
   310  
   311  func TestDoc(t *testing.T) {
   312  	maybeSkip(t)
   313  	for _, test := range tests {
   314  		var b bytes.Buffer
   315  		var flagSet flag.FlagSet
   316  		err := do(&b, &flagSet, test.args)
   317  		if err != nil {
   318  			t.Fatalf("%s: %s\n", test.name, err)
   319  		}
   320  		output := b.Bytes()
   321  		failed := false
   322  		for j, yes := range test.yes {
   323  			re, err := regexp.Compile(yes)
   324  			if err != nil {
   325  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
   326  			}
   327  			if !re.Match(output) {
   328  				t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
   329  				failed = true
   330  			}
   331  		}
   332  		for j, no := range test.no {
   333  			re, err := regexp.Compile(no)
   334  			if err != nil {
   335  				t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
   336  			}
   337  			if re.Match(output) {
   338  				t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
   339  				failed = true
   340  			}
   341  		}
   342  		if failed {
   343  			t.Logf("\n%s", output)
   344  		}
   345  	}
   346  }
   347  
   348  // Test the code to try multiple packages. Our test case is
   349  //	go doc rand.Float64
   350  // This needs to find math/rand.Float64; however crypto/rand, which doesn't
   351  // have the symbol, usually appears first in the directory listing.
   352  func TestMultiplePackages(t *testing.T) {
   353  	if testing.Short() {
   354  		t.Skip("scanning file system takes too long")
   355  	}
   356  	maybeSkip(t)
   357  	var b bytes.Buffer // We don't care about the output.
   358  	// Make sure crypto/rand does not have the symbol.
   359  	{
   360  		var flagSet flag.FlagSet
   361  		err := do(&b, &flagSet, []string{"crypto/rand.float64"})
   362  		if err == nil {
   363  			t.Errorf("expected error from crypto/rand.float64")
   364  		} else if !strings.Contains(err.Error(), "no symbol float64") {
   365  			t.Errorf("unexpected error %q from crypto/rand.float64", err)
   366  		}
   367  	}
   368  	// Make sure math/rand does have the symbol.
   369  	{
   370  		var flagSet flag.FlagSet
   371  		err := do(&b, &flagSet, []string{"math/rand.float64"})
   372  		if err != nil {
   373  			t.Errorf("unexpected error %q from math/rand.float64", err)
   374  		}
   375  	}
   376  	// Try the shorthand.
   377  	{
   378  		var flagSet flag.FlagSet
   379  		err := do(&b, &flagSet, []string{"rand.float64"})
   380  		if err != nil {
   381  			t.Errorf("unexpected error %q from rand.float64", err)
   382  		}
   383  	}
   384  	// Now try a missing symbol. We should see both packages in the error.
   385  	{
   386  		var flagSet flag.FlagSet
   387  		err := do(&b, &flagSet, []string{"rand.doesnotexit"})
   388  		if err == nil {
   389  			t.Errorf("expected error from rand.doesnotexit")
   390  		} else {
   391  			errStr := err.Error()
   392  			if !strings.Contains(errStr, "no symbol") {
   393  				t.Errorf("error %q should contain 'no symbol", errStr)
   394  			}
   395  			if !strings.Contains(errStr, "crypto/rand") {
   396  				t.Errorf("error %q should contain crypto/rand", errStr)
   397  			}
   398  			if !strings.Contains(errStr, "math/rand") {
   399  				t.Errorf("error %q should contain math/rand", errStr)
   400  			}
   401  		}
   402  	}
   403  }
   404  
   405  type trimTest struct {
   406  	path   string
   407  	prefix string
   408  	result string
   409  	ok     bool
   410  }
   411  
   412  var trimTests = []trimTest{
   413  	{"", "", "", true},
   414  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
   415  	{"/usr/gopher/bar", "/usr/gopher", "bar", true},
   416  	{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
   417  	{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
   418  	{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
   419  }
   420  
   421  func TestTrim(t *testing.T) {
   422  	for _, test := range trimTests {
   423  		result, ok := trim(test.path, test.prefix)
   424  		if ok != test.ok {
   425  			t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
   426  			continue
   427  		}
   428  		if result != test.result {
   429  			t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
   430  			continue
   431  		}
   432  	}
   433  }