summaryrefslogtreecommitdiffstats
path: root/src/cmd/doc/doc_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/doc/doc_test.go')
-rw-r--r--src/cmd/doc/doc_test.go1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
new file mode 100644
index 0000000..6a259ae
--- /dev/null
+++ b/src/cmd/doc/doc_test.go
@@ -0,0 +1,1075 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "go/build"
+ "internal/testenv"
+ "log"
+ "os"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ // Clear GOPATH so we don't access the user's own packages in the test.
+ buildCtx.GOPATH = ""
+ testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
+
+ // Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was
+ // built with -trimpath). dirsInit would identify it using 'go env GOROOT',
+ // but we can't be sure that the 'go' in $PATH is the right one either.
+ buildCtx.GOROOT = testenv.GOROOT(nil)
+ build.Default.GOROOT = testenv.GOROOT(nil)
+
+ // Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test.
+ // Normally testdata directories are ignored, but sending it to dirs.scan directly is
+ // a hack that works around the check.
+ testdataDir, err := filepath.Abs("testdata")
+ if err != nil {
+ panic(err)
+ }
+ dirsInit(
+ Dir{importPath: "testdata", dir: testdataDir},
+ Dir{importPath: "testdata/nested", dir: filepath.Join(testdataDir, "nested")},
+ Dir{importPath: "testdata/nested/nested", dir: filepath.Join(testdataDir, "nested", "nested")})
+
+ os.Exit(m.Run())
+}
+
+func maybeSkip(t *testing.T) {
+ if runtime.GOOS == "ios" {
+ t.Skip("iOS does not have a full file tree")
+ }
+}
+
+type isDotSlashTest struct {
+ str string
+ result bool
+}
+
+var isDotSlashTests = []isDotSlashTest{
+ {``, false},
+ {`x`, false},
+ {`...`, false},
+ {`.../`, false},
+ {`...\`, false},
+
+ {`.`, true},
+ {`./`, true},
+ {`.\`, true},
+ {`./x`, true},
+ {`.\x`, true},
+
+ {`..`, true},
+ {`../`, true},
+ {`..\`, true},
+ {`../x`, true},
+ {`..\x`, true},
+}
+
+func TestIsDotSlashPath(t *testing.T) {
+ for _, test := range isDotSlashTests {
+ if result := isDotSlash(test.str); result != test.result {
+ t.Errorf("isDotSlash(%q) = %t; expected %t", test.str, result, test.result)
+ }
+ }
+}
+
+type test struct {
+ name string
+ args []string // Arguments to "[go] doc".
+ yes []string // Regular expressions that should match.
+ no []string // Regular expressions that should not match.
+}
+
+const p = "cmd/doc/testdata"
+
+var tests = []test{
+ // Sanity check.
+ {
+ "sanity check",
+ []string{p},
+ []string{`type ExportedType struct`},
+ nil,
+ },
+
+ // Package dump includes import, package statement.
+ {
+ "package clause",
+ []string{p},
+ []string{`package pkg.*cmd/doc/testdata`},
+ nil,
+ },
+
+ // Constants.
+ // Package dump
+ {
+ "full package",
+ []string{p},
+ []string{
+ `Package comment`,
+ `const ExportedConstant = 1`, // Simple constant.
+ `const ConstOne = 1`, // First entry in constant block.
+ `const ConstFive ...`, // From block starting with unexported constant.
+ `var ExportedVariable = 1`, // Simple variable.
+ `var VarOne = 1`, // First entry in variable block.
+ `func ExportedFunc\(a int\) bool`, // Function.
+ `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
+ `type ExportedType struct{ ... }`, // Exported type.
+ `const ExportedTypedConstant ExportedType = iota`, // Typed constant.
+ `const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type.
+ `const ConstLeft2 uint64 ...`, // Typed constant using unexported iota.
+ `const ConstGroup1 unexportedType = iota ...`, // Typed constant using unexported type.
+ `const ConstGroup4 ExportedType = ExportedType{}`, // Typed constant using exported type.
+ `const MultiLineConst = ...`, // Multi line constant.
+ `var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
+ `func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
+ `var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`, // Long list of arguments.
+ `type T1 = T2`, // Type alias
+ `type SimpleConstraint interface{ ... }`,
+ `type TildeConstraint interface{ ... }`,
+ `type StructConstraint interface{ ... }`,
+ },
+ []string{
+ `const internalConstant = 2`, // No internal constants.
+ `var internalVariable = 2`, // No internal variables.
+ `func internalFunc(a int) bool`, // No internal functions.
+ `Comment about exported constant`, // No comment for single constant.
+ `Comment about exported variable`, // No comment for single variable.
+ `Comment about block of constants`, // No comment for constant block.
+ `Comment about block of variables`, // No comment for variable block.
+ `Comment before ConstOne`, // No comment for first entry in constant block.
+ `Comment before VarOne`, // No comment for first entry in variable block.
+ `ConstTwo = 2`, // No second entry in constant block.
+ `VarTwo = 2`, // No second entry in variable block.
+ `VarFive = 5`, // From block starting with unexported variable.
+ `type unexportedType`, // No unexported type.
+ `unexportedTypedConstant`, // No unexported typed constant.
+ `\bField`, // No fields.
+ `Method`, // No methods.
+ `someArgument[5-8]`, // No truncated arguments.
+ `type T1 T2`, // Type alias does not display as type declaration.
+ },
+ },
+ // Package dump -all
+ {
+ "full package",
+ []string{"-all", p},
+ []string{
+ `package pkg .*import`,
+ `Package comment`,
+ `CONSTANTS`,
+ `Comment before ConstOne`,
+ `ConstOne = 1`,
+ `ConstTwo = 2 // Comment on line with ConstTwo`,
+ `ConstFive`,
+ `ConstSix`,
+ `Const block where first entry is unexported`,
+ `ConstLeft2, constRight2 uint64`,
+ `constLeft3, ConstRight3`,
+ `ConstLeft4, ConstRight4`,
+ `Duplicate = iota`,
+ `const CaseMatch = 1`,
+ `const Casematch = 2`,
+ `const ExportedConstant = 1`,
+ `const MultiLineConst = `,
+ `MultiLineString1`,
+ `VARIABLES`,
+ `Comment before VarOne`,
+ `VarOne = 1`,
+ `Comment about block of variables`,
+ `VarFive = 5`,
+ `var ExportedVariable = 1`,
+ `var ExportedVarOfUnExported unexportedType`,
+ `var LongLine = newLongLine\(`,
+ `var MultiLineVar = map\[struct {`,
+ `FUNCTIONS`,
+ `func ExportedFunc\(a int\) bool`,
+ `Comment about exported function`,
+ `func MultiLineFunc\(x interface`,
+ `func ReturnUnexported\(\) unexportedType`,
+ `TYPES`,
+ `type ExportedInterface interface`,
+ `type ExportedStructOneField struct`,
+ `type ExportedType struct`,
+ `Comment about exported type`,
+ `const ConstGroup4 ExportedType = ExportedType`,
+ `ExportedTypedConstant ExportedType = iota`,
+ `Constants tied to ExportedType`,
+ `func ExportedTypeConstructor\(\) \*ExportedType`,
+ `Comment about constructor for exported type`,
+ `func ReturnExported\(\) ExportedType`,
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `Comment about exported method`,
+ `type T1 = T2`,
+ `type T2 int`,
+ `type SimpleConstraint interface {`,
+ `type TildeConstraint interface {`,
+ `type StructConstraint interface {`,
+ },
+ []string{
+ `constThree`,
+ `_, _ uint64 = 2 \* iota, 1 << iota`,
+ `constLeft1, constRight1`,
+ `duplicate`,
+ `varFour`,
+ `func internalFunc`,
+ `unexportedField`,
+ `func \(unexportedType\)`,
+ },
+ },
+ // Package with just the package declaration. Issue 31457.
+ {
+ "only package declaration",
+ []string{"-all", p + "/nested/empty"},
+ []string{`package empty .*import`},
+ nil,
+ },
+ // Package dump -short
+ {
+ "full package with -short",
+ []string{`-short`, p},
+ []string{
+ `const ExportedConstant = 1`, // Simple constant.
+ `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
+ },
+ []string{
+ `MultiLine(String|Method|Field)`, // No data from multi line portions.
+ },
+ },
+ // Package dump -u
+ {
+ "full package with u",
+ []string{`-u`, p},
+ []string{
+ `const ExportedConstant = 1`, // Simple constant.
+ `const internalConstant = 2`, // Internal constants.
+ `func internalFunc\(a int\) bool`, // Internal functions.
+ `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
+ },
+ []string{
+ `Comment about exported constant`, // No comment for simple constant.
+ `Comment about block of constants`, // No comment for constant block.
+ `Comment about internal function`, // No comment for internal function.
+ `MultiLine(String|Method|Field)`, // No data from multi line portions.
+ },
+ },
+ // Package dump -u -all
+ {
+ "full package",
+ []string{"-u", "-all", p},
+ []string{
+ `package pkg .*import`,
+ `Package comment`,
+ `CONSTANTS`,
+ `Comment before ConstOne`,
+ `ConstOne += 1`,
+ `ConstTwo += 2 // Comment on line with ConstTwo`,
+ `constThree = 3 // Comment on line with constThree`,
+ `ConstFive`,
+ `const internalConstant += 2`,
+ `Comment about internal constant`,
+ `VARIABLES`,
+ `Comment before VarOne`,
+ `VarOne += 1`,
+ `Comment about block of variables`,
+ `varFour += 4`,
+ `VarFive += 5`,
+ `varSix += 6`,
+ `var ExportedVariable = 1`,
+ `var LongLine = newLongLine\(`,
+ `var MultiLineVar = map\[struct {`,
+ `var internalVariable = 2`,
+ `Comment about internal variable`,
+ `FUNCTIONS`,
+ `func ExportedFunc\(a int\) bool`,
+ `Comment about exported function`,
+ `func MultiLineFunc\(x interface`,
+ `func internalFunc\(a int\) bool`,
+ `Comment about internal function`,
+ `func newLongLine\(ss .*string\)`,
+ `TYPES`,
+ `type ExportedType struct`,
+ `type T1 = T2`,
+ `type T2 int`,
+ `type unexportedType int`,
+ `Comment about unexported type`,
+ `ConstGroup1 unexportedType = iota`,
+ `ConstGroup2`,
+ `ConstGroup3`,
+ `ExportedTypedConstant_unexported unexportedType = iota`,
+ `Constants tied to unexportedType`,
+ `const unexportedTypedConstant unexportedType = 1`,
+ `func ReturnUnexported\(\) unexportedType`,
+ `func \(unexportedType\) ExportedMethod\(\) bool`,
+ `func \(unexportedType\) unexportedMethod\(\) bool`,
+ },
+ nil,
+ },
+
+ // Single constant.
+ {
+ "single constant",
+ []string{p, `ExportedConstant`},
+ []string{
+ `Comment about exported constant`, // Include comment.
+ `const ExportedConstant = 1`,
+ },
+ nil,
+ },
+ // Single constant -u.
+ {
+ "single constant with -u",
+ []string{`-u`, p, `internalConstant`},
+ []string{
+ `Comment about internal constant`, // Include comment.
+ `const internalConstant = 2`,
+ },
+ nil,
+ },
+ // Block of constants.
+ {
+ "block of constants",
+ []string{p, `ConstTwo`},
+ []string{
+ `Comment before ConstOne.\n.*ConstOne = 1`, // First...
+ `ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up.
+ `Comment about block of constants`, // Comment does too.
+ },
+ []string{
+ `constThree`, // No unexported constant.
+ },
+ },
+ // Block of constants -u.
+ {
+ "block of constants with -u",
+ []string{"-u", p, `constThree`},
+ []string{
+ `constThree = 3.*Comment on line with constThree`,
+ },
+ nil,
+ },
+ // Block of constants -src.
+ {
+ "block of constants with -src",
+ []string{"-src", p, `ConstTwo`},
+ []string{
+ `Comment about block of constants`, // Top comment.
+ `ConstOne.*=.*1`, // Each constant seen.
+ `ConstTwo.*=.*2.*Comment on line with ConstTwo`,
+ `constThree`, // Even unexported constants.
+ },
+ nil,
+ },
+ // Block of constants with carryover type from unexported field.
+ {
+ "block of constants with carryover type",
+ []string{p, `ConstLeft2`},
+ []string{
+ `ConstLeft2, constRight2 uint64`,
+ `constLeft3, ConstRight3`,
+ `ConstLeft4, ConstRight4`,
+ },
+ nil,
+ },
+ // Block of constants -u with carryover type from unexported field.
+ {
+ "block of constants with carryover type",
+ []string{"-u", p, `ConstLeft2`},
+ []string{
+ `_, _ uint64 = 2 \* iota, 1 << iota`,
+ `constLeft1, constRight1`,
+ `ConstLeft2, constRight2`,
+ `constLeft3, ConstRight3`,
+ `ConstLeft4, ConstRight4`,
+ },
+ nil,
+ },
+
+ // Single variable.
+ {
+ "single variable",
+ []string{p, `ExportedVariable`},
+ []string{
+ `ExportedVariable`, // Include comment.
+ `var ExportedVariable = 1`,
+ },
+ nil,
+ },
+ // Single variable -u.
+ {
+ "single variable with -u",
+ []string{`-u`, p, `internalVariable`},
+ []string{
+ `Comment about internal variable`, // Include comment.
+ `var internalVariable = 2`,
+ },
+ nil,
+ },
+ // Block of variables.
+ {
+ "block of variables",
+ []string{p, `VarTwo`},
+ []string{
+ `Comment before VarOne.\n.*VarOne = 1`, // First...
+ `VarTwo = 2.*Comment on line with VarTwo`, // And second show up.
+ `Comment about block of variables`, // Comment does too.
+ },
+ []string{
+ `varThree= 3`, // No unexported variable.
+ },
+ },
+ // Block of variables -u.
+ {
+ "block of variables with -u",
+ []string{"-u", p, `varThree`},
+ []string{
+ `varThree = 3.*Comment on line with varThree`,
+ },
+ nil,
+ },
+
+ // Function.
+ {
+ "function",
+ []string{p, `ExportedFunc`},
+ []string{
+ `Comment about exported function`, // Include comment.
+ `func ExportedFunc\(a int\) bool`,
+ },
+ nil,
+ },
+ // Function -u.
+ {
+ "function with -u",
+ []string{"-u", p, `internalFunc`},
+ []string{
+ `Comment about internal function`, // Include comment.
+ `func internalFunc\(a int\) bool`,
+ },
+ nil,
+ },
+ // Function with -src.
+ {
+ "function with -src",
+ []string{"-src", p, `ExportedFunc`},
+ []string{
+ `Comment about exported function`, // Include comment.
+ `func ExportedFunc\(a int\) bool`,
+ `return true != false`, // Include body.
+ },
+ nil,
+ },
+
+ // Type.
+ {
+ "type",
+ []string{p, `ExportedType`},
+ []string{
+ `Comment about exported type`, // Include comment.
+ `type ExportedType struct`, // Type definition.
+ `Comment before exported field.*\n.*ExportedField +int` +
+ `.*Comment on line with exported field`,
+ `ExportedEmbeddedType.*Comment on line with exported embedded field`,
+ `Has unexported fields`,
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
+ `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
+ `io.Reader.*Comment on line with embedded Reader`,
+ },
+ []string{
+ `unexportedField`, // No unexported field.
+ `int.*embedded`, // No unexported embedded field.
+ `Comment about exported method`, // No comment about exported method.
+ `unexportedMethod`, // No unexported method.
+ `unexportedTypedConstant`, // No unexported constant.
+ `error`, // No embedded error.
+ },
+ },
+ // Type with -src. Will see unexported fields.
+ {
+ "type",
+ []string{"-src", p, `ExportedType`},
+ []string{
+ `Comment about exported type`, // Include comment.
+ `type ExportedType struct`, // Type definition.
+ `Comment before exported field`,
+ `ExportedField.*Comment on line with exported field`,
+ `ExportedEmbeddedType.*Comment on line with exported embedded field`,
+ `unexportedType.*Comment on line with unexported embedded field`,
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
+ `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
+ `io.Reader.*Comment on line with embedded Reader`,
+ },
+ []string{
+ `Comment about exported method`, // No comment about exported method.
+ `unexportedMethod`, // No unexported method.
+ `unexportedTypedConstant`, // No unexported constant.
+ },
+ },
+ // Type -all.
+ {
+ "type",
+ []string{"-all", p, `ExportedType`},
+ []string{
+ `type ExportedType struct {`, // Type definition as source.
+ `Comment about exported type`, // Include comment afterwards.
+ `const ConstGroup4 ExportedType = ExportedType\{\}`, // Related constants.
+ `ExportedTypedConstant ExportedType = iota`,
+ `Constants tied to ExportedType`,
+ `func ExportedTypeConstructor\(\) \*ExportedType`,
+ `Comment about constructor for exported type.`,
+ `func ReturnExported\(\) ExportedType`,
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `Comment about exported method.`,
+ `func \(ExportedType\) Uncommented\(a int\) bool\n\n`, // Ensure line gap after method with no comment
+ },
+ []string{
+ `unexportedType`,
+ },
+ },
+ // Type T1 dump (alias).
+ {
+ "type T1",
+ []string{p + ".T1"},
+ []string{
+ `type T1 = T2`,
+ },
+ []string{
+ `type T1 T2`,
+ `type ExportedType`,
+ },
+ },
+ // Type -u with unexported fields.
+ {
+ "type with unexported fields and -u",
+ []string{"-u", p, `ExportedType`},
+ []string{
+ `Comment about exported type`, // Include comment.
+ `type ExportedType struct`, // Type definition.
+ `Comment before exported field.*\n.*ExportedField +int`,
+ `unexportedField.*int.*Comment on line with unexported field`,
+ `ExportedEmbeddedType.*Comment on line with exported embedded field`,
+ `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field`,
+ `\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field`,
+ `unexportedType.*Comment on line with unexported embedded field`,
+ `\*unexportedType.*Comment on line with unexported embedded \*field`,
+ `io.Reader.*Comment on line with embedded Reader`,
+ `error.*Comment on line with embedded error`,
+ `func \(ExportedType\) unexportedMethod\(a int\) bool`,
+ `unexportedTypedConstant`,
+ },
+ []string{
+ `Has unexported fields`,
+ },
+ },
+ // Unexported type with -u.
+ {
+ "unexported type with -u",
+ []string{"-u", p, `unexportedType`},
+ []string{
+ `Comment about unexported type`, // Include comment.
+ `type unexportedType int`, // Type definition.
+ `func \(unexportedType\) ExportedMethod\(\) bool`,
+ `func \(unexportedType\) unexportedMethod\(\) bool`,
+ `ExportedTypedConstant_unexported unexportedType = iota`,
+ `const unexportedTypedConstant unexportedType = 1`,
+ },
+ nil,
+ },
+
+ // Interface.
+ {
+ "interface type",
+ []string{p, `ExportedInterface`},
+ []string{
+ `Comment about exported interface`, // Include comment.
+ `type ExportedInterface interface`, // Interface definition.
+ `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`,
+ `io.Reader.*Comment on line with embedded Reader`,
+ `error.*Comment on line with embedded error`,
+ `Has unexported methods`,
+ },
+ []string{
+ `unexportedField`, // No unexported field.
+ `Comment about exported method`, // No comment about exported method.
+ `unexportedMethod`, // No unexported method.
+ `unexportedTypedConstant`, // No unexported constant.
+ },
+ },
+ // Interface -u with unexported methods.
+ {
+ "interface type with unexported methods and -u",
+ []string{"-u", p, `ExportedInterface`},
+ []string{
+ `Comment about exported interface`, // Include comment.
+ `type ExportedInterface interface`, // Interface definition.
+ `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`,
+ `unexportedMethod\(\).*Comment on line with unexported method`,
+ `io.Reader.*Comment on line with embedded Reader`,
+ `error.*Comment on line with embedded error`,
+ },
+ []string{
+ `Has unexported methods`,
+ },
+ },
+
+ // Interface method.
+ {
+ "interface method",
+ []string{p, `ExportedInterface.ExportedMethod`},
+ []string{
+ `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`,
+ },
+ []string{
+ `Comment about exported interface`,
+ },
+ },
+ // Interface method at package level.
+ {
+ "interface method at package level",
+ []string{p, `ExportedMethod`},
+ []string{
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `Comment about exported method`,
+ },
+ []string{
+ `Comment before exported method.*\n.*ExportedMethod\(\)` +
+ `.*Comment on line with exported method`,
+ },
+ },
+
+ // Method.
+ {
+ "method",
+ []string{p, `ExportedType.ExportedMethod`},
+ []string{
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `Comment about exported method`,
+ },
+ nil,
+ },
+ // Method with -u.
+ {
+ "method with -u",
+ []string{"-u", p, `ExportedType.unexportedMethod`},
+ []string{
+ `func \(ExportedType\) unexportedMethod\(a int\) bool`,
+ `Comment about unexported method`,
+ },
+ nil,
+ },
+ // Method with -src.
+ {
+ "method with -src",
+ []string{"-src", p, `ExportedType.ExportedMethod`},
+ []string{
+ `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+ `Comment about exported method`,
+ `return true != true`,
+ },
+ nil,
+ },
+
+ // Field.
+ {
+ "field",
+ []string{p, `ExportedType.ExportedField`},
+ []string{
+ `type ExportedType struct`,
+ `ExportedField int`,
+ `Comment before exported field`,
+ `Comment on line with exported field`,
+ `other fields elided`,
+ },
+ nil,
+ },
+
+ // Field with -u.
+ {
+ "method with -u",
+ []string{"-u", p, `ExportedType.unexportedField`},
+ []string{
+ `unexportedField int`,
+ `Comment on line with unexported field`,
+ },
+ nil,
+ },
+
+ // Field of struct with only one field.
+ {
+ "single-field struct",
+ []string{p, `ExportedStructOneField.OnlyField`},
+ []string{`the only field`},
+ []string{`other fields elided`},
+ },
+
+ // Case matching off.
+ {
+ "case matching off",
+ []string{p, `casematch`},
+ []string{
+ `CaseMatch`,
+ `Casematch`,
+ },
+ nil,
+ },
+
+ // Case matching on.
+ {
+ "case matching on",
+ []string{"-c", p, `Casematch`},
+ []string{
+ `Casematch`,
+ },
+ []string{
+ `CaseMatch`,
+ },
+ },
+
+ // Merging comments with -src.
+ {
+ "merge comments with -src A",
+ []string{"-src", p + "/merge", `A`},
+ []string{
+ `A doc`,
+ `func A`,
+ `A comment`,
+ },
+ []string{
+ `Package A doc`,
+ `Package B doc`,
+ `B doc`,
+ `B comment`,
+ `B doc`,
+ },
+ },
+ {
+ "merge comments with -src B",
+ []string{"-src", p + "/merge", `B`},
+ []string{
+ `B doc`,
+ `func B`,
+ `B comment`,
+ },
+ []string{
+ `Package A doc`,
+ `Package B doc`,
+ `A doc`,
+ `A comment`,
+ `A doc`,
+ },
+ },
+
+ // No dups with -u. Issue 21797.
+ {
+ "case matching on, no dups",
+ []string{"-u", p, `duplicate`},
+ []string{
+ `Duplicate`,
+ `duplicate`,
+ },
+ []string{
+ "\\)\n+const", // This will appear if the const decl appears twice.
+ },
+ },
+ {
+ "non-imported: pkg.sym",
+ []string{"nested.Foo"},
+ []string{"Foo struct"},
+ nil,
+ },
+ {
+ "non-imported: pkg only",
+ []string{"nested"},
+ []string{"Foo struct"},
+ nil,
+ },
+ {
+ "non-imported: pkg sym",
+ []string{"nested", "Foo"},
+ []string{"Foo struct"},
+ nil,
+ },
+ {
+ "formatted doc on function",
+ []string{p, "ExportedFormattedDoc"},
+ []string{
+ `func ExportedFormattedDoc\(a int\) bool`,
+ ` Comment about exported function with formatting\.
+
+ Example
+
+ fmt\.Println\(FormattedDoc\(\)\)
+
+ Text after pre-formatted block\.`,
+ },
+ nil,
+ },
+ {
+ "formatted doc on type field",
+ []string{p, "ExportedFormattedType.ExportedField"},
+ []string{
+ `type ExportedFormattedType struct`,
+ ` // Comment before exported field with formatting\.
+ //[ ]
+ // Example
+ //[ ]
+ // a\.ExportedField = 123
+ //[ ]
+ // Text after pre-formatted block\.`,
+ `ExportedField int`,
+ },
+ nil,
+ },
+}
+
+func TestDoc(t *testing.T) {
+ maybeSkip(t)
+ defer log.SetOutput(log.Writer())
+ for _, test := range tests {
+ var b bytes.Buffer
+ var flagSet flag.FlagSet
+ var logbuf bytes.Buffer
+ log.SetOutput(&logbuf)
+ err := do(&b, &flagSet, test.args)
+ if err != nil {
+ t.Fatalf("%s %v: %s\n", test.name, test.args, err)
+ }
+ if logbuf.Len() > 0 {
+ t.Errorf("%s %v: unexpected log messages:\n%s", test.name, test.args, logbuf.Bytes())
+ }
+ output := b.Bytes()
+ failed := false
+ for j, yes := range test.yes {
+ re, err := regexp.Compile(yes)
+ if err != nil {
+ t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
+ }
+ if !re.Match(output) {
+ t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
+ failed = true
+ }
+ }
+ for j, no := range test.no {
+ re, err := regexp.Compile(no)
+ if err != nil {
+ t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
+ }
+ if re.Match(output) {
+ t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
+ failed = true
+ }
+ }
+ if bytes.Count(output, []byte("TYPES\n")) > 1 {
+ t.Fatalf("%s: repeating headers", test.name)
+ }
+ if failed {
+ t.Logf("\n%s", output)
+ }
+ }
+}
+
+// Test the code to try multiple packages. Our test case is
+//
+// go doc rand.Float64
+//
+// This needs to find math/rand.Float64; however crypto/rand, which doesn't
+// have the symbol, usually appears first in the directory listing.
+func TestMultiplePackages(t *testing.T) {
+ if testing.Short() {
+ t.Skip("scanning file system takes too long")
+ }
+ maybeSkip(t)
+ var b bytes.Buffer // We don't care about the output.
+ // Make sure crypto/rand does not have the symbol.
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"crypto/rand.float64"})
+ if err == nil {
+ t.Errorf("expected error from crypto/rand.float64")
+ } else if !strings.Contains(err.Error(), "no symbol float64") {
+ t.Errorf("unexpected error %q from crypto/rand.float64", err)
+ }
+ }
+ // Make sure math/rand does have the symbol.
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"math/rand.float64"})
+ if err != nil {
+ t.Errorf("unexpected error %q from math/rand.float64", err)
+ }
+ }
+ // Try the shorthand.
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"rand.float64"})
+ if err != nil {
+ t.Errorf("unexpected error %q from rand.float64", err)
+ }
+ }
+ // Now try a missing symbol. We should see both packages in the error.
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"rand.doesnotexit"})
+ if err == nil {
+ t.Errorf("expected error from rand.doesnotexit")
+ } else {
+ errStr := err.Error()
+ if !strings.Contains(errStr, "no symbol") {
+ t.Errorf("error %q should contain 'no symbol", errStr)
+ }
+ if !strings.Contains(errStr, "crypto/rand") {
+ t.Errorf("error %q should contain crypto/rand", errStr)
+ }
+ if !strings.Contains(errStr, "math/rand") {
+ t.Errorf("error %q should contain math/rand", errStr)
+ }
+ }
+ }
+}
+
+// Test the code to look up packages when given two args. First test case is
+//
+// go doc binary BigEndian
+//
+// This needs to find encoding/binary.BigEndian, which means
+// finding the package encoding/binary given only "binary".
+// Second case is
+//
+// go doc rand Float64
+//
+// which again needs to find math/rand and not give up after crypto/rand,
+// which has no such function.
+func TestTwoArgLookup(t *testing.T) {
+ if testing.Short() {
+ t.Skip("scanning file system takes too long")
+ }
+ maybeSkip(t)
+ var b bytes.Buffer // We don't care about the output.
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"binary", "BigEndian"})
+ if err != nil {
+ t.Errorf("unexpected error %q from binary BigEndian", err)
+ }
+ }
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"rand", "Float64"})
+ if err != nil {
+ t.Errorf("unexpected error %q from rand Float64", err)
+ }
+ }
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"bytes", "Foo"})
+ if err == nil {
+ t.Errorf("expected error from bytes Foo")
+ } else if !strings.Contains(err.Error(), "no symbol Foo") {
+ t.Errorf("unexpected error %q from bytes Foo", err)
+ }
+ }
+ {
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"})
+ if err == nil {
+ // actually present in the user's filesystem
+ } else if !strings.Contains(err.Error(), "no such package") {
+ t.Errorf("unexpected error %q from nosuchpackage Foo", err)
+ }
+ }
+}
+
+// Test the code to look up packages when the first argument starts with "./".
+// Our test case is in effect "cd src/text; doc ./template". This should get
+// text/template but before Issue 23383 was fixed would give html/template.
+func TestDotSlashLookup(t *testing.T) {
+ if testing.Short() {
+ t.Skip("scanning file system takes too long")
+ }
+ maybeSkip(t)
+ where, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if err := os.Chdir(where); err != nil {
+ t.Fatal(err)
+ }
+ }()
+ if err := os.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")); err != nil {
+ t.Fatal(err)
+ }
+ var b strings.Builder
+ var flagSet flag.FlagSet
+ err = do(&b, &flagSet, []string{"./template"})
+ if err != nil {
+ t.Errorf("unexpected error %q from ./template", err)
+ }
+ // The output should contain information about the text/template package.
+ const want = `package template // import "text/template"`
+ output := b.String()
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("wrong package: %.*q...", len(want), output)
+ }
+}
+
+// Test that we don't print spurious package clauses
+// when there should be no output at all. Issue 37969.
+func TestNoPackageClauseWhenNoMatch(t *testing.T) {
+ maybeSkip(t)
+ var b strings.Builder
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"template.ZZZ"})
+ // Expect an error.
+ if err == nil {
+ t.Error("expect an error for template.zzz")
+ }
+ // And the output should not contain any package clauses.
+ const dontWant = `package template // import `
+ output := b.String()
+ if strings.Contains(output, dontWant) {
+ t.Fatalf("improper package clause printed:\n%s", output)
+ }
+}
+
+type trimTest struct {
+ path string
+ prefix string
+ result string
+ ok bool
+}
+
+var trimTests = []trimTest{
+ {"", "", "", true},
+ {"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
+ {"/usr/gopher/bar", "/usr/gopher", "bar", true},
+ {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
+ {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
+}
+
+func TestTrim(t *testing.T) {
+ for _, test := range trimTests {
+ result, ok := trim(test.path, test.prefix)
+ if ok != test.ok {
+ t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
+ continue
+ }
+ if result != test.result {
+ t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
+ continue
+ }
+ }
+}