|
| 1 | +// Copyright 2021 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 | + "go/ast" |
| 10 | + "go/doc" |
| 11 | + "go/parser" |
| 12 | + "go/token" |
| 13 | + "os" |
| 14 | + "regexp" |
| 15 | + "strings" |
| 16 | + "testing" |
| 17 | +) |
| 18 | + |
| 19 | +// Test that the examples in the command documentation do what they |
| 20 | +// say. |
| 21 | +func TestDoc(t *testing.T) { |
| 22 | + // Read the package documentation. |
| 23 | + fset := token.NewFileSet() |
| 24 | + f, err := parser.ParseFile(fset, "main.go", nil, parser.ParseComments) |
| 25 | + if err != nil { |
| 26 | + t.Fatal(err) |
| 27 | + } |
| 28 | + p, err := doc.NewFromFiles(fset, []*ast.File{f}, "p") |
| 29 | + if err != nil { |
| 30 | + t.Fatal(err) |
| 31 | + } |
| 32 | + tests := parseDocTests(p.Doc) |
| 33 | + if len(tests) == 0 { |
| 34 | + t.Fatal("failed to parse doc tests: found 0 tests") |
| 35 | + } |
| 36 | + |
| 37 | + // Run the tests. |
| 38 | + if err := os.Chdir("testdata"); err != nil { |
| 39 | + t.Fatal(err) |
| 40 | + } |
| 41 | + defer os.Chdir("..") |
| 42 | + for _, test := range tests { |
| 43 | + var got, gotErr bytes.Buffer |
| 44 | + t.Logf("benchstat %s", strings.Join(test.args, " ")) |
| 45 | + if err := benchstat(&got, &gotErr, test.args); err != nil { |
| 46 | + t.Fatalf("unexpected error: %s", err) |
| 47 | + } |
| 48 | + |
| 49 | + // None of the doc tests should have error output. |
| 50 | + if gotErr.Len() != 0 { |
| 51 | + t.Errorf("unexpected stderr output:\n%s", gotErr.String()) |
| 52 | + continue |
| 53 | + } |
| 54 | + |
| 55 | + // Compare the output |
| 56 | + diff(t, []byte(test.want), got.Bytes()) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +type docTest struct { |
| 61 | + args []string |
| 62 | + want string |
| 63 | +} |
| 64 | + |
| 65 | +var docTestRe = regexp.MustCompile(`(?m)^[ \t]+\$ benchstat (.*)\n((?:\t.*\n|\n)+)`) |
| 66 | + |
| 67 | +func parseDocTests(doc string) []*docTest { |
| 68 | + var tests []*docTest |
| 69 | + for _, m := range docTestRe.FindAllStringSubmatch(doc, -1) { |
| 70 | + want := m[2] |
| 71 | + // Strip extra trailing newlines |
| 72 | + want = strings.TrimRight(want, "\n") + "\n" |
| 73 | + // Strip \t at the beginning of each line |
| 74 | + want = strings.Replace(want[1:], "\n\t", "\n", -1) |
| 75 | + tests = append(tests, &docTest{ |
| 76 | + args: parseArgs(m[1]), |
| 77 | + want: want, |
| 78 | + }) |
| 79 | + } |
| 80 | + return tests |
| 81 | +} |
| 82 | + |
| 83 | +// parseArgs is a very basic parser for shell-like word lists. |
| 84 | +func parseArgs(x string) []string { |
| 85 | + // TODO: Use strings.Cut |
| 86 | + var out []string |
| 87 | + x = strings.Trim(x, " ") |
| 88 | + for len(x) > 0 { |
| 89 | + if x[0] == '"' { |
| 90 | + x = x[1:] |
| 91 | + i := strings.Index(x, "\"") |
| 92 | + if i < 0 { |
| 93 | + panic("missing \"") |
| 94 | + } |
| 95 | + out = append(out, x[:i]) |
| 96 | + x = strings.TrimLeft(x[i+1:], " ") |
| 97 | + } else if i := strings.Index(x, " "); i < 0 { |
| 98 | + out = append(out, x) |
| 99 | + x = "" |
| 100 | + } else { |
| 101 | + out = append(out, x[:i]) |
| 102 | + x = strings.TrimLeft(x[i+1:], " ") |
| 103 | + } |
| 104 | + } |
| 105 | + return out |
| 106 | +} |
0 commit comments