aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/bisect
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-11-06 17:59:11 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-11-07 11:07:03 +0100
commit30cb7f98cd1aba45565123caf4cbd73772bb8b58 (patch)
treed43386f633ef60f4314d9d8ae2b02b0dea4af370 /pkg/bisect
parentf15876118387bb9e4c079f29d06394b36e393180 (diff)
pkg/bisect: detect wrong bisections
Detect bisection to merge commits and to commits that don't affect kernel binary (comments, other arches, whitespaces, etc). Such bisections are not reported in emails (but shown on web). Update #1271
Diffstat (limited to 'pkg/bisect')
-rw-r--r--pkg/bisect/bisect.go187
-rw-r--r--pkg/bisect/bisect_test.go334
2 files changed, 283 insertions, 238 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go
index 1458e141e..5a6197c32 100644
--- a/pkg/bisect/bisect.go
+++ b/pkg/bisect/bisect.go
@@ -56,7 +56,7 @@ type env struct {
bisecter vcs.Bisecter
commit *vcs.Commit
head *vcs.Commit
- inst instance.BuilderTester
+ inst instance.Env
numTests int
buildTime time.Duration
testTime time.Duration
@@ -64,44 +64,53 @@ type env struct {
const NumTests = 10 // number of tests we do per commit
-// Run does the bisection and returns:
-// - if bisection is conclusive, the single cause/fix commit
+// Result describes bisection result:
+// - if bisection is conclusive, the single cause/fix commit in Commits
// - for cause bisection report is the crash on the cause commit
// - for fix bisection report is nil
-// - *vcs.Commit will be nil
-// - if bisection is inconclusive, range of potential cause/fix commits
+// - Commit is nil
+// - NoopChange is set if the commit did not cause any change in the kernel binary
+// (bisection result it most likely wrong)
+// - if bisection is inconclusive, range of potential cause/fix commits in Commits
// - report is nil in such case
-// - *vcs.Commit will be nil
-// - if the crash still happens on the oldest release/HEAD (for cause/fix bisection correspondingly),
-// no commits and the crash report on the oldest release/HEAD; and *vcs.Commit will point to
-// the oldest/latest commit where crash happens.
-// - if the crash is not reproduced on the start commit, an error, *vcs.Commit will be nil
-func Run(cfg *Config) ([]*vcs.Commit, *report.Report, *vcs.Commit, error) {
+// - Commit is nil
+// - if the crash still happens on the oldest release/HEAD (for cause/fix bisection correspondingly)
+// - no commits in Commits
+// - the crash report on the oldest release/HEAD;
+// - Commit points to the oldest/latest commit where crash happens.
+type Result struct {
+ Commits []*vcs.Commit
+ Report *report.Report
+ Commit *vcs.Commit
+ NoopChange bool
+}
+
+// Run does the bisection and returns either the Result,
+// or, if the crash is not reproduced on the start commit, an error.
+func Run(cfg *Config) (*Result, error) {
if err := checkConfig(cfg); err != nil {
- return nil, nil, nil, err
+ return nil, err
}
cfg.Manager.Cover = false // it's not supported somewhere back in time
repo, err := vcs.NewRepo(cfg.Manager.TargetOS, cfg.Manager.Type, cfg.Manager.KernelSrc)
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
bisecter, ok := repo.(vcs.Bisecter)
if !ok {
- return nil, nil, nil, fmt.Errorf("bisection is not implemented for %v", cfg.Manager.TargetOS)
+ return nil, fmt.Errorf("bisection is not implemented for %v", cfg.Manager.TargetOS)
}
inst, err := instance.NewEnv(&cfg.Manager)
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
if _, err = repo.CheckoutBranch(cfg.Kernel.Repo, cfg.Kernel.Branch); err != nil {
- return nil, nil, nil, err
+ return nil, err
}
-
return runImpl(cfg, repo, bisecter, inst)
}
-func runImpl(cfg *Config, repo vcs.Repo, bisecter vcs.Bisecter, inst instance.BuilderTester) (
- []*vcs.Commit, *report.Report, *vcs.Commit, error) {
+func runImpl(cfg *Config, repo vcs.Repo, bisecter vcs.Bisecter, inst instance.Env) (*Result, error) {
env := &env{
cfg: cfg,
repo: repo,
@@ -110,7 +119,7 @@ func runImpl(cfg *Config, repo vcs.Repo, bisecter vcs.Bisecter, inst instance.Bu
}
head, err := repo.HeadCommit()
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
env.head = head
if cfg.Fix {
@@ -119,91 +128,106 @@ func runImpl(cfg *Config, repo vcs.Repo, bisecter vcs.Bisecter, inst instance.Bu
env.log("bisecting cause commit starting from %v", cfg.Kernel.Commit)
}
start := time.Now()
- commits, rep, bad, err := env.bisect()
+ res, err := env.bisect()
env.log("revisions tested: %v, total time: %v (build: %v, test: %v)",
env.numTests, time.Since(start), env.buildTime, env.testTime)
if err != nil {
env.log("error: %v", err)
- return nil, nil, nil, err
+ return nil, err
}
- if len(commits) == 0 {
+ if len(res.Commits) == 0 {
if cfg.Fix {
env.log("the crash still happens on HEAD")
} else {
env.log("the crash already happened on the oldest tested release")
}
- env.log("commit msg: %v", bad.Title)
- env.log("crash: %v\n%s", rep.Title, rep.Report)
- return nil, rep, bad, nil
+ env.log("commit msg: %v", res.Commit.Title)
+ env.log("crash: %v\n%s", res.Report.Title, res.Report.Report)
+ return res, nil
}
what := "bad"
if cfg.Fix {
what = "good"
}
- if len(commits) > 1 {
+ if len(res.Commits) > 1 {
env.log("bisection is inconclusive, the first %v commit could be any of:", what)
- for _, com := range commits {
+ for _, com := range res.Commits {
env.log("%v", com.Hash)
}
- return commits, nil, nil, nil
+ return res, nil
}
- com := commits[0]
+ com := res.Commits[0]
env.log("first %v commit: %v %v", what, com.Hash, com.Title)
env.log("cc: %q", com.CC)
- if rep != nil {
- env.log("crash: %v\n%s", rep.Title, rep.Report)
+ if res.Report != nil {
+ env.log("crash: %v\n%s", res.Report.Title, res.Report.Report)
}
- return commits, rep, nil, nil
+ return res, nil
}
-func (env *env) bisect() ([]*vcs.Commit, *report.Report, *vcs.Commit, error) {
+func (env *env) bisect() (*Result, error) {
cfg := env.cfg
var err error
if err := build.Clean(cfg.Manager.TargetOS, cfg.Manager.TargetVMArch,
cfg.Manager.Type, cfg.Manager.KernelSrc); err != nil {
- return nil, nil, nil, fmt.Errorf("kernel clean failed: %v", err)
+ return nil, fmt.Errorf("kernel clean failed: %v", err)
}
env.log("building syzkaller on %v", cfg.Syzkaller.Commit)
if err := env.inst.BuildSyzkaller(cfg.Syzkaller.Repo, cfg.Syzkaller.Commit); err != nil {
- return nil, nil, nil, err
+ return nil, err
}
com, err := env.repo.CheckoutCommit(cfg.Kernel.Repo, cfg.Kernel.Commit)
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
env.commit = com
- res, _, rep0, err := env.test()
+ testRes, err := env.test()
if err != nil {
- return nil, nil, nil, err
- } else if res != vcs.BisectBad {
- return nil, nil, nil, fmt.Errorf("the crash wasn't reproduced on the original commit")
+ return nil, err
+ } else if testRes.verdict != vcs.BisectBad {
+ return nil, fmt.Errorf("the crash wasn't reproduced on the original commit")
}
bad, good, rep1, err := env.commitRange()
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
if rep1 != nil {
- return nil, rep1, bad, nil // still not fixed/happens on the oldest release
+ return &Result{Report: rep1, Commit: bad}, nil // still not fixed/happens on the oldest release
}
- reports := make(map[string]*report.Report)
- reports[cfg.Kernel.Commit] = rep0
+ results := map[string]*testResult{cfg.Kernel.Commit: testRes}
commits, err := env.bisecter.Bisect(bad.Hash, good.Hash, cfg.Trace, func() (vcs.BisectResult, error) {
- res, com, rep, err := env.test()
- reports[com.Hash] = rep
+ testRes1, err := env.test()
+ if err != nil {
+ return 0, err
+ }
+ results[testRes1.com.Hash] = testRes1
if cfg.Fix {
- if res == vcs.BisectBad {
- res = vcs.BisectGood
- } else if res == vcs.BisectGood {
- res = vcs.BisectBad
+ if testRes1.verdict == vcs.BisectBad {
+ testRes1.verdict = vcs.BisectGood
+ } else if testRes1.verdict == vcs.BisectGood {
+ testRes1.verdict = vcs.BisectBad
}
}
- return res, err
+ return testRes1.verdict, err
})
- var rep *report.Report
+ if err != nil {
+ return nil, err
+ }
+ res := &Result{
+ Commits: commits,
+ }
if len(commits) == 1 {
- rep = reports[commits[0].Hash]
+ com := commits[0]
+ if testRes := results[com.Hash]; testRes != nil {
+ res.Report = testRes.rep
+ if testRes.kernelSign != "" && len(com.Parents) == 1 {
+ if prevRes := results[com.Parents[0]]; prevRes != nil {
+ res.NoopChange = testRes.kernelSign == prevRes.kernelSign
+ }
+ }
+ }
}
- return commits, rep, nil, err
+ return res, nil
}
func (env *env) commitRange() (*vcs.Commit, *vcs.Commit, *report.Report, error) {
@@ -218,12 +242,12 @@ func (env *env) commitRangeForFix() (*vcs.Commit, *vcs.Commit, *report.Report, e
if _, err := env.repo.SwitchCommit(env.head.Hash); err != nil {
return nil, nil, nil, err
}
- res, _, rep, err := env.test()
+ res, err := env.test()
if err != nil {
return nil, nil, nil, err
}
- if res != vcs.BisectGood {
- return env.head, nil, rep, nil
+ if res.verdict != vcs.BisectGood {
+ return env.head, nil, res.rep, nil
}
return env.head, env.commit, nil, nil
}
@@ -245,45 +269,57 @@ func (env *env) commitRangeForBug() (*vcs.Commit, *vcs.Commit, *report.Report, e
if err != nil {
return nil, nil, nil, err
}
- res, _, rep, err := env.test()
+ res, err := env.test()
if err != nil {
return nil, nil, nil, err
}
- if res == vcs.BisectGood {
+ if res.verdict == vcs.BisectGood {
return lastBad, com, nil, nil
}
- if res == vcs.BisectBad {
+ if res.verdict == vcs.BisectBad {
lastBad = com
- lastRep = rep
+ lastRep = res.rep
}
}
return lastBad, nil, lastRep, nil
}
-func (env *env) test() (vcs.BisectResult, *vcs.Commit, *report.Report, error) {
+type testResult struct {
+ verdict vcs.BisectResult
+ com *vcs.Commit
+ rep *report.Report
+ kernelSign string
+}
+
+func (env *env) test() (*testResult, error) {
cfg := env.cfg
env.numTests++
current, err := env.repo.HeadCommit()
if err != nil {
- return 0, nil, nil, err
+ return nil, err
}
bisectEnv, err := env.bisecter.EnvForCommit(cfg.BinDir, current.Hash, cfg.Kernel.Config)
if err != nil {
- return 0, nil, nil, err
+ return nil, err
}
compilerID, err := build.CompilerIdentity(bisectEnv.Compiler)
if err != nil {
- return 0, nil, nil, err
+ return nil, err
}
env.log("testing commit %v with %v", current.Hash, compilerID)
buildStart := time.Now()
if err := build.Clean(cfg.Manager.TargetOS, cfg.Manager.TargetVMArch,
cfg.Manager.Type, cfg.Manager.KernelSrc); err != nil {
- return 0, nil, nil, fmt.Errorf("kernel clean failed: %v", err)
+ return nil, fmt.Errorf("kernel clean failed: %v", err)
}
- _, err = env.inst.BuildKernel(bisectEnv.Compiler, cfg.Kernel.Userspace,
+ _, kernelSign, err := env.inst.BuildKernel(bisectEnv.Compiler, cfg.Kernel.Userspace,
cfg.Kernel.Cmdline, cfg.Kernel.Sysctl, bisectEnv.KernelConfig)
env.buildTime += time.Since(buildStart)
+ res := &testResult{
+ verdict: vcs.BisectSkip,
+ com: current,
+ kernelSign: kernelSign,
+ }
if err != nil {
if verr, ok := err.(*osutil.VerboseError); ok {
env.log("%v", verr.Title)
@@ -294,27 +330,28 @@ func (env *env) test() (vcs.BisectResult, *vcs.Commit, *report.Report, error) {
} else {
env.log("%v", err)
}
- return vcs.BisectSkip, current, nil, nil
+ return res, nil
}
testStart := time.Now()
results, err := env.inst.Test(NumTests, cfg.Repro.Syz, cfg.Repro.Opts, cfg.Repro.C)
env.testTime += time.Since(testStart)
if err != nil {
env.log("failed: %v", err)
- return vcs.BisectSkip, current, nil, nil
+ return res, nil
}
bad, good, rep := env.processResults(current, results)
- res := vcs.BisectSkip
+ res.rep = rep
+ res.verdict = vcs.BisectSkip
if bad != 0 {
- res = vcs.BisectBad
+ res.verdict = vcs.BisectBad
} else if NumTests-good-bad > NumTests/3*2 {
// More than 2/3 of instances failed with infrastructure error,
// can't reliably tell that the commit is good.
- res = vcs.BisectSkip
+ res.verdict = vcs.BisectSkip
} else if good != 0 {
- res = vcs.BisectGood
+ res.verdict = vcs.BisectGood
}
- return res, current, rep, nil
+ return res, nil
}
func (env *env) processResults(current *vcs.Commit, results []error) (bad, good int, rep *report.Report) {
diff --git a/pkg/bisect/bisect_test.go b/pkg/bisect/bisect_test.go
index 72e12fbe3..04609e701 100644
--- a/pkg/bisect/bisect_test.go
+++ b/pkg/bisect/bisect_test.go
@@ -7,7 +7,6 @@ import (
"bytes"
"fmt"
"io/ioutil"
- "math"
"os"
"strconv"
"testing"
@@ -21,13 +20,9 @@ import (
// testEnv will implement instance.BuilderTester. This allows us to
// set bisect.env.inst to a testEnv object.
type testEnv struct {
- repo *vcs.TestRepo
- r vcs.Repo
- t *testing.T
- fix bool
- brokenStart float64
- brokenEnd float64
- culprit float64
+ t *testing.T
+ r vcs.Repo
+ test BisectionTest
}
func (env *testEnv) BuildSyzkaller(repo, commit string) error {
@@ -35,90 +30,68 @@ func (env *testEnv) BuildSyzkaller(repo, commit string) error {
}
func (env *testEnv) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string,
- kernelConfig []byte) (string, error) {
- return "", nil
-}
-
-func crashErrors(num int, title string) []error {
- var errors []error
- for i := 0; i < num; i++ {
- errors = append(errors, &instance.CrashError{
- Report: &report.Report{
- Title: fmt.Sprintf("crashes at %v", title),
- },
- })
+ kernelConfig []byte) (string, string, error) {
+ commit := env.headCommit()
+ kernelSign := fmt.Sprintf("sign-%v", commit)
+ if commit >= env.test.sameBinaryStart && commit <= env.test.sameBinaryEnd {
+ kernelSign = "same-sign"
}
- return errors
+ return "", kernelSign, nil
}
-func nilErrors(num int) []error {
- var errors []error
- for i := 0; i < num; i++ {
- errors = append(errors, nil)
+func (env *testEnv) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error) {
+ commit := env.headCommit()
+ if commit >= env.test.brokenStart && commit <= env.test.brokenEnd {
+ return nil, fmt.Errorf("broken build")
}
- return errors
+ if !env.test.fix && commit >= env.test.culprit || env.test.fix && commit < env.test.culprit {
+ return crashErrors(numVMs, "crash occurs"), nil
+ }
+ return make([]error, numVMs), nil
}
-func (env *testEnv) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error) {
- hc, err := env.r.HeadCommit()
+func (env *testEnv) headCommit() int {
+ com, err := env.r.HeadCommit()
if err != nil {
env.t.Fatal(err)
}
- commit, err := strconv.ParseFloat(hc.Title, 64)
+ commit, err := strconv.ParseUint(com.Title, 10, 64)
if err != nil {
- env.t.Fatalf("invalid commit title: %v", hc.Title)
- }
- var e error
- var res []error
- if commit >= env.brokenStart && commit <= env.brokenEnd {
- e = fmt.Errorf("broken build")
- } else if commit < env.culprit && !env.fix || commit >= env.culprit && env.fix {
- res = nilErrors(numVMs)
- } else {
- res = crashErrors(numVMs, "crash occurs")
+ env.t.Fatalf("invalid commit title: %v", com.Title)
}
- return res, e
+ return int(commit)
}
-type Ctx struct {
- t *testing.T
- baseDir string
- repo *vcs.TestRepo
- r vcs.Repo
- cfg *Config
- inst *testEnv
- originRepo *vcs.TestRepo
-}
-
-func NewCtx(t *testing.T, fix bool, brokenStart, brokenEnd, culprit float64, commit string) *Ctx {
- baseDir, err := ioutil.TempDir("", "syz-git-test")
+func runBisection(t *testing.T, test BisectionTest) (*Result, error) {
+ baseDir, err := ioutil.TempDir("", "syz-bisect-test")
if err != nil {
t.Fatal(err)
}
- originRepo := vcs.CreateTestRepo(t, baseDir, "originRepo")
+ defer os.RemoveAll(baseDir)
+ repo := vcs.CreateTestRepo(t, baseDir, "repo")
+ if !repo.SupportsBisection() {
+ t.Skip("bisection is unsupported by git (probably too old version)")
+ }
for rv := 4; rv < 10; rv++ {
for i := 0; i < 6; i++ {
- originRepo.CommitChange(fmt.Sprintf("%v", rv*100+i))
+ repo.CommitChange(fmt.Sprintf("%v", rv*100+i))
if i == 0 {
- originRepo.SetTag(fmt.Sprintf("v%v.0", rv))
+ repo.SetTag(fmt.Sprintf("v%v.0", rv))
}
}
}
- if !originRepo.SupportsBisection() {
- t.Skip("bisection is unsupported by git (probably too old version)")
- }
- repo := vcs.CloneTestRepo(t, baseDir, "repo", originRepo)
r, err := vcs.NewRepo("test", "64", repo.Dir)
if err != nil {
t.Fatal(err)
}
- sc, err := r.GetCommitByTitle(commit)
+ sc, err := r.GetCommitByTitle(fmt.Sprint(test.startCommit))
if err != nil {
t.Fatal(err)
}
+ trace := new(bytes.Buffer)
cfg := &Config{
- Fix: fix,
- Trace: new(bytes.Buffer),
+ Fix: test.fix,
+ Trace: trace,
Manager: mgrconfig.Config{
TargetOS: "test",
TargetVMArch: "64",
@@ -126,177 +99,212 @@ func NewCtx(t *testing.T, fix bool, brokenStart, brokenEnd, culprit float64, com
KernelSrc: repo.Dir,
},
Kernel: KernelConfig{
- Repo: originRepo.Dir,
+ Repo: repo.Dir,
Commit: sc.Hash,
},
}
inst := &testEnv{
- repo: repo,
- r: r,
- t: t,
- fix: fix,
- brokenStart: brokenStart,
- brokenEnd: brokenEnd,
- culprit: culprit,
- }
- c := &Ctx{
- t: t,
- baseDir: baseDir,
- repo: repo,
- r: r,
- cfg: cfg,
- inst: inst,
- originRepo: originRepo,
+ t: t,
+ r: r,
+ test: test,
}
- return c
+ res, err := runImpl(cfg, r, r.(vcs.Bisecter), inst)
+ t.Log(trace.String())
+ return res, err
}
-type BisectionTests struct {
+type BisectionTest struct {
// input environment
name string
fix bool
- startCommit string
- brokenStart float64
- brokenEnd float64
+ startCommit int
+ brokenStart int
+ brokenEnd int
+ // Range of commits that result in the same kernel binary signature.
+ sameBinaryStart int
+ sameBinaryEnd int
// expected output
- errIsNil bool
+ expectErr bool
+ expectRep bool
+ noopChange bool
commitLen int
- repIsNil bool
- oldestLatest string
+ oldestLatest int
// input and output
- culprit float64
+ culprit int
}
func TestBisectionResults(t *testing.T) {
t.Parallel()
- var tests = []BisectionTests{
+ tests := []BisectionTest{
// Tests that bisection returns the correct cause commit.
{
- name: "bisect cause finds cause",
- fix: false,
- startCommit: "905",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: true,
+ name: "cause-finds-cause",
+ startCommit: 905,
commitLen: 1,
- repIsNil: false,
+ expectRep: true,
culprit: 602,
},
// Tests that cause bisection returns error when crash does not reproduce
// on the original commit.
{
- name: "bisect cause does not repro",
- fix: false,
- startCommit: "400",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: false,
- commitLen: 0,
- repIsNil: true,
- culprit: math.Inf(0),
+ name: "cause-does-not-repro",
+ startCommit: 400,
+ expectErr: true,
},
// Tests that no commits are returned when crash occurs on oldest commit
// for cause bisection.
{
- name: "bisect cause crashes oldest",
- fix: false,
- startCommit: "905",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: true,
+ name: "cause-crashes-oldest",
+ startCommit: 905,
commitLen: 0,
- repIsNil: false,
+ expectRep: true,
culprit: 0,
- oldestLatest: "400",
+ oldestLatest: 400,
},
- // Tests that more than 1 commit is returned when cause bisection is
- // inconclusive.
+ // Tests that more than 1 commit is returned when cause bisection is inconclusive.
{
- name: "bisect cause inconclusive",
- fix: false,
- startCommit: "802",
+ name: "cause-inconclusive",
+ startCommit: 802,
brokenStart: 500,
brokenEnd: 700,
- errIsNil: true,
commitLen: 14,
- repIsNil: true,
culprit: 605,
},
// Tests that bisection returns the correct fix commit.
{
- name: "bisect fix finds fix",
+ name: "fix-finds-fix",
fix: true,
- startCommit: "400",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: true,
+ startCommit: 400,
commitLen: 1,
- repIsNil: true,
culprit: 500,
},
// Tests that fix bisection returns error when crash does not reproduce
// on the original commit.
{
- name: "bisect fix does not repro",
+ name: "fix-does-not-repro",
fix: true,
- startCommit: "905",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: false,
- commitLen: 0,
- repIsNil: true,
- culprit: 0,
+ startCommit: 905,
+ expectErr: true,
},
// Tests that no commits are returned when crash occurs on HEAD
// for fix bisection.
{
- name: "bisect fix crashes HEAD",
+ name: "fix-crashes-HEAD",
fix: true,
- startCommit: "400",
- brokenStart: math.Inf(0),
- brokenEnd: 0,
- errIsNil: true,
+ startCommit: 400,
commitLen: 0,
- repIsNil: false,
+ expectRep: true,
culprit: 1000,
- oldestLatest: "905",
+ oldestLatest: 905,
},
- // Tests that more than 1 commit is returned when fix bisection is
- // inconclusive.
+ // Tests that more than 1 commit is returned when fix bisection is inconclusive.
{
- name: "bisect fix inconclusive",
+ name: "fix-inconclusive",
fix: true,
- startCommit: "400",
+ startCommit: 400,
brokenStart: 500,
brokenEnd: 600,
- errIsNil: true,
commitLen: 8,
- repIsNil: true,
culprit: 501,
},
+ {
+ name: "cause-same-binary",
+ startCommit: 905,
+ commitLen: 1,
+ expectRep: true,
+ culprit: 503,
+ sameBinaryStart: 502,
+ sameBinaryEnd: 503,
+ noopChange: true,
+ },
+ {
+ name: "cause-same-binary-off-by-one",
+ startCommit: 905,
+ commitLen: 1,
+ expectRep: true,
+ culprit: 503,
+ sameBinaryStart: 400,
+ sameBinaryEnd: 502,
+ },
+ {
+ name: "cause-same-binary-off-by-one-2",
+ startCommit: 905,
+ commitLen: 1,
+ expectRep: true,
+ culprit: 503,
+ sameBinaryStart: 503,
+ sameBinaryEnd: 905,
+ },
+ {
+ name: "fix-same-binary",
+ fix: true,
+ startCommit: 400,
+ commitLen: 1,
+ culprit: 503,
+ sameBinaryStart: 502,
+ sameBinaryEnd: 504,
+ noopChange: true,
+ },
}
for _, test := range tests {
- t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) {
- c := NewCtx(t, test.fix, test.brokenStart, test.brokenEnd, test.culprit, test.startCommit)
- defer os.RemoveAll(c.baseDir)
- commits, rep, com, err := runImpl(c.cfg, c.r, c.r.(vcs.Bisecter), c.inst)
- if test.errIsNil && err != nil || !test.errIsNil && err == nil {
- t.Fatalf("returned error: '%v'", err)
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ if test.expectErr &&
+ (test.commitLen != 0 ||
+ test.expectRep ||
+ test.oldestLatest != 0 ||
+ test.culprit != 0) {
+ t.Fatalf("expecting non-default values on error")
+ }
+ if test.brokenStart > test.brokenEnd {
+ t.Fatalf("bad broken start/end: %v/%v",
+ test.brokenStart, test.brokenEnd)
+ }
+ if test.sameBinaryStart > test.sameBinaryEnd {
+ t.Fatalf("bad same binary start/end: %v/%v",
+ test.sameBinaryStart, test.sameBinaryEnd)
+ }
+ res, err := runBisection(t, test)
+ if test.expectErr != (err != nil) {
+ t.Fatalf("returned error: %v", err)
+ }
+ if err != nil {
+ if res != nil {
+ t.Fatalf("got both result and error: '%v' %+v", err, *res)
+ }
+ return
+ }
+ if len(res.Commits) != test.commitLen {
+ t.Fatalf("expected %d commits got %d commits", test.commitLen, len(res.Commits))
}
- if len(commits) != test.commitLen {
- t.Fatalf("expected %d commits got %d commits", test.commitLen, len(commits))
+ expectedTitle := fmt.Sprint(test.culprit)
+ if len(res.Commits) == 1 && expectedTitle != res.Commits[0].Title {
+ t.Fatalf("expected commit '%v' got '%v'", expectedTitle, res.Commits[0].Title)
}
- expectedTitle := fmt.Sprintf("%v", test.culprit)
- if len(commits) == 1 && expectedTitle != commits[0].Title {
- t.Fatalf("expected commit '%v' got '%v'", expectedTitle, commits[0].Title)
+ if test.expectRep != (res.Report != nil) {
+ t.Fatalf("got rep: %v, want: %v", res.Report, test.expectRep)
}
- if test.repIsNil && rep != nil || !test.repIsNil && rep == nil {
- t.Fatalf("returned rep: '%v'", err)
+ if res.NoopChange != test.noopChange {
+ t.Fatalf("got noop change: %v, want: %v", res.NoopChange, test.noopChange)
}
- if test.oldestLatest != "" && test.oldestLatest != com.Title ||
- test.oldestLatest == "" && com != nil {
- t.Fatalf("expected latest/oldest: '%v' got '%v'", test.oldestLatest, com.Title)
+ if test.oldestLatest != 0 && fmt.Sprint(test.oldestLatest) != res.Commit.Title ||
+ test.oldestLatest == 0 && res.Commit != nil {
+ t.Fatalf("expected latest/oldest: %v got '%v'",
+ test.oldestLatest, res.Commit.Title)
}
})
}
}
+
+func crashErrors(num int, title string) []error {
+ var errors []error
+ for i := 0; i < num; i++ {
+ errors = append(errors, &instance.CrashError{
+ Report: &report.Report{
+ Title: fmt.Sprintf("crashes at %v", title),
+ },
+ })
+ }
+ return errors
+}