golang.org/x/perf@v0.0.0-20240510023725-bedb9135df6d/cmd/benchstat/doc_test.go (about) 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 }