| // 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 main | |
| import ( | |
| "bytes" | |
| "go/ast" | |
| "go/doc" | |
| "go/parser" | |
| "go/token" | |
| "os" | |
| "regexp" | |
| "strings" | |
| "testing" | |
| ) | |
| // Test that the examples in the command documentation do what they | |
| // say. | |
| func TestDoc(t *testing.T) { | |
| // Read the package documentation. | |
| fset := token.NewFileSet() | |
| f, err := parser.ParseFile(fset, "main.go", nil, parser.ParseComments) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| p, err := doc.NewFromFiles(fset, []*ast.File{f}, "p") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| tests := parseDocTests(p.Doc) | |
| if len(tests) == 0 { | |
| t.Fatal("failed to parse doc tests: found 0 tests") | |
| } | |
| // Run the tests. | |
| if err := os.Chdir("testdata"); err != nil { | |
| t.Fatal(err) | |
| } | |
| defer os.Chdir("..") | |
| for _, test := range tests { | |
| var got, gotErr bytes.Buffer | |
| t.Logf("benchstat %s", strings.Join(test.args, " ")) | |
| if err := benchstat(&got, &gotErr, test.args); err != nil { | |
| t.Fatalf("unexpected error: %s", err) | |
| } | |
| // None of the doc tests should have error output. | |
| if gotErr.Len() != 0 { | |
| t.Errorf("unexpected stderr output:\n%s", gotErr.String()) | |
| continue | |
| } | |
| // Compare the output | |
| diff(t, []byte(test.want), got.Bytes()) | |
| } | |
| } | |
| type docTest struct { | |
| args []string | |
| want string | |
| } | |
| var docTestRe = regexp.MustCompile(`(?m)^[ \t]+\$ benchstat (.*)\n((?:\t.*\n|\n)+)`) | |
| func parseDocTests(doc string) []*docTest { | |
| var tests []*docTest | |
| for _, m := range docTestRe.FindAllStringSubmatch(doc, -1) { | |
| want := m[2] | |
| // Strip extra trailing newlines | |
| want = strings.TrimRight(want, "\n") + "\n" | |
| // Strip \t at the beginning of each line | |
| want = strings.Replace(want[1:], "\n\t", "\n", -1) | |
| tests = append(tests, &docTest{ | |
| args: parseArgs(m[1]), | |
| want: want, | |
| }) | |
| } | |
| return tests | |
| } | |
| // parseArgs is a very basic parser for shell-like word lists. | |
| func parseArgs(x string) []string { | |
| // TODO: Use strings.Cut | |
| var out []string | |
| x = strings.Trim(x, " ") | |
| for len(x) > 0 { | |
| if x[0] == '"' { | |
| x = x[1:] | |
| i := strings.Index(x, "\"") | |
| if i < 0 { | |
| panic("missing \"") | |
| } | |
| out = append(out, x[:i]) | |
| x = strings.TrimLeft(x[i+1:], " ") | |
| } else if i := strings.Index(x, " "); i < 0 { | |
| out = append(out, x) | |
| x = "" | |
| } else { | |
| out = append(out, x[:i]) | |
| x = strings.TrimLeft(x[i+1:], " ") | |
| } | |
| } | |
| return out | |
| } |