// Copyright 2023 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" "encoding/json" "flag" "fmt" "go/build/constraint" "math/rand" "os" "path/filepath" "strings" "testing" "golang.org/x/tools/internal/bisect" "golang.org/x/tools/internal/diffp" "golang.org/x/tools/txtar" ) var update = flag.Bool("update", false, "update testdata with new stdout/stderr") func Test(t *testing.T) { files, err := filepath.Glob("testdata/*.txt") if err != nil { t.Fatal(err) } for _, file := range files { t.Run(strings.TrimSuffix(filepath.Base(file), ".txt"), func(t *testing.T) { data, err := os.ReadFile(file) if err != nil { t.Fatal(err) } a := txtar.Parse(data) var wantStdout, wantStderr []byte files := a.Files if len(files) > 0 && files[0].Name == "stdout" { wantStdout = files[0].Data files = files[1:] } if len(files) > 0 && files[0].Name == "stderr" { wantStderr = files[0].Data files = files[1:] } if len(files) > 0 { t.Fatalf("unexpected txtar entry: %s", files[0].Name) } var tt struct { Fail string Bisect Bisect } if err := json.Unmarshal(a.Comment, &tt); err != nil { t.Fatal(err) } expr, err := constraint.Parse("//go:build " + tt.Fail) if err != nil { t.Fatalf("invalid Cmd: %v", err) } rnd := rand.New(rand.NewSource(1)) b := &tt.Bisect b.Cmd = "test" b.Args = []string{"PATTERN"} var stdout, stderr bytes.Buffer b.Stdout = &stdout b.Stderr = &stderr b.TestRun = func(env []string, cmd string, args []string) (out []byte, err error) { pattern := args[0] m, err := bisect.New(pattern) if err != nil { t.Fatal(err) } have := make(map[string]bool) for i, color := range colors { if m.ShouldEnable(uint64(i)) { have[color] = true } if m.ShouldReport(uint64(i)) { out = fmt.Appendf(out, "%s %s\n", color, bisect.Marker(uint64(i))) } } err = nil if eval(rnd, expr, have) { err = fmt.Errorf("failed") } return out, err } if !b.Search() { stderr.WriteString("\n") } rewrite := false if !bytes.Equal(stdout.Bytes(), wantStdout) { if *update { rewrite = true } else { t.Errorf("incorrect stdout: %s", diffp.Diff("have", stdout.Bytes(), "want", wantStdout)) } } if !bytes.Equal(stderr.Bytes(), wantStderr) { if *update { rewrite = true } else { t.Errorf("incorrect stderr: %s", diffp.Diff("have", stderr.Bytes(), "want", wantStderr)) } } if rewrite { a.Files = []txtar.File{{Name: "stdout", Data: stdout.Bytes()}, {Name: "stderr", Data: stderr.Bytes()}} err := os.WriteFile(file, txtar.Format(a), 0666) if err != nil { t.Fatal(err) } t.Logf("updated %s", file) } }) } } func eval(rnd *rand.Rand, z constraint.Expr, have map[string]bool) bool { switch z := z.(type) { default: panic(fmt.Sprintf("unexpected type %T", z)) case *constraint.NotExpr: return !eval(rnd, z.X, have) case *constraint.AndExpr: return eval(rnd, z.X, have) && eval(rnd, z.Y, have) case *constraint.OrExpr: return eval(rnd, z.X, have) || eval(rnd, z.Y, have) case *constraint.TagExpr: if z.Tag == "random" { return rnd.Intn(2) == 1 } return have[z.Tag] } } var colors = strings.Fields(` aliceblue amaranth amber amethyst applegreen applered apricot aquamarine azure babyblue beige brickred black blue bluegreen blueviolet blush bronze brown burgundy byzantium carmine cerise cerulean champagne chartreusegreen chocolate cobaltblue coffee copper coral crimson cyan desertsand electricblue emerald erin gold gray green harlequin indigo ivory jade junglegreen lavender lemon lilac lime magenta magentarose maroon mauve navyblue ochre olive orange orangered orchid peach pear periwinkle persianblue pink plum prussianblue puce purple raspberry red redviolet rose ruby salmon sangria sapphire scarlet silver slategray springbud springgreen tan taupe teal turquoise ultramarine violet viridian white yellow `)