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  }