aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/bisect
diff options
context:
space:
mode:
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
+}