blob: bff1bf23c0c6c4f4e6287d9ec8d09aad59d93e34 [file] [log] [blame]
// 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("<bisect failed>\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
`)