whatcanGOwrong

This commit is contained in:
2024-09-19 21:38:24 -04:00
commit d0ae4d841d
17908 changed files with 4096831 additions and 0 deletions
@@ -0,0 +1,13 @@
// 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.
//go:build !go1.20
package main
import "os/exec"
func cmdInterrupt(cmd *exec.Cmd) {
// cmd.Cancel and cmd.WaitDelay not available before Go 1.20.
}
@@ -0,0 +1,26 @@
// 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.
//go:build go1.20
package main
import (
"os"
"os/exec"
"time"
)
func cmdInterrupt(cmd *exec.Cmd) {
cmd.Cancel = func() error {
// On timeout, send interrupt,
// in hopes of shutting down process tree.
// Ignore errors sending signal; it's all best effort
// and not even implemented on Windows.
// TODO(rsc): Maybe use a new process group and kill the whole group?
cmd.Process.Signal(os.Interrupt)
return nil
}
cmd.WaitDelay = 2 * time.Second
}
@@ -0,0 +1,733 @@
// 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.
// Bisect finds changes responsible for causing a failure.
// A typical use is to identify the source locations in a program
// that are miscompiled by a given compiler optimization.
//
// Usage:
//
// bisect [flags] [var=value...] command [arguments...]
//
// Bisect operates on a target command line the target that can be
// run with various changes individually enabled or disabled. With none
// of the changes enabled, the target is known to succeed (exit with exit
// code zero). With all the changes enabled, the target is known to fail
// (exit any other way). Bisect repeats the target with different sets of
// changes enabled, using binary search to find (non-overlapping) minimal
// change sets that provoke the failure.
//
// The target must cooperate with bisect by accepting a change pattern
// and then enabling and reporting the changes that match that pattern.
// The change pattern is passed to the target by substituting it anywhere
// the string PATTERN appears in the environment values or the command
// arguments. For each change that matches the pattern, the target must
// enable that change and also print one or more “match lines”
// (to standard output or standard error) describing the change.
// The [golang.org/x/tools/internal/bisect] package provides functions to help
// targets implement this protocol. We plan to publish that package
// in a non-internal location after finalizing its API.
//
// Bisect starts by running the target with no changes enabled and then
// with all changes enabled. It expects the former to succeed and the latter to fail,
// and then it will search for the minimal set of changes that must be enabled
// to provoke the failure. If the situation is reversed the target fails with no
// changes enabled and succeeds with all changes enabled then bisect
// automatically runs in reverse as well, searching for the minimal set of changes
// that must be disabled to provoke the failure.
//
// Bisect prints tracing logs to standard error and the minimal change sets
// to standard output.
//
// # Command Line Flags
//
// Bisect supports the following command-line flags:
//
// -max=M
//
// Stop after finding M minimal change sets. The default is no maximum, meaning to run until
// all changes that provoke a failure have been identified.
//
// -maxset=S
//
// Disallow change sets larger than S elements. The default is no maximum.
//
// -timeout=D
//
// If the target runs for longer than duration D, stop the target and interpret that as a failure.
// The default is no timeout.
//
// -count=N
//
// Run each trial N times (default 2), checking for consistency.
//
// -v
//
// Print verbose output, showing each run and its match lines.
//
// In addition to these general flags,
// bisect supports a few “shortcut” flags that make it more convenient
// to use with specific targets.
//
// -compile=<rewrite>
//
// This flag is equivalent to adding an environment variable
// “GOCOMPILEDEBUG=<rewrite>hash=PATTERN”,
// which, as discussed in more detail in the example below,
// allows bisect to identify the specific source locations where the
// compiler rewrite causes the target to fail.
//
// -godebug=<name>=<value>
//
// This flag is equivalent to adding an environment variable
// “GODEBUG=<name>=<value>#PATTERN”,
// which allows bisect to identify the specific call stacks where
// the changed [GODEBUG setting] value causes the target to fail.
//
// # Example
//
// The Go compiler provides support for enabling or disabling certain rewrites
// and optimizations to allow bisect to identify specific source locations where
// the rewrite causes the program to fail. For example, to bisect a failure caused
// by the new loop variable semantics:
//
// bisect go test -gcflags=all=-d=loopvarhash=PATTERN
//
// The -gcflags=all= instructs the go command to pass the -d=... to the Go compiler
// when compiling all packages. Bisect varies PATTERN to determine the minimal set of changes
// needed to reproduce the failure.
//
// The go command also checks the GOCOMPILEDEBUG environment variable for flags
// to pass to the compiler, so the above command is equivalent to:
//
// bisect GOCOMPILEDEBUG=loopvarhash=PATTERN go test
//
// Finally, as mentioned earlier, the -compile flag allows shortening this command further:
//
// bisect -compile=loopvar go test
//
// # Defeating Build Caches
//
// Build systems cache build results, to avoid repeating the same compilations
// over and over. When using a cached build result, the go command (correctly)
// reprints the cached standard output and standard error associated with that
// command invocation. (This makes commands like 'go build -gcflags=-S' for
// printing an assembly listing work reliably.)
//
// Unfortunately, most build systems, including Bazel, are not as careful
// as the go command about reprinting compiler output. If the compiler is
// what prints match lines, a build system that suppresses compiler
// output when using cached compiler results will confuse bisect.
// To defeat such build caches, bisect replaces the literal text “RANDOM”
// in environment values and command arguments with a random 64-bit value
// during each invocation. The Go compiler conveniently accepts a
// -d=ignore=... debug flag that ignores its argument, so to run the
// previous example using Bazel, the invocation is:
//
// bazel test --define=gc_goopts=-d=loopvarhash=PATTERN,unused=RANDOM //path/to:test
//
// [GODEBUG setting]: https://tip.golang.org/doc/godebug
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"math/bits"
"math/rand"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"time"
"golang.org/x/tools/internal/bisect"
)
// Preserve import of bisect, to allow [bisect.Match] in the doc comment.
var _ bisect.Matcher
func usage() {
fmt.Fprintf(os.Stderr, "usage: bisect [flags] [var=value...] command [arguments...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("bisect: ")
var b Bisect
b.Stdout = os.Stdout
b.Stderr = os.Stderr
flag.IntVar(&b.Max, "max", 0, "stop after finding `m` failing change sets")
flag.IntVar(&b.MaxSet, "maxset", 0, "do not search for change sets larger than `s` elements")
flag.DurationVar(&b.Timeout, "timeout", 0, "stop target and consider failed after duration `d`")
flag.IntVar(&b.Count, "count", 2, "run target `n` times for each trial")
flag.BoolVar(&b.Verbose, "v", false, "enable verbose output")
env := ""
envFlag := ""
flag.Func("compile", "bisect source locations affected by Go compiler `rewrite` (fma, loopvar, ...)", func(value string) error {
if envFlag != "" {
return fmt.Errorf("cannot use -%s and -compile", envFlag)
}
envFlag = "compile"
env = "GOCOMPILEDEBUG=" + value + "hash=PATTERN"
return nil
})
flag.Func("godebug", "bisect call stacks affected by GODEBUG setting `name=value`", func(value string) error {
if envFlag != "" {
return fmt.Errorf("cannot use -%s and -godebug", envFlag)
}
envFlag = "godebug"
env = "GODEBUG=" + value + "#PATTERN"
return nil
})
flag.Usage = usage
flag.Parse()
args := flag.Args()
// Split command line into env settings, command name, args.
i := 0
for i < len(args) && strings.Contains(args[i], "=") {
i++
}
if i == len(args) {
usage()
}
b.Env, b.Cmd, b.Args = args[:i], args[i], args[i+1:]
if env != "" {
b.Env = append([]string{env}, b.Env...)
}
// Check that PATTERN is available for us to vary.
found := false
for _, e := range b.Env {
if _, v, _ := strings.Cut(e, "="); strings.Contains(v, "PATTERN") {
found = true
}
}
for _, a := range b.Args {
if strings.Contains(a, "PATTERN") {
found = true
}
}
if !found {
log.Fatalf("no PATTERN in target environment or args")
}
if !b.Search() {
os.Exit(1)
}
}
// A Bisect holds the state for a bisect invocation.
type Bisect struct {
// Env is the additional environment variables for the command.
// PATTERN and RANDOM are substituted in the values, but not the names.
Env []string
// Cmd is the command (program name) to run.
// PATTERN and RANDOM are not substituted.
Cmd string
// Args is the command arguments.
// PATTERN and RANDOM are substituted anywhere they appear.
Args []string
// Command-line flags controlling bisect behavior.
Max int // maximum number of sets to report (0 = unlimited)
MaxSet int // maximum number of elements in a set (0 = unlimited)
Timeout time.Duration // kill target and assume failed after this duration (0 = unlimited)
Count int // run target this many times for each trial and give up if flaky (min 1 assumed; default 2 on command line set in main)
Verbose bool // print long output about each trial (only useful for debugging bisect itself)
// State for running bisect, replaced during testing.
// Failing change sets are printed to Stdout; all other output goes to Stderr.
Stdout io.Writer // where to write standard output (usually os.Stdout)
Stderr io.Writer // where to write standard error (usually os.Stderr)
TestRun func(env []string, cmd string, args []string) (out []byte, err error) // if non-nil, used instead of exec.Command
// State maintained by Search.
// By default, Search looks for a minimal set of changes that cause a failure when enabled.
// If Disable is true, the search is inverted and seeks a minimal set of changes that
// cause a failure when disabled. In this case, the search proceeds as normal except that
// each pattern starts with a !.
Disable bool
// SkipDigits is the number of hex digits to use in skip messages.
// If the set of available changes is the same in each run, as it should be,
// then this doesn't matter: we'll only exclude suffixes that uniquely identify
// a given change. But for some programs, especially bisecting runtime
// behaviors, sometimes enabling one change unlocks questions about other
// changes. Strictly speaking this is a misuse of bisect, but just to make
// bisect more robust, we use the y and n runs to create an estimate of the
// number of bits needed for a unique suffix, and then we round it up to
// a number of hex digits, with one extra digit for good measure, and then
// we always use that many hex digits for skips.
SkipHexDigits int
// Add is a list of suffixes to add to every trial, because they
// contain changes that are necessary for a group we are assembling.
Add []string
// Skip is a list of suffixes that uniquely identify changes to exclude from every trial,
// because they have already been used in failing change sets.
// Suffixes later in the list may only be unique after removing
// the ones earlier in the list.
// Skip applies after Add.
Skip []string
}
// A Result holds the result of a single target trial.
type Result struct {
Success bool // whether the target succeeded (exited with zero status)
Cmd string // full target command line
Out string // full target output (stdout and stderr combined)
Suffix string // the suffix used for collecting MatchIDs, MatchText, and MatchFull
MatchIDs []uint64 // match IDs enabled during this trial
MatchText []string // match reports for the IDs, with match markers removed
MatchFull []string // full match lines for the IDs, with match markers kept
}
// &searchFatal is a special panic value to signal that Search failed.
// This lets us unwind the search recursion on a fatal error
// but have Search return normally.
var searchFatal int
// Search runs a bisect search according to the configuration in b.
// It reports whether any failing change sets were found.
func (b *Bisect) Search() bool {
defer func() {
// Recover from panic(&searchFatal), implicitly returning false from Search.
// Re-panic on any other panic.
if e := recover(); e != nil && e != &searchFatal {
panic(e)
}
}()
// Run with no changes and all changes, to figure out which direction we're searching.
// The goal is to find the minimal set of changes to toggle
// starting with the state where everything works.
// If "no changes" succeeds and "all changes" fails,
// we're looking for a minimal set of changes to enable to provoke the failure
// (broken = runY, b.Negate = false)
// If "no changes" fails and "all changes" succeeds,
// we're looking for a minimal set of changes to disable to provoke the failure
// (broken = runN, b.Negate = true).
b.Logf("checking target with all changes disabled")
runN := b.Run("n")
b.Logf("checking target with all changes enabled")
runY := b.Run("y")
var broken *Result
switch {
case runN.Success && !runY.Success:
b.Logf("target succeeds with no changes, fails with all changes")
b.Logf("searching for minimal set of enabled changes causing failure")
broken = runY
b.Disable = false
case !runN.Success && runY.Success:
b.Logf("target fails with no changes, succeeds with all changes")
b.Logf("searching for minimal set of disabled changes causing failure")
broken = runN
b.Disable = true
case runN.Success && runY.Success:
b.Fatalf("target succeeds with no changes and all changes")
case !runN.Success && !runY.Success:
b.Fatalf("target fails with no changes and all changes")
}
// Compute minimum number of bits needed to distinguish
// all the changes we saw during N and all the changes we saw during Y.
b.SkipHexDigits = skipHexDigits(runN.MatchIDs, runY.MatchIDs)
// Loop finding and printing change sets, until none remain.
found := 0
for {
// Find set.
bad := b.search(broken)
if bad == nil {
if found == 0 {
b.Fatalf("cannot find any failing change sets of size ≤ %d", b.MaxSet)
}
break
}
// Confirm that set really does fail, to avoid false accusations.
// Also asking for user-visible output; earlier runs did not.
b.Logf("confirming failing change set")
b.Add = append(b.Add[:0], bad...)
broken = b.Run("v")
if broken.Success {
b.Logf("confirmation run succeeded unexpectedly")
}
b.Add = b.Add[:0]
// Print confirmed change set.
found++
b.Logf("FOUND failing change set")
desc := "(enabling changes causes failure)"
if b.Disable {
desc = "(disabling changes causes failure)"
}
fmt.Fprintf(b.Stdout, "--- change set #%d %s\n%s\n---\n", found, desc, strings.Join(broken.MatchText, "\n"))
// Stop if we've found enough change sets.
if b.Max > 0 && found >= b.Max {
break
}
// If running bisect target | tee bad.txt, prints to stdout and stderr
// both appear on the terminal, but the ones to stdout go through tee
// and can take a little bit of extra time. Sleep 1 millisecond to give
// tee time to catch up, so that its stdout print does not get interlaced
// with the stderr print from the next b.Log message.
time.Sleep(1 * time.Millisecond)
// Disable the now-known-bad changes and see if any failures remain.
b.Logf("checking for more failures")
b.Skip = append(bad, b.Skip...)
broken = b.Run("")
if broken.Success {
what := "enabled"
if b.Disable {
what = "disabled"
}
b.Logf("target succeeds with all remaining changes %s", what)
break
}
b.Logf("target still fails; searching for more bad changes")
}
return true
}
// Fatalf prints a message to standard error and then panics,
// causing Search to return false.
func (b *Bisect) Fatalf(format string, args ...any) {
s := fmt.Sprintf("bisect: fatal error: "+format, args...)
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
b.Stderr.Write([]byte(s))
panic(&searchFatal)
}
// Logf prints a message to standard error.
func (b *Bisect) Logf(format string, args ...any) {
s := fmt.Sprintf("bisect: "+format, args...)
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
b.Stderr.Write([]byte(s))
}
func skipHexDigits(idY, idN []uint64) int {
var all []uint64
seen := make(map[uint64]bool)
for _, x := range idY {
seen[x] = true
all = append(all, x)
}
for _, x := range idN {
if !seen[x] {
seen[x] = true
all = append(all, x)
}
}
sort.Slice(all, func(i, j int) bool { return bits.Reverse64(all[i]) < bits.Reverse64(all[j]) })
digits := sort.Search(64/4, func(digits int) bool {
mask := uint64(1)<<(4*digits) - 1
for i := 0; i+1 < len(all); i++ {
if all[i]&mask == all[i+1]&mask {
return false
}
}
return true
})
if digits < 64/4 {
digits++
}
return digits
}
// search searches for a single locally minimal change set.
//
// Invariant: r describes the result of r.Suffix + b.Add, which failed.
// (There's an implicit -b.Skip everywhere here. b.Skip does not change.)
// We want to extend r.Suffix to preserve the failure, working toward
// a suffix that identifies a single change.
func (b *Bisect) search(r *Result) []string {
// The caller should be passing in a failure result that we diagnose.
if r.Success {
b.Fatalf("internal error: unexpected success") // mistake by caller
}
// If the failure reported no changes, the target is misbehaving.
if len(r.MatchIDs) == 0 {
b.Fatalf("failure with no reported changes:\n\n$ %s\n%s\n", r.Cmd, r.Out)
}
// If there's one matching change, that's the one we're looking for.
if len(r.MatchIDs) == 1 {
return []string{fmt.Sprintf("x%0*x", b.SkipHexDigits, r.MatchIDs[0]&(1<<(4*b.SkipHexDigits)-1))}
}
// If the suffix we were tracking in the trial is already 64 bits,
// either the target is bad or bisect itself is buggy.
if len(r.Suffix) >= 64 {
b.Fatalf("failed to isolate a single change with very long suffix")
}
// We want to split the current matchIDs by left-extending the suffix with 0 and 1.
// If all the matches have the same next bit, that won't cause a split, which doesn't
// break the algorithm but does waste time. Avoid wasting time by left-extending
// the suffix to the longest suffix shared by all the current match IDs
// before adding 0 or 1.
suffix := commonSuffix(r.MatchIDs)
if !strings.HasSuffix(suffix, r.Suffix) {
b.Fatalf("internal error: invalid common suffix") // bug in commonSuffix
}
// Run 0suffix and 1suffix. If one fails, chase down the failure in that half.
r0 := b.Run("0" + suffix)
if !r0.Success {
return b.search(r0)
}
r1 := b.Run("1" + suffix)
if !r1.Success {
return b.search(r1)
}
// suffix failed, but 0suffix and 1suffix succeeded.
// Assuming the target isn't flaky, this means we need
// at least one change from 0suffix AND at least one from 1suffix.
// We are already tracking N = len(b.Add) other changes and are
// allowed to build sets of size at least 1+N (or we shouldn't be here at all).
// If we aren't allowed to build sets of size 2+N, give up this branch.
if b.MaxSet > 0 && 2+len(b.Add) > b.MaxSet {
return nil
}
// Adding all matches for 1suffix, recurse to narrow down 0suffix.
old := len(b.Add)
b.Add = append(b.Add, "1"+suffix)
r0 = b.Run("0" + suffix)
if r0.Success {
// 0suffix + b.Add + 1suffix = suffix + b.Add is what r describes, and it failed.
b.Fatalf("target fails inconsistently")
}
bad0 := b.search(r0)
if bad0 == nil {
// Search failed due to MaxSet limit.
return nil
}
b.Add = b.Add[:old]
// Adding the specific match we found in 0suffix, recurse to narrow down 1suffix.
b.Add = append(b.Add[:old], bad0...)
r1 = b.Run("1" + suffix)
if r1.Success {
// 1suffix + b.Add + bad0 = bad0 + b.Add + 1suffix is what b.search(r0) reported as a failure.
b.Fatalf("target fails inconsistently")
}
bad1 := b.search(r1)
if bad1 == nil {
// Search failed due to MaxSet limit.
return nil
}
b.Add = b.Add[:old]
// bad0 and bad1 together provoke the failure.
return append(bad0, bad1...)
}
// Run runs a set of trials selecting changes with the given suffix,
// plus the ones in b.Add and not the ones in b.Skip.
// The returned result's MatchIDs, MatchText, and MatchFull
// only list the changes that match suffix.
// When b.Count > 1, Run runs b.Count trials and requires
// that they all succeed or they all fail. If not, it calls b.Fatalf.
func (b *Bisect) Run(suffix string) *Result {
out := b.run(suffix)
for i := 1; i < b.Count; i++ {
r := b.run(suffix)
if r.Success != out.Success {
b.Fatalf("target fails inconsistently")
}
}
return out
}
// run runs a single trial for Run.
func (b *Bisect) run(suffix string) *Result {
random := fmt.Sprint(rand.Uint64())
// Accept suffix == "v" to mean we need user-visible output.
visible := ""
if suffix == "v" {
visible = "v"
suffix = ""
}
// Construct change ID pattern.
var pattern string
if suffix == "y" || suffix == "n" {
pattern = suffix
suffix = ""
} else {
var elem []string
if suffix != "" {
elem = append(elem, "+", suffix)
}
for _, x := range b.Add {
elem = append(elem, "+", x)
}
for _, x := range b.Skip {
elem = append(elem, "-", x)
}
pattern = strings.Join(elem, "")
if pattern == "" {
pattern = "y"
}
}
if b.Disable {
pattern = "!" + pattern
}
pattern = visible + pattern
// Construct substituted env and args.
env := make([]string, len(b.Env))
for i, x := range b.Env {
k, v, _ := strings.Cut(x, "=")
env[i] = k + "=" + replace(v, pattern, random)
}
args := make([]string, len(b.Args))
for i, x := range b.Args {
args[i] = replace(x, pattern, random)
}
// Construct and log command line.
// There is no newline in the log print.
// The line will be completed when the command finishes.
cmdText := strings.Join(append(append(env, b.Cmd), args...), " ")
fmt.Fprintf(b.Stderr, "bisect: run: %s...", cmdText)
// Run command with args and env.
var out []byte
var err error
if b.TestRun != nil {
out, err = b.TestRun(env, b.Cmd, args)
} else {
ctx := context.Background()
if b.Timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, b.Timeout)
defer cancel()
}
cmd := exec.CommandContext(ctx, b.Cmd, args...)
cmd.Env = append(os.Environ(), env...)
// Set up cmd.Cancel, cmd.WaitDelay on Go 1.20 and later
// TODO(rsc): Inline go120.go's cmdInterrupt once we stop supporting Go 1.19.
cmdInterrupt(cmd)
out, err = cmd.CombinedOutput()
}
// Parse output to construct result.
r := &Result{
Suffix: suffix,
Success: err == nil,
Cmd: cmdText,
Out: string(out),
}
// Calculate bits, mask to identify suffix matches.
var bits, mask uint64
if suffix != "" && suffix != "y" && suffix != "n" && suffix != "v" {
var err error
bits, err = strconv.ParseUint(suffix, 2, 64)
if err != nil {
b.Fatalf("internal error: bad suffix")
}
mask = uint64(1<<len(suffix)) - 1
}
// Process output, collecting match reports for suffix.
have := make(map[uint64]bool)
all := r.Out
for all != "" {
var line string
line, all, _ = strings.Cut(all, "\n")
short, id, ok := bisect.CutMarker(line)
if !ok || (id&mask) != bits {
continue
}
if !have[id] {
have[id] = true
r.MatchIDs = append(r.MatchIDs, id)
}
r.MatchText = append(r.MatchText, short)
r.MatchFull = append(r.MatchFull, line)
}
// Finish log print from above, describing the command's completion.
if err == nil {
fmt.Fprintf(b.Stderr, " ok (%d matches)\n", len(r.MatchIDs))
} else {
fmt.Fprintf(b.Stderr, " FAIL (%d matches)\n", len(r.MatchIDs))
}
if err != nil && len(r.MatchIDs) == 0 {
b.Fatalf("target failed without printing any matches\n%s", r.Out)
}
// In verbose mode, print extra debugging: all the lines with match markers.
if b.Verbose {
b.Logf("matches:\n%s", strings.Join(r.MatchFull, "\n\t"))
}
return r
}
// replace returns x with literal text PATTERN and RANDOM replaced by pattern and random.
func replace(x, pattern, random string) string {
x = strings.ReplaceAll(x, "PATTERN", pattern)
x = strings.ReplaceAll(x, "RANDOM", random)
return x
}
// commonSuffix returns the longest common binary suffix shared by all uint64s in list.
// If list is empty, commonSuffix returns an empty string.
func commonSuffix(list []uint64) string {
if len(list) == 0 {
return ""
}
b := list[0]
n := 64
for _, x := range list {
for x&((1<<n)-1) != b {
n--
b &= (1 << n) - 1
}
}
s := make([]byte, n)
for i := n - 1; i >= 0; i-- {
s[i] = '0' + byte(b&1)
b >>= 1
}
return string(s[:])
}
@@ -0,0 +1,233 @@
// 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
`)
@@ -0,0 +1,20 @@
// 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.
// Starting in Go 1.20, the global rand is auto-seeded,
// with a better value than the current Unix nanoseconds.
// Only seed if we're using older versions of Go.
//go:build !go1.20
package main
import (
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
@@ -0,0 +1,29 @@
This directory contains test inputs for the bisect command.
Each text file is a txtar archive (see <https://pkg.go.dev/golang.org/x/tools/txtar>
or `go doc txtar`).
The comment at the top of the archive is a JSON object describing a
target behavior. Specifically, the Fail key gives a boolean expression
that should provoke a failure. Bisect's job is to discover this
condition.
The Bisect key describes settings in the Bisect struct that we want to
change, to simulate the use of various command-line options.
The txtar archive files should be "stdout" and "stderr", giving the
expected standard output and standard error. If the bisect command
should exit with a non-zero status, the stderr in the archive will end
with the line "<bisect failed>".
Running `go test -update` will rewrite the stdout and stderr files in
each testdata archive to match the current state of the tool. This is
a useful command when the logging prints from bisect change or when
writing a new test.
To use `go test -update` to write a new test:
- Create a new .txt file with just a JSON object at the top,
specifying what you want to test.
- Run `go test -update`.
- Reload the .txt file and read the stdout and stderr to see if you agree.
@@ -0,0 +1,44 @@
{"Fail": "amber || apricot"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... ok (88 matches)
bisect: target succeeds with all remaining changes enabled
@@ -0,0 +1,67 @@
{"Fail": "amber || apricot", "Bisect": {"Count": 2}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... ok (88 matches)
bisect: run: test -x006-x002... ok (88 matches)
bisect: target succeeds with all remaining changes enabled
@@ -0,0 +1,57 @@
{"Fail": "amber || apricot && peach"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... ok (45 matches)
bisect: run: test +0+1-x002... FAIL (44 matches)
bisect: run: test +00+1-x002... ok (23 matches)
bisect: run: test +10+1-x002... FAIL (21 matches)
bisect: run: test +010+1-x002... ok (10 matches)
bisect: run: test +110+1-x002... FAIL (11 matches)
bisect: run: test +0110+1-x002... FAIL (6 matches)
bisect: run: test +00110+1-x002... FAIL (3 matches)
bisect: run: test +000110+1-x002... FAIL (2 matches)
bisect: run: test +0000110+1-x002... FAIL (1 matches)
bisect: run: test +1+x006-x002... FAIL (45 matches)
bisect: run: test +01+x006-x002... ok (23 matches)
bisect: run: test +11+x006-x002... FAIL (22 matches)
bisect: run: test +011+x006-x002... FAIL (11 matches)
bisect: run: test +0011+x006-x002... ok (6 matches)
bisect: run: test +1011+x006-x002... FAIL (5 matches)
bisect: run: test +01011+x006-x002... ok (3 matches)
bisect: run: test +11011+x006-x002... FAIL (2 matches)
bisect: run: test +011011+x006-x002... ok (1 matches)
bisect: run: test +111011+x006-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006+x03b-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x03b-x002... ok (87 matches)
bisect: target succeeds with all remaining changes enabled
@@ -0,0 +1,23 @@
{"Fail": "amber || apricot && peach", "Bisect": {"Max": 1}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
@@ -0,0 +1,59 @@
{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"Max": 2}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
@@ -0,0 +1,84 @@
{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 3}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches)
bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches)
bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches)
bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches)
bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches)
bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
@@ -0,0 +1,13 @@
{"Fail": "apricot && peach", "Bisect": {"MaxSet": 1}}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... ok (45 matches)
bisect: fatal error: cannot find any failing change sets of size ≤ 1
<bisect failed>
@@ -0,0 +1,138 @@
{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 4}}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
blue
green
red
---
--- change set #3 (enabling changes causes failure)
black
cyan
magenta
yellow
---
--- change set #4 (enabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... ok (44 matches)
bisect: run: test +1-x002... FAIL (45 matches)
bisect: run: test +01-x002... ok (23 matches)
bisect: run: test +11-x002... ok (22 matches)
bisect: run: test +01+11-x002... FAIL (23 matches)
bisect: run: test +001+11-x002... ok (12 matches)
bisect: run: test +101+11-x002... FAIL (11 matches)
bisect: run: test +0101+11-x002... ok (6 matches)
bisect: run: test +1101+11-x002... ok (5 matches)
bisect: run: test +0101+11+1101-x002... FAIL (6 matches)
bisect: run: test +00101+11+1101-x002... FAIL (3 matches)
bisect: run: test +000101+11+1101-x002... FAIL (2 matches)
bisect: run: test +0000101+11+1101-x002... ok (1 matches)
bisect: run: test +1000101+11+1101-x002... FAIL (1 matches)
bisect: run: test +1101+11+x045-x002... FAIL (5 matches)
bisect: run: test +01101+11+x045-x002... FAIL (3 matches)
bisect: run: test +001101+11+x045-x002... FAIL (2 matches)
bisect: run: test +0001101+11+x045-x002... FAIL (1 matches)
bisect: run: test +11+x045+x00d-x002... FAIL (22 matches)
bisect: run: test +011+x045+x00d-x002... ok (11 matches)
bisect: run: test +111+x045+x00d-x002... FAIL (11 matches)
bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches)
bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches)
bisect: run: test +000111+x045+x00d-x002... ok (2 matches)
bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches)
bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches)
bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches)
bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches)
bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches)
bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches)
bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +0001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00001+x020+x00c+1001-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +10001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +010001+x020+x00c+1001-x045-x00d-x027-x002... ok (2 matches)
bisect: run: test +110001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +01001+x020+x00c+x031-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +11001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0011001+x020+x00c+x031-x045-x00d-x027-x002... ok (1 matches)
bisect: run: test +1011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x020+x00c+x031+x059-x045-x00d-x027-x002... FAIL (4 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (82 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (42 matches)
bisect: run: test +1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (40 matches)
bisect: run: test +0+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (42 matches)
bisect: run: test +00+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (21 matches)
bisect: run: test +10+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +010+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (10 matches)
bisect: run: test +110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (6 matches)
bisect: run: test +00110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (3 matches)
bisect: run: test +000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +0000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches)
bisect: run: test +1+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (40 matches)
bisect: run: test +01+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (19 matches)
bisect: run: test +11+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches)
bisect: run: test +011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches)
bisect: run: test +0011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (6 matches)
bisect: run: test +1011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (5 matches)
bisect: run: test +01011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (3 matches)
bisect: run: test +11011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: run: test +011011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (1 matches)
bisect: run: test +111011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006+x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (80 matches)
bisect: target succeeds with all remaining changes enabled
@@ -0,0 +1,57 @@
{"Fail": "!amber || !apricot && !peach"}
-- stdout --
--- change set #1 (disabling changes causes failure)
amber
---
--- change set #2 (disabling changes causes failure)
apricot
peach
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... FAIL (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... ok (90 matches)
bisect: target fails with no changes, succeeds with all changes
bisect: searching for minimal set of disabled changes causing failure
bisect: run: test !+0... FAIL (45 matches)
bisect: run: test !+00... ok (23 matches)
bisect: run: test !+10... FAIL (22 matches)
bisect: run: test !+010... FAIL (11 matches)
bisect: run: test !+0010... FAIL (6 matches)
bisect: run: test !+00010... FAIL (3 matches)
bisect: run: test !+000010... FAIL (2 matches)
bisect: run: test !+0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v!+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test !-x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test !+0-x002... ok (44 matches)
bisect: run: test !+1-x002... ok (45 matches)
bisect: run: test !+0+1-x002... FAIL (44 matches)
bisect: run: test !+00+1-x002... ok (23 matches)
bisect: run: test !+10+1-x002... FAIL (21 matches)
bisect: run: test !+010+1-x002... ok (10 matches)
bisect: run: test !+110+1-x002... FAIL (11 matches)
bisect: run: test !+0110+1-x002... FAIL (6 matches)
bisect: run: test !+00110+1-x002... FAIL (3 matches)
bisect: run: test !+000110+1-x002... FAIL (2 matches)
bisect: run: test !+0000110+1-x002... FAIL (1 matches)
bisect: run: test !+1+x006-x002... FAIL (45 matches)
bisect: run: test !+01+x006-x002... ok (23 matches)
bisect: run: test !+11+x006-x002... FAIL (22 matches)
bisect: run: test !+011+x006-x002... FAIL (11 matches)
bisect: run: test !+0011+x006-x002... ok (6 matches)
bisect: run: test !+1011+x006-x002... FAIL (5 matches)
bisect: run: test !+01011+x006-x002... ok (3 matches)
bisect: run: test !+11011+x006-x002... FAIL (2 matches)
bisect: run: test !+011011+x006-x002... ok (1 matches)
bisect: run: test !+111011+x006-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v!+x006+x03b-x002... FAIL (2 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test !-x006-x03b-x002... ok (87 matches)
bisect: target succeeds with all remaining changes disabled
@@ -0,0 +1,59 @@
{"Fail": "amber || apricot || blue && random"}
-- stdout --
--- change set #1 (enabling changes causes failure)
amber
---
--- change set #2 (enabling changes causes failure)
apricot
---
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... FAIL (45 matches)
bisect: run: test +00... ok (23 matches)
bisect: run: test +10... FAIL (22 matches)
bisect: run: test +010... FAIL (11 matches)
bisect: run: test +0010... FAIL (6 matches)
bisect: run: test +00010... FAIL (3 matches)
bisect: run: test +000010... FAIL (2 matches)
bisect: run: test +0000010... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x002... FAIL (89 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x002... FAIL (44 matches)
bisect: run: test +00-x002... ok (23 matches)
bisect: run: test +10-x002... FAIL (21 matches)
bisect: run: test +010-x002... ok (10 matches)
bisect: run: test +110-x002... FAIL (11 matches)
bisect: run: test +0110-x002... FAIL (6 matches)
bisect: run: test +00110-x002... FAIL (3 matches)
bisect: run: test +000110-x002... FAIL (2 matches)
bisect: run: test +0000110-x002... FAIL (1 matches)
bisect: confirming failing change set
bisect: run: test v+x006-x002... FAIL (1 matches)
bisect: FOUND failing change set
bisect: checking for more failures
bisect: run: test -x006-x002... FAIL (88 matches)
bisect: target still fails; searching for more bad changes
bisect: run: test +0-x006-x002... ok (43 matches)
bisect: run: test +1-x006-x002... FAIL (45 matches)
bisect: run: test +01-x006-x002... FAIL (23 matches)
bisect: run: test +001-x006-x002... ok (12 matches)
bisect: run: test +101-x006-x002... FAIL (11 matches)
bisect: run: test +0101-x006-x002... ok (6 matches)
bisect: run: test +1101-x006-x002... FAIL (5 matches)
bisect: run: test +01101-x006-x002... ok (3 matches)
bisect: run: test +11101-x006-x002... ok (2 matches)
bisect: run: test +01101+11101-x006-x002... FAIL (3 matches)
bisect: run: test +001101+11101-x006-x002... ok (2 matches)
bisect: run: test +101101+11101-x006-x002... ok (1 matches)
bisect: run: test +001101+11101+101101-x006-x002... ok (2 matches)
bisect: fatal error: target fails inconsistently
<bisect failed>
@@ -0,0 +1,24 @@
{"Fail": "blue && random"}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +01... FAIL (23 matches)
bisect: run: test +001... ok (12 matches)
bisect: run: test +101... FAIL (11 matches)
bisect: run: test +0101... ok (6 matches)
bisect: run: test +1101... FAIL (5 matches)
bisect: run: test +01101... ok (3 matches)
bisect: run: test +11101... ok (2 matches)
bisect: run: test +01101+11101... FAIL (3 matches)
bisect: run: test +001101+11101... ok (2 matches)
bisect: run: test +101101+11101... ok (1 matches)
bisect: run: test +001101+11101+101101... ok (2 matches)
bisect: fatal error: target fails inconsistently
<bisect failed>
@@ -0,0 +1,19 @@
{"Fail": "blue && random", "Bisect": {"Count": 2}}
-- stdout --
-- stderr --
bisect: checking target with all changes disabled
bisect: run: test n... ok (90 matches)
bisect: run: test n... ok (90 matches)
bisect: checking target with all changes enabled
bisect: run: test y... FAIL (90 matches)
bisect: run: test y... FAIL (90 matches)
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: test +0... ok (45 matches)
bisect: run: test +0... ok (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +1... FAIL (45 matches)
bisect: run: test +01... FAIL (23 matches)
bisect: run: test +01... ok (23 matches)
bisect: fatal error: target fails inconsistently
<bisect failed>