]> Cypherpunks repositories - gostls13.git/commitdiff
go/doc: move example tests into files
authorJonathan Amsterdam <jba@google.com>
Fri, 22 Apr 2022 13:13:48 +0000 (09:13 -0400)
committerJonathan Amsterdam <jba@google.com>
Tue, 10 May 2022 16:17:51 +0000 (16:17 +0000)
Move the test cases for doc.Examples from example_test.go into
their own files under testdata/examples.

This makes example_test.go easier to read and collapses several
similar test functions into one.

It will also make it less cumbersome to add large examples later.

Change-Id: Id220c1205e94027d14291898e541b69344842686
Reviewed-on: https://go-review.googlesource.com/c/go/+/401756
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
18 files changed:
src/go/doc/example_test.go
src/go/doc/testdata/examples/README.md [new file with mode: 0644]
src/go/doc/testdata/examples/empty.go [new file with mode: 0644]
src/go/doc/testdata/examples/empty.golden [new file with mode: 0644]
src/go/doc/testdata/examples/import_groups.go [new file with mode: 0644]
src/go/doc/testdata/examples/import_groups.golden [new file with mode: 0644]
src/go/doc/testdata/examples/import_groups_named.go [new file with mode: 0644]
src/go/doc/testdata/examples/import_groups_named.golden [new file with mode: 0644]
src/go/doc/testdata/examples/inspect_signature.go [new file with mode: 0644]
src/go/doc/testdata/examples/inspect_signature.golden [new file with mode: 0644]
src/go/doc/testdata/examples/multiple.go [new file with mode: 0644]
src/go/doc/testdata/examples/multiple.golden [new file with mode: 0644]
src/go/doc/testdata/examples/whole_file.go [new file with mode: 0644]
src/go/doc/testdata/examples/whole_file.golden [new file with mode: 0644]
src/go/doc/testdata/examples/whole_function.go [new file with mode: 0644]
src/go/doc/testdata/examples/whole_function.golden [new file with mode: 0644]
src/go/doc/testdata/examples/whole_function_external.go [new file with mode: 0644]
src/go/doc/testdata/examples/whole_function_external.golden [new file with mode: 0644]

index 4d87a654c9a9b899d3d7d6bc18f5ec966686f8d8..7919c3a2c03eddc07d3c17cb93a6bd60543b9744 100644 (file)
@@ -12,512 +12,79 @@ import (
        "go/format"
        "go/parser"
        "go/token"
+       "internal/diff"
+       "internal/txtar"
+       "path/filepath"
        "reflect"
        "strings"
        "testing"
 )
 
-const exampleTestFile = `
-package foo_test
-
-import (
-       "flag"
-       "fmt"
-       "log"
-       "sort"
-       "os/exec"
-)
-
-func ExampleHello() {
-       fmt.Println("Hello, world!")
-       // Output: Hello, world!
-}
-
-func ExampleImport() {
-       out, err := exec.Command("date").Output()
-       if err != nil {
-               log.Fatal(err)
-       }
-       fmt.Printf("The date is %s\n", out)
-}
-
-func ExampleKeyValue() {
-       v := struct {
-               a string
-               b int
-       }{
-               a: "A",
-               b: 1,
-       }
-       fmt.Print(v)
-       // Output: a: "A", b: 1
-}
-
-func ExampleKeyValueImport() {
-       f := flag.Flag{
-               Name: "play",
-       }
-       fmt.Print(f)
-       // Output: Name: "play"
-}
-
-var keyValueTopDecl = struct {
-       a string
-       b int
-}{
-       a: "B",
-       b: 2,
-}
-
-func ExampleKeyValueTopDecl() {
-       fmt.Print(keyValueTopDecl)
-       // Output: a: "B", b: 2
-}
-
-// Person represents a person by name and age.
-type Person struct {
-    Name string
-    Age  int
-}
-
-// String returns a string representation of the Person.
-func (p Person) String() string {
-    return fmt.Sprintf("%s: %d", p.Name, p.Age)
-}
-
-// ByAge implements sort.Interface for []Person based on
-// the Age field.
-type ByAge []Person
-
-// Len returns the number of elements in ByAge.
-func (a (ByAge)) Len() int { return len(a) }
-
-// Swap swaps the elements in ByAge.
-func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
-
-// people is the array of Person
-var people = []Person{
-       {"Bob", 31},
-       {"John", 42},
-       {"Michael", 17},
-       {"Jenny", 26},
-}
-
-func ExampleSort() {
-    fmt.Println(people)
-    sort.Sort(ByAge(people))
-    fmt.Println(people)
-    // Output:
-    // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
-    // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
-}
-`
-
-var exampleTestCases = []struct {
-       Name, Play, Output string
-}{
-       {
-               Name:   "Hello",
-               Play:   exampleHelloPlay,
-               Output: "Hello, world!\n",
-       },
-       {
-               Name: "Import",
-               Play: exampleImportPlay,
-       },
-       {
-               Name:   "KeyValue",
-               Play:   exampleKeyValuePlay,
-               Output: "a: \"A\", b: 1\n",
-       },
-       {
-               Name:   "KeyValueImport",
-               Play:   exampleKeyValueImportPlay,
-               Output: "Name: \"play\"\n",
-       },
-       {
-               Name:   "KeyValueTopDecl",
-               Play:   exampleKeyValueTopDeclPlay,
-               Output: "a: \"B\", b: 2\n",
-       },
-       {
-               Name:   "Sort",
-               Play:   exampleSortPlay,
-               Output: "[Bob: 31 John: 42 Michael: 17 Jenny: 26]\n[Michael: 17 Jenny: 26 Bob: 31 John: 42]\n",
-       },
-}
-
-const exampleHelloPlay = `package main
-
-import (
-       "fmt"
-)
-
-func main() {
-       fmt.Println("Hello, world!")
-}
-`
-const exampleImportPlay = `package main
-
-import (
-       "fmt"
-       "log"
-       "os/exec"
-)
-
-func main() {
-       out, err := exec.Command("date").Output()
-       if err != nil {
-               log.Fatal(err)
-       }
-       fmt.Printf("The date is %s\n", out)
-}
-`
-
-const exampleKeyValuePlay = `package main
-
-import (
-       "fmt"
-)
-
-func main() {
-       v := struct {
-               a string
-               b int
-       }{
-               a: "A",
-               b: 1,
-       }
-       fmt.Print(v)
-}
-`
-
-const exampleKeyValueImportPlay = `package main
-
-import (
-       "flag"
-       "fmt"
-)
-
-func main() {
-       f := flag.Flag{
-               Name: "play",
-       }
-       fmt.Print(f)
-}
-`
-
-const exampleKeyValueTopDeclPlay = `package main
-
-import (
-       "fmt"
-)
-
-var keyValueTopDecl = struct {
-       a string
-       b int
-}{
-       a: "B",
-       b: 2,
-}
-
-func main() {
-       fmt.Print(keyValueTopDecl)
-}
-`
-
-const exampleSortPlay = `package main
-
-import (
-       "fmt"
-       "sort"
-)
-
-// Person represents a person by name and age.
-type Person struct {
-       Name string
-       Age  int
-}
-
-// String returns a string representation of the Person.
-func (p Person) String() string {
-       return fmt.Sprintf("%s: %d", p.Name, p.Age)
-}
-
-// ByAge implements sort.Interface for []Person based on
-// the Age field.
-type ByAge []Person
-
-// Len returns the number of elements in ByAge.
-func (a ByAge) Len() int { return len(a) }
-
-// Swap swaps the elements in ByAge.
-func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
-
-// people is the array of Person
-var people = []Person{
-       {"Bob", 31},
-       {"John", 42},
-       {"Michael", 17},
-       {"Jenny", 26},
-}
-
-func main() {
-       fmt.Println(people)
-       sort.Sort(ByAge(people))
-       fmt.Println(people)
-}
-`
-
 func TestExamples(t *testing.T) {
-       fset := token.NewFileSet()
-       file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
+       dir := filepath.Join("testdata", "examples")
+       filenames, err := filepath.Glob(filepath.Join(dir, "*.go"))
        if err != nil {
                t.Fatal(err)
        }
-       for i, e := range doc.Examples(file) {
-               c := exampleTestCases[i]
-               if e.Name != c.Name {
-                       t.Errorf("got Name == %q, want %q", e.Name, c.Name)
-               }
-               if w := c.Play; w != "" {
-                       g := formatFile(t, fset, e.Play)
-                       if g != w {
-                               t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
+       for _, filename := range filenames {
+               t.Run(strings.TrimSuffix(filepath.Base(filename), ".go"), func(t *testing.T) {
+                       fset := token.NewFileSet()
+                       astFile, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       goldenFilename := strings.TrimSuffix(filename, ".go") + ".golden"
+                       archive, err := txtar.ParseFile(goldenFilename)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       golden := map[string]string{}
+                       for _, f := range archive.Files {
+                               golden[f.Name] = strings.TrimSpace(string(f.Data))
                        }
-               }
-               if g, w := e.Output, c.Output; g != w {
-                       t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
-               }
-       }
-}
-
-const exampleWholeFile = `package foo_test
-
-type X int
-
-func (X) Foo() {
-}
-
-func (X) TestBlah() {
-}
-
-func (X) BenchmarkFoo() {
-}
-
-func (X) FuzzFoo() {
-}
-
-func Example() {
-       fmt.Println("Hello, world!")
-       // Output: Hello, world!
-}
-`
-
-const exampleWholeFileOutput = `package main
-
-type X int
-
-func (X) Foo() {
-}
-
-func (X) TestBlah() {
-}
-
-func (X) BenchmarkFoo() {
-}
-
-func (X) FuzzFoo() {
-}
-
-func main() {
-       fmt.Println("Hello, world!")
-}
-`
-
-const exampleWholeFileFunction = `package foo_test
-
-func Foo(x int) {
-}
-
-func Example() {
-       fmt.Println("Hello, world!")
-       // Output: Hello, world!
-}
-`
-
-const exampleWholeFileFunctionOutput = `package main
-
-func Foo(x int) {
-}
-
-func main() {
-       fmt.Println("Hello, world!")
-}
-`
-
-const exampleWholeFileExternalFunction = `package foo_test
-
-func foo(int)
-
-func Example() {
-       foo(42)
-       // Output:
-}
-`
-
-const exampleWholeFileExternalFunctionOutput = `package main
-
-func foo(int)
-
-func main() {
-       foo(42)
-}
-`
-
-var exampleWholeFileTestCases = []struct {
-       Title, Source, Play, Output string
-}{
-       {
-               "Methods",
-               exampleWholeFile,
-               exampleWholeFileOutput,
-               "Hello, world!\n",
-       },
-       {
-               "Function",
-               exampleWholeFileFunction,
-               exampleWholeFileFunctionOutput,
-               "Hello, world!\n",
-       },
-       {
-               "ExternalFunction",
-               exampleWholeFileExternalFunction,
-               exampleWholeFileExternalFunctionOutput,
-               "",
-       },
-}
-
-func TestExamplesWholeFile(t *testing.T) {
-       for _, c := range exampleWholeFileTestCases {
-               fset := token.NewFileSet()
-               file, err := parser.ParseFile(fset, "test.go", strings.NewReader(c.Source), parser.ParseComments)
-               if err != nil {
-                       t.Fatal(err)
-               }
-               es := doc.Examples(file)
-               if len(es) != 1 {
-                       t.Fatalf("%s: wrong number of examples; got %d want 1", c.Title, len(es))
-               }
-               e := es[0]
-               if e.Name != "" {
-                       t.Errorf("%s: got Name == %q, want %q", c.Title, e.Name, "")
-               }
-               if g, w := formatFile(t, fset, e.Play), c.Play; g != w {
-                       t.Errorf("%s: got Play == %q, want %q", c.Title, g, w)
-               }
-               if g, w := e.Output, c.Output; g != w {
-                       t.Errorf("%s: got Output == %q, want %q", c.Title, g, w)
-               }
-       }
-}
-
-const exampleInspectSignature = `package foo_test
-
-import (
-       "bytes"
-       "io"
-)
-
-func getReader() io.Reader { return nil }
-
-func do(b bytes.Reader) {}
-
-func Example() {
-       getReader()
-       do()
-       // Output:
-}
-
-func ExampleIgnored() {
-}
-`
-
-const exampleInspectSignatureOutput = `package main
-
-import (
-       "bytes"
-       "io"
-)
-
-func getReader() io.Reader { return nil }
-
-func do(b bytes.Reader) {}
-
-func main() {
-       getReader()
-       do()
-}
-`
-
-func TestExampleInspectSignature(t *testing.T) {
-       // Verify that "bytes" and "io" are imported. See issue #28492.
-       fset := token.NewFileSet()
-       file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleInspectSignature), parser.ParseComments)
-       if err != nil {
-               t.Fatal(err)
-       }
-       es := doc.Examples(file)
-       if len(es) != 2 {
-               t.Fatalf("wrong number of examples; got %d want 2", len(es))
-       }
-       // We are interested in the first example only.
-       e := es[0]
-       if e.Name != "" {
-               t.Errorf("got Name == %q, want %q", e.Name, "")
-       }
-       if g, w := formatFile(t, fset, e.Play), exampleInspectSignatureOutput; g != w {
-               t.Errorf("got Play == %q, want %q", g, w)
-       }
-       if g, w := e.Output, ""; g != w {
-               t.Errorf("got Output == %q, want %q", g, w)
-       }
-}
-
-const exampleEmpty = `
-package p
-func Example() {}
-func Example_a()
-`
-
-const exampleEmptyOutput = `package main
-
-func main() {}
-func main()
-`
 
-func TestExampleEmpty(t *testing.T) {
-       fset := token.NewFileSet()
-       file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleEmpty), parser.ParseComments)
-       if err != nil {
-               t.Fatal(err)
-       }
+                       // Collect the results of doc.Examples in a map keyed by example name.
+                       examples := map[string]*doc.Example{}
+                       for _, e := range doc.Examples(astFile) {
+                               examples[e.Name] = e
+                               // Treat missing sections in the golden as empty.
+                               for _, kind := range []string{"Play", "Output"} {
+                                       key := e.Name + "." + kind
+                                       if _, ok := golden[key]; !ok {
+                                               golden[key] = ""
+                                       }
+                               }
+                       }
 
-       es := doc.Examples(file)
-       if len(es) != 1 {
-               t.Fatalf("wrong number of examples; got %d want 1", len(es))
-       }
-       e := es[0]
-       if e.Name != "" {
-               t.Errorf("got Name == %q, want %q", e.Name, "")
-       }
-       if g, w := formatFile(t, fset, e.Play), exampleEmptyOutput; g != w {
-               t.Errorf("got Play == %q, want %q", g, w)
-       }
-       if g, w := e.Output, ""; g != w {
-               t.Errorf("got Output == %q, want %q", g, w)
+                       // Each section in the golden file corresponds to an example we expect
+                       // to see.
+                       for sectionName, want := range golden {
+                               name, kind, found := strings.Cut(sectionName, ".")
+                               if !found {
+                                       t.Fatalf("bad section name %q, want EXAMPLE_NAME.KIND", sectionName)
+                               }
+                               ex := examples[name]
+                               if ex == nil {
+                                       t.Fatalf("no example named %q", name)
+                               }
+
+                               var got string
+                               switch kind {
+                               case "Play":
+                                       got = strings.TrimSpace(formatFile(t, fset, ex.Play))
+
+                               case "Output":
+                                       got = strings.TrimSpace(ex.Output)
+                               default:
+                                       t.Fatalf("bad section kind %q", kind)
+                               }
+
+                               if got != want {
+                                       t.Errorf("%s mismatch:\n%s", sectionName,
+                                               diff.Diff("want", []byte(want), "got", []byte(got)))
+                               }
+                       }
+               })
        }
 }
 
diff --git a/src/go/doc/testdata/examples/README.md b/src/go/doc/testdata/examples/README.md
new file mode 100644 (file)
index 0000000..a1c18e8
--- /dev/null
@@ -0,0 +1,12 @@
+These files are processed by example_test.go:TestExamples.
+
+A .golden file is a txtar file with two sections for each example that should be
+created by doc.Examples from the corresponding .go file.
+
+One section, named EXAMPLE_NAME.Output, contains the example's output,
+the value of the field Example.Output.
+
+The other, named EXAMPLE_NAME.Play, contains the formatted code for a playable
+version of the example, the value of the field Example.Play.
+
+If a section is missing, it is treated as being empty.
diff --git a/src/go/doc/testdata/examples/empty.go b/src/go/doc/testdata/examples/empty.go
new file mode 100644 (file)
index 0000000..0b10420
--- /dev/null
@@ -0,0 +1,8 @@
+// Copyright 2021 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 p
+
+func Example() {}
+func Example_a()
diff --git a/src/go/doc/testdata/examples/empty.golden b/src/go/doc/testdata/examples/empty.golden
new file mode 100644 (file)
index 0000000..2aafd20
--- /dev/null
@@ -0,0 +1,6 @@
+-- .Play --
+package main
+
+func main() {}
+func main()
+
diff --git a/src/go/doc/testdata/examples/import_groups.go b/src/go/doc/testdata/examples/import_groups.go
new file mode 100644 (file)
index 0000000..05f21ca
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2022 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 foo_test
+
+import (
+       "fmt"
+       "time"
+
+       "golang.org/x/time/rate"
+)
+
+func Example() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
+
+func ExampleLimiter() {
+       // Uses fmt, time and rate.
+       l := rate.NewLimiter(rate.Every(time.Second), 1)
+       fmt.Println(l)
+}
diff --git a/src/go/doc/testdata/examples/import_groups.golden b/src/go/doc/testdata/examples/import_groups.golden
new file mode 100644 (file)
index 0000000..efe2cc1
--- /dev/null
@@ -0,0 +1,27 @@
+-- .Play --
+package main
+
+import (
+       "fmt"
+)
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+-- .Output --
+Hello, world!
+-- Limiter.Play --
+package main
+
+import (
+       "fmt"
+       "time"
+
+       "golang.org/x/time/rate"
+)
+
+func main() {
+       // Uses fmt, time and rate.
+       l := rate.NewLimiter(rate.Every(time.Second), 1)
+       fmt.Println(l)
+}
diff --git a/src/go/doc/testdata/examples/import_groups_named.go b/src/go/doc/testdata/examples/import_groups_named.go
new file mode 100644 (file)
index 0000000..377022b
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2022 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 foo_test
+
+import (
+       "fmt"
+       tm "time"
+
+       r "golang.org/x/time/rate"
+)
+
+func Example() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
+
+func ExampleLimiter() {
+       // Uses fmt, time and rate.
+       l := r.NewLimiter(r.Every(tm.Second), 1)
+       fmt.Println(l)
+}
diff --git a/src/go/doc/testdata/examples/import_groups_named.golden b/src/go/doc/testdata/examples/import_groups_named.golden
new file mode 100644 (file)
index 0000000..9baf373
--- /dev/null
@@ -0,0 +1,27 @@
+-- .Play --
+package main
+
+import (
+       "fmt"
+)
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+-- .Output --
+Hello, world!
+-- Limiter.Play --
+package main
+
+import (
+       "fmt"
+       tm "time"
+
+       r "golang.org/x/time/rate"
+)
+
+func main() {
+       // Uses fmt, time and rate.
+       l := r.NewLimiter(r.Every(tm.Second), 1)
+       fmt.Println(l)
+}
diff --git a/src/go/doc/testdata/examples/inspect_signature.go b/src/go/doc/testdata/examples/inspect_signature.go
new file mode 100644 (file)
index 0000000..c4a36e7
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2021 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 foo_test
+
+import (
+       "bytes"
+       "io"
+)
+
+func getReader() io.Reader { return nil }
+
+func do(b bytes.Reader) {}
+
+func Example() {
+       getReader()
+       do()
+       // Output:
+}
+
+func ExampleIgnored() {
+}
diff --git a/src/go/doc/testdata/examples/inspect_signature.golden b/src/go/doc/testdata/examples/inspect_signature.golden
new file mode 100644 (file)
index 0000000..c0d9b2e
--- /dev/null
@@ -0,0 +1,24 @@
+-- .Play --
+package main
+
+import (
+       "bytes"
+       "io"
+)
+
+func getReader() io.Reader { return nil }
+
+func do(b bytes.Reader) {}
+
+func main() {
+       getReader()
+       do()
+}
+-- Ignored.Play --
+package main
+
+import ()
+
+func main() {
+}
+
diff --git a/src/go/doc/testdata/examples/multiple.go b/src/go/doc/testdata/examples/multiple.go
new file mode 100644 (file)
index 0000000..2728264
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright 2021 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 foo_test
+
+import (
+       "flag"
+       "fmt"
+       "log"
+       "os/exec"
+       "sort"
+)
+
+func ExampleHello() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
+
+func ExampleImport() {
+       out, err := exec.Command("date").Output()
+       if err != nil {
+               log.Fatal(err)
+       }
+       fmt.Printf("The date is %s\n", out)
+}
+
+func ExampleKeyValue() {
+       v := struct {
+               a string
+               b int
+       }{
+               a: "A",
+               b: 1,
+       }
+       fmt.Print(v)
+       // Output: a: "A", b: 1
+}
+
+func ExampleKeyValueImport() {
+       f := flag.Flag{
+               Name: "play",
+       }
+       fmt.Print(f)
+       // Output: Name: "play"
+}
+
+var keyValueTopDecl = struct {
+       a string
+       b int
+}{
+       a: "B",
+       b: 2,
+}
+
+func ExampleKeyValueTopDecl() {
+       fmt.Print(keyValueTopDecl)
+       // Output: a: "B", b: 2
+}
+
+// Person represents a person by name and age.
+type Person struct {
+       Name string
+       Age  int
+}
+
+// String returns a string representation of the Person.
+func (p Person) String() string {
+       return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
+
+// ByAge implements sort.Interface for []Person based on
+// the Age field.
+type ByAge []Person
+
+// Len returns the number of elements in ByAge.
+func (a ByAge) Len() int { return len(a) }
+
+// Swap swaps the elements in ByAge.
+func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
+
+// people is the array of Person
+var people = []Person{
+       {"Bob", 31},
+       {"John", 42},
+       {"Michael", 17},
+       {"Jenny", 26},
+}
+
+func ExampleSort() {
+       fmt.Println(people)
+       sort.Sort(ByAge(people))
+       fmt.Println(people)
+       // Output:
+       // [Bob: 31 John: 42 Michael: 17 Jenny: 26]
+       // [Michael: 17 Jenny: 26 Bob: 31 John: 42]
+}
diff --git a/src/go/doc/testdata/examples/multiple.golden b/src/go/doc/testdata/examples/multiple.golden
new file mode 100644 (file)
index 0000000..d2d791e
--- /dev/null
@@ -0,0 +1,129 @@
+-- Hello.Play --
+package main
+
+import (
+       "fmt"
+)
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+-- Hello.Output --
+Hello, world!
+-- Import.Play --
+package main
+
+import (
+       "fmt"
+       "log"
+       "os/exec"
+)
+
+func main() {
+       out, err := exec.Command("date").Output()
+       if err != nil {
+               log.Fatal(err)
+       }
+       fmt.Printf("The date is %s\n", out)
+}
+-- KeyValue.Play --
+package main
+
+import (
+       "fmt"
+)
+
+func main() {
+       v := struct {
+               a string
+               b int
+       }{
+               a: "A",
+               b: 1,
+       }
+       fmt.Print(v)
+}
+-- KeyValue.Output --
+a: "A", b: 1
+-- KeyValueImport.Play --
+package main
+
+import (
+       "flag"
+       "fmt"
+)
+
+func main() {
+       f := flag.Flag{
+               Name: "play",
+       }
+       fmt.Print(f)
+}
+-- KeyValueImport.Output --
+Name: "play"
+-- KeyValueTopDecl.Play --
+package main
+
+import (
+       "fmt"
+)
+
+var keyValueTopDecl = struct {
+       a string
+       b int
+}{
+       a: "B",
+       b: 2,
+}
+
+func main() {
+       fmt.Print(keyValueTopDecl)
+}
+-- KeyValueTopDecl.Output --
+a: "B", b: 2
+-- Sort.Play --
+package main
+
+import (
+       "fmt"
+       "sort"
+)
+
+// Person represents a person by name and age.
+type Person struct {
+       Name string
+       Age  int
+}
+
+// String returns a string representation of the Person.
+func (p Person) String() string {
+       return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
+
+// ByAge implements sort.Interface for []Person based on
+// the Age field.
+type ByAge []Person
+
+// Len returns the number of elements in ByAge.
+func (a ByAge) Len() int { return len(a) }
+
+// Swap swaps the elements in ByAge.
+func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
+
+// people is the array of Person
+var people = []Person{
+       {"Bob", 31},
+       {"John", 42},
+       {"Michael", 17},
+       {"Jenny", 26},
+}
+
+func main() {
+       fmt.Println(people)
+       sort.Sort(ByAge(people))
+       fmt.Println(people)
+}
+-- Sort.Output --
+[Bob: 31 John: 42 Michael: 17 Jenny: 26]
+[Michael: 17 Jenny: 26 Bob: 31 John: 42]
diff --git a/src/go/doc/testdata/examples/whole_file.go b/src/go/doc/testdata/examples/whole_file.go
new file mode 100644 (file)
index 0000000..61954ce
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2021 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 foo_test
+
+import "fmt"
+
+type X int
+
+func (X) Foo() {
+}
+
+func (X) TestBlah() {
+}
+
+func (X) BenchmarkFoo() {
+}
+
+func Example() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
diff --git a/src/go/doc/testdata/examples/whole_file.golden b/src/go/doc/testdata/examples/whole_file.golden
new file mode 100644 (file)
index 0000000..74a2291
--- /dev/null
@@ -0,0 +1,21 @@
+-- .Play --
+package main
+
+import "fmt"
+
+type X int
+
+func (X) Foo() {
+}
+
+func (X) TestBlah() {
+}
+
+func (X) BenchmarkFoo() {
+}
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+-- .Output --
+Hello, world!
diff --git a/src/go/doc/testdata/examples/whole_function.go b/src/go/doc/testdata/examples/whole_function.go
new file mode 100644 (file)
index 0000000..1754ee3
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2021 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 foo_test
+
+func Foo(x int) {
+}
+
+func Example() {
+       fmt.Println("Hello, world!")
+       // Output: Hello, world!
+}
diff --git a/src/go/doc/testdata/examples/whole_function.golden b/src/go/doc/testdata/examples/whole_function.golden
new file mode 100644 (file)
index 0000000..7d5b5cb
--- /dev/null
@@ -0,0 +1,11 @@
+-- .Play --
+package main
+
+func Foo(x int) {
+}
+
+func main() {
+       fmt.Println("Hello, world!")
+}
+-- .Output --
+Hello, world!
diff --git a/src/go/doc/testdata/examples/whole_function_external.go b/src/go/doc/testdata/examples/whole_function_external.go
new file mode 100644 (file)
index 0000000..0e0e2f5
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2021 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 foo_test
+
+func foo(int)
+
+func Example() {
+       foo(42)
+       // Output:
+}
diff --git a/src/go/doc/testdata/examples/whole_function_external.golden b/src/go/doc/testdata/examples/whole_function_external.golden
new file mode 100644 (file)
index 0000000..ec8f114
--- /dev/null
@@ -0,0 +1,8 @@
+-- .Play --
+package main
+
+func foo(int)
+
+func main() {
+       foo(42)
+}