aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorZubin Mithra <zsm@chromium.org>2019-10-08 15:57:52 -0700
committerDmitry Vyukov <dvyukov@google.com>2019-10-22 10:09:57 +0200
commita2bdbd8c37841cc507a2ad59f25d90d6467e5858 (patch)
treebc4ad0e738a749c2321b7181e4ea04c5615738e7 /pkg
parent37dc03de04826cc0d5d1e3699832b0a3113d40af (diff)
pkg/bisect: add initial testing support for cause bisection
(note: incomplete change) Refactor existing code as follows: * Move reusable test utility functions from git_repo_test.go to pkg/vcs/test_util.go and make them exported. * Split Run() into Run()+runImpl(). * Change type of bisect.go:env.inst to `instance.BuilderTester`. Change usage inside syz-testbuild/testbuild.go accordingly. * Move most of linux.PreviousReleaseTags() into vcs/git.go as git.previousReleaseTags(). * Allow build.CompilerIdentity to be mocked. Introduce the following changes: * instance.BuilderTester is an interface with methods BuildSyzkaller() BuildKernel() Test() NewEnv() now returns this interface. * type testEnv implements instance.BuilderTester. * type testBuilder implements builder interface. Add a entry into table inside pkg/build/build.go:getBuilder() to return testBuilder object.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/bisect/bisect.go27
-rw-r--r--pkg/bisect/bisect_test.go133
-rw-r--r--pkg/build/build.go3
-rw-r--r--pkg/build/testlinux.go16
-rw-r--r--pkg/instance/instance.go18
-rw-r--r--pkg/vcs/git.go24
-rw-r--r--pkg/vcs/git_repo_test.go125
-rw-r--r--pkg/vcs/linux.go22
-rw-r--r--pkg/vcs/test_util.go116
-rw-r--r--pkg/vcs/testos.go23
-rw-r--r--pkg/vcs/vcs.go2
11 files changed, 369 insertions, 140 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go
index 78cb58bd4..904be4b53 100644
--- a/pkg/bisect/bisect.go
+++ b/pkg/bisect/bisect.go
@@ -55,7 +55,7 @@ type env struct {
repo vcs.Repo
bisecter vcs.Bisecter
head *vcs.Commit
- inst *instance.Env
+ inst instance.BuilderTester
numTests int
buildTime time.Duration
testTime time.Duration
@@ -85,11 +85,30 @@ func Run(cfg *Config) ([]*vcs.Commit, *report.Report, error) {
if !ok {
return nil, nil, fmt.Errorf("bisection is not implemented for %v", cfg.Manager.TargetOS)
}
+ inst, err := instance.NewEnv(&cfg.Manager)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, err = repo.CheckoutBranch(cfg.Kernel.Repo, cfg.Kernel.Branch); err != nil {
+ return nil, 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, error) {
env := &env{
cfg: cfg,
repo: repo,
bisecter: bisecter,
+ inst: inst,
}
+ head, err := repo.HeadCommit()
+ if err != nil {
+ return nil, nil, err
+ }
+ env.head = head
if cfg.Fix {
env.log("bisecting fixing commit since %v", cfg.Kernel.Commit)
} else {
@@ -135,12 +154,6 @@ func Run(cfg *Config) ([]*vcs.Commit, *report.Report, error) {
func (env *env) bisect() ([]*vcs.Commit, *report.Report, error) {
cfg := env.cfg
var err error
- if env.inst, err = instance.NewEnv(&cfg.Manager); err != nil {
- return nil, nil, err
- }
- if env.head, err = env.repo.CheckoutBranch(cfg.Kernel.Repo, cfg.Kernel.Branch); err != nil {
- return nil, nil, err
- }
if err := build.Clean(cfg.Manager.TargetOS, cfg.Manager.TargetVMArch,
cfg.Manager.Type, cfg.Manager.KernelSrc); err != nil {
return nil, nil, fmt.Errorf("kernel clean failed: %v", err)
diff --git a/pkg/bisect/bisect_test.go b/pkg/bisect/bisect_test.go
new file mode 100644
index 000000000..472fde289
--- /dev/null
+++ b/pkg/bisect/bisect_test.go
@@ -0,0 +1,133 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package bisect
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "testing"
+
+ "github.com/google/syzkaller/pkg/build"
+ "github.com/google/syzkaller/pkg/instance"
+ "github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/pkg/report"
+ "github.com/google/syzkaller/pkg/vcs"
+)
+
+// 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
+ // TODO: add a "fix bool" here so that Test() can return results according to
+ // whether fix/cause bisection is happening.
+}
+
+func (env *testEnv) BuildSyzkaller(repo, commit string) error {
+ return nil
+}
+
+func (env *testEnv) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string,
+ kernelConfig []byte) (string, error) {
+ return "", nil
+}
+
+func (env *testEnv) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error) {
+ hc, err := env.r.HeadCommit()
+ if err != nil {
+ env.t.Fatal(err)
+ }
+ // For cause bisection, if newer than or equal to 602, it crashes.
+ // -- 602 is the cause commit.
+ // TODO: for fix bisection(check env.fix), if older than 602, it crashes.
+ // -- 602 is the fix commit.
+ val, err := strconv.Atoi(hc.Title)
+ if err != nil {
+ env.t.Fatalf("invalid commit title: %v", val)
+ }
+ if val >= 602 {
+ var errors []error
+ for i := 0; i < numVMs; i++ {
+ errors = append(errors, &instance.CrashError{
+ Report: &report.Report{
+ Title: fmt.Sprintf("crashes at %v", hc.Title),
+ },
+ })
+ }
+ return errors, nil
+ }
+ var errors []error
+ for i := 0; i < numVMs; i++ {
+ errors = append(errors, nil)
+ }
+ return errors, nil
+}
+
+// TestBisectCause tests that bisection returns the correct cause
+// commit.
+func TestBisectCause(t *testing.T) {
+ t.Parallel()
+ build.CompilerIdentity = func(_ string) (string, error) {
+ return "unused-compiler-identity", nil
+ }
+ baseDir, err := ioutil.TempDir("", "syz-git-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(baseDir)
+ originRepo := vcs.CreateTestRepo(t, baseDir, "originRepo")
+ for rv := 4; rv < 10; rv++ {
+ for i := 0; i < 6; i++ {
+ originRepo.CommitChange(fmt.Sprintf("%v", rv*100+i))
+ if i == 0 {
+ originRepo.SetTag(fmt.Sprintf("v%v.0", rv))
+ }
+ }
+ }
+ repo := vcs.CloneTestRepo(t, baseDir, "repo", originRepo)
+ r, err := vcs.NewRepo("test", "64", repo.Dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ head, err := r.HeadCommit()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cfg := &Config{
+ Fix: false,
+ Trace: new(bytes.Buffer),
+ Manager: mgrconfig.Config{
+ TargetOS: "test",
+ TargetVMArch: "64",
+ Type: "qemu",
+ KernelSrc: repo.Dir,
+ },
+ Kernel: KernelConfig{
+ Commit: head.Hash,
+ Repo: originRepo.Dir,
+ },
+ }
+ inst := &testEnv{
+ repo: repo,
+ r: r,
+ t: t,
+ }
+ commits, rep, err := runImpl(cfg, r, r.(vcs.Bisecter), inst)
+ if err != nil {
+ t.Fatalf("returned error: '%v'", err)
+ }
+ if len(commits) != 1 {
+ t.Fatalf("Got %d commits: %v", len(commits), commits)
+ }
+ if commits[0].Title != "602" {
+ t.Fatalf("Expected commit '602' got '%v'", commits[0].Title)
+ }
+ if rep == nil {
+ t.Fatal("returned rep==nil, report should not be empty")
+ }
+}
diff --git a/pkg/build/build.go b/pkg/build/build.go
index 7e2a71ae4..7fce681df 100644
--- a/pkg/build/build.go
+++ b/pkg/build/build.go
@@ -79,6 +79,7 @@ func getBuilder(targetOS, targetArch, vmType string) (builder, error) {
{"openbsd", "amd64", []string{"gce", "vmm"}, openbsd{}},
{"netbsd", "amd64", []string{"gce", "qemu"}, netbsd{}},
{"freebsd", "amd64", []string{"gce", "qemu"}, freebsd{}},
+ {"test", "64", []string{"qemu"}, testBuilder{}},
}
for _, s := range supported {
if targetOS == s.OS && targetArch == s.arch {
@@ -92,7 +93,7 @@ func getBuilder(targetOS, targetArch, vmType string) (builder, error) {
return nil, fmt.Errorf("unsupported image type %v/%v/%v", targetOS, targetArch, vmType)
}
-func CompilerIdentity(compiler string) (string, error) {
+var CompilerIdentity = func(compiler string) (string, error) {
if compiler == "" {
return "", nil
}
diff --git a/pkg/build/testlinux.go b/pkg/build/testlinux.go
new file mode 100644
index 000000000..482c162c5
--- /dev/null
+++ b/pkg/build/testlinux.go
@@ -0,0 +1,16 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package build
+
+// TypeBuilder implements the builder interface.
+type testBuilder struct{}
+
+func (tb testBuilder) build(targetArch, vmType, kernelDir, outputDir, compiler, userspaceDir,
+ cmdlineFile, sysctlFile string, config []byte) error {
+ return nil
+}
+
+func (tb testBuilder) clean(string, string) error {
+ return nil
+}
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index cc1b147f1..b3e24aace 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -27,11 +27,17 @@ import (
"github.com/google/syzkaller/vm"
)
-type Env struct {
+type BuilderTester interface {
+ BuildSyzkaller(string, string) error
+ BuildKernel(string, string, string, string, []byte) (string, error)
+ Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error)
+}
+
+type env struct {
cfg *mgrconfig.Config
}
-func NewEnv(cfg *mgrconfig.Config) (*Env, error) {
+func NewEnv(cfg *mgrconfig.Config) (BuilderTester, error) {
if !vm.AllowsOvercommit(cfg.Type) {
return nil, fmt.Errorf("test instances are not supported for %v VMs", cfg.Type)
}
@@ -47,13 +53,13 @@ func NewEnv(cfg *mgrconfig.Config) (*Env, error) {
if err := osutil.MkdirAll(cfg.Workdir); err != nil {
return nil, fmt.Errorf("failed to create tmp dir: %v", err)
}
- env := &Env{
+ env := &env{
cfg: cfg,
}
return env, nil
}
-func (env *Env) BuildSyzkaller(repo, commit string) error {
+func (env *env) BuildSyzkaller(repo, commit string) error {
cfg := env.cfg
srcIndex := strings.LastIndex(cfg.Syzkaller, "/src/")
if srcIndex == -1 {
@@ -82,7 +88,7 @@ func (env *Env) BuildSyzkaller(repo, commit string) error {
return nil
}
-func (env *Env) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string,
+func (env *env) BuildKernel(compilerBin, userspaceDir, cmdlineFile, sysctlFile string,
kernelConfig []byte) (string, error) {
cfg := env.cfg
imageDir := filepath.Join(cfg.Workdir, "image")
@@ -170,7 +176,7 @@ func (err *CrashError) Error() string {
// Test boots numVMs VMs, tests basic kernel operation, and optionally tests the provided reproducer.
// TestError is returned if there is a problem with kernel/image (crash, reboot loop, etc).
// CrashError is returned if the reproducer crashes kernel.
-func (env *Env) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error) {
+func (env *env) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]error, error) {
if err := mgrconfig.Complete(env.cfg); err != nil {
return nil, err
}
diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go
index 7b0a621b6..359c3d68e 100644
--- a/pkg/vcs/git.go
+++ b/pkg/vcs/git.go
@@ -450,3 +450,27 @@ func (git *git) bisectInconclusive(output []byte) ([]*Commit, error) {
}
return commits, nil
}
+
+func (git *git) previousReleaseTags(commit string, self bool) ([]string, error) {
+ var tags []string
+ if self {
+ output, err := git.git("tag", "--list", "--points-at", commit, "--merged", commit, "v*.*")
+ if err != nil {
+ return nil, err
+ }
+ tags, err = gitParseReleaseTags(output)
+ if err != nil {
+ return nil, err
+ }
+ }
+ output, err := git.git("tag", "--no-contains", commit, "--merged", commit, "v*.*")
+ if err != nil {
+ return nil, err
+ }
+ tags1, err := gitParseReleaseTags(output)
+ if err != nil {
+ return nil, err
+ }
+ tags = append(tags, tags1...)
+ return tags, nil
+}
diff --git a/pkg/vcs/git_repo_test.go b/pkg/vcs/git_repo_test.go
index 017aaa658..227834b71 100644
--- a/pkg/vcs/git_repo_test.go
+++ b/pkg/vcs/git_repo_test.go
@@ -10,10 +10,8 @@ import (
"path/filepath"
"sort"
"testing"
- "time"
"github.com/google/go-cmp/cmp"
- "github.com/google/syzkaller/pkg/osutil"
)
func init() {
@@ -21,12 +19,6 @@ func init() {
os.Setenv("SYZ_DISABLE_SANDBOXING", "yes")
}
-const (
- userEmail = `test@syzkaller.com`
- userName = `Test Syzkaller`
- extractFixTagsEmail = `"syzbot" <syzbot@my.mail.com>`
-)
-
func TestGitRepo(t *testing.T) {
t.Parallel()
baseDir, err := ioutil.TempDir("", "syz-git-test")
@@ -34,30 +26,30 @@ func TestGitRepo(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(baseDir)
- repo1 := createTestRepo(t, baseDir, "repo1")
- repo2 := createTestRepo(t, baseDir, "repo2")
+ repo1 := CreateTestRepo(t, baseDir, "repo1")
+ repo2 := CreateTestRepo(t, baseDir, "repo2")
repo := newGit(filepath.Join(baseDir, "repo"), nil)
{
- com, err := repo.Poll(repo1.dir, "master")
+ com, err := repo.Poll(repo1.Dir, "master")
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(com, repo1.commits["master"]["1"]); diff != "" {
+ if diff := cmp.Diff(com, repo1.Commits["master"]["1"]); diff != "" {
t.Fatal(diff)
}
}
{
- com, err := repo.CheckoutBranch(repo1.dir, "branch1")
+ com, err := repo.CheckoutBranch(repo1.Dir, "branch1")
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(com, repo1.commits["branch1"]["1"]); diff != "" {
+ if diff := cmp.Diff(com, repo1.Commits["branch1"]["1"]); diff != "" {
t.Fatal(diff)
}
}
{
- want := repo1.commits["branch1"]["0"]
- com, err := repo.CheckoutCommit(repo1.dir, want.Hash)
+ want := repo1.Commits["branch1"]["0"]
+ com, err := repo.CheckoutCommit(repo1.Dir, want.Hash)
if err != nil {
t.Fatal(err)
}
@@ -66,7 +58,7 @@ func TestGitRepo(t *testing.T) {
}
}
{
- commits, err := repo.ListRecentCommits(repo1.commits["branch1"]["1"].Hash)
+ commits, err := repo.ListRecentCommits(repo1.Commits["branch1"]["1"].Hash)
if err != nil {
t.Fatal(err)
}
@@ -76,8 +68,8 @@ func TestGitRepo(t *testing.T) {
}
}
{
- want := repo2.commits["branch1"]["0"]
- com, err := repo.CheckoutCommit(repo2.dir, want.Hash)
+ want := repo2.Commits["branch1"]["0"]
+ com, err := repo.CheckoutCommit(repo2.Dir, want.Hash)
if err != nil {
t.Fatal(err)
}
@@ -86,8 +78,8 @@ func TestGitRepo(t *testing.T) {
}
}
{
- want := repo2.commits["branch1"]["1"]
- com, err := repo.CheckoutCommit(repo2.dir, want.Hash)
+ want := repo2.Commits["branch1"]["1"]
+ com, err := repo.CheckoutCommit(repo2.Dir, want.Hash)
if err != nil {
t.Fatal(err)
}
@@ -96,16 +88,16 @@ func TestGitRepo(t *testing.T) {
}
}
{
- com, err := repo.CheckoutBranch(repo2.dir, "branch2")
+ com, err := repo.CheckoutBranch(repo2.Dir, "branch2")
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(com, repo2.commits["branch2"]["1"]); diff != "" {
+ if diff := cmp.Diff(com, repo2.Commits["branch2"]["1"]); diff != "" {
t.Fatal(diff)
}
}
{
- want := repo2.commits["branch2"]["0"]
+ want := repo2.Commits["branch2"]["0"]
com, err := repo.SwitchCommit(want.Hash)
if err != nil {
t.Fatal(err)
@@ -123,9 +115,9 @@ func TestMetadata(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(repoDir)
- repo := makeTestRepo(t, repoDir)
+ repo := MakeTestRepo(t, repoDir)
for i, test := range metadataTests {
- repo.commitChange(test.description)
+ repo.CommitChange(test.description)
com, err := repo.repo.HeadCommit()
if err != nil {
t.Fatal(err)
@@ -297,10 +289,10 @@ func TestBisect(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(repoDir)
- repo := makeTestRepo(t, repoDir)
+ repo := MakeTestRepo(t, repoDir)
var commits []string
for i := 0; i < 5; i++ {
- repo.commitChange(fmt.Sprintf("commit %v", i))
+ repo.CommitChange(fmt.Sprintf("commit %v", i))
com, err := repo.repo.HeadCommit()
if err != nil {
t.Fatal(err)
@@ -413,80 +405,3 @@ func TestBisect(t *testing.T) {
}
}
}
-
-type testWriter testing.T
-
-func (t *testWriter) Write(data []byte) (int, error) {
- (*testing.T)(t).Log(string(data))
- return len(data), nil
-}
-
-func createTestRepo(t *testing.T, baseDir, name string) *testRepo {
- repo := makeTestRepo(t, filepath.Join(baseDir, name))
- repo.git("checkout", "-b", "master")
- repo.commitFileChange("master", "0")
- for _, branch := range []string{"branch1", "branch2"} {
- repo.git("checkout", "-b", branch, "master")
- repo.commitFileChange(branch, "0")
- repo.commitFileChange(branch, "1")
- }
- repo.git("checkout", "master")
- repo.commitFileChange("master", "1")
- return repo
-}
-
-type testRepo struct {
- t *testing.T
- dir string
- name string
- commits map[string]map[string]*Commit
- repo *git
-}
-
-func makeTestRepo(t *testing.T, dir string) *testRepo {
- if err := osutil.MkdirAll(dir); err != nil {
- t.Fatal(err)
- }
- ignoreCC := map[string]bool{
- "stable@vger.kernel.org": true,
- }
- repo := &testRepo{
- t: t,
- dir: dir,
- name: filepath.Base(dir),
- commits: make(map[string]map[string]*Commit),
- repo: newGit(dir, ignoreCC),
- }
- repo.git("init")
- repo.git("config", "--add", "user.email", userEmail)
- repo.git("config", "--add", "user.name", userName)
- return repo
-}
-
-func (repo *testRepo) git(args ...string) {
- if _, err := osutil.RunCmd(time.Minute, repo.dir, "git", args...); err != nil {
- repo.t.Fatal(err)
- }
-}
-
-func (repo *testRepo) commitFileChange(branch, change string) {
- id := fmt.Sprintf("%v-%v-%v", repo.name, branch, change)
- file := filepath.Join(repo.dir, "file")
- if err := osutil.WriteFile(file, []byte(id)); err != nil {
- repo.t.Fatal(err)
- }
- repo.git("add", file)
- repo.git("commit", "-m", id)
- if repo.commits[branch] == nil {
- repo.commits[branch] = make(map[string]*Commit)
- }
- com, err := repo.repo.HeadCommit()
- if err != nil {
- repo.t.Fatal(err)
- }
- repo.commits[branch][change] = com
-}
-
-func (repo *testRepo) commitChange(description string) {
- repo.git("commit", "--allow-empty", "-m", description)
-}
diff --git a/pkg/vcs/linux.go b/pkg/vcs/linux.go
index e12682277..aaca97fe7 100644
--- a/pkg/vcs/linux.go
+++ b/pkg/vcs/linux.go
@@ -31,30 +31,10 @@ func newLinux(dir string) *linux {
}
func (ctx *linux) PreviousReleaseTags(commit string) ([]string, error) {
- return ctx.previousReleaseTags(commit, false)
-}
-
-func (ctx *linux) previousReleaseTags(commit string, self bool) ([]string, error) {
- var tags []string
- if self {
- output, err := ctx.git.git("tag", "--list", "--points-at", commit, "--merged", commit, "v*.*")
- if err != nil {
- return nil, err
- }
- tags, err = gitParseReleaseTags(output)
- if err != nil {
- return nil, err
- }
- }
- output, err := ctx.git.git("tag", "--no-contains", commit, "--merged", commit, "v*.*")
- if err != nil {
- return nil, err
- }
- tags1, err := gitParseReleaseTags(output)
+ tags, err := ctx.git.previousReleaseTags(commit, false)
if err != nil {
return nil, err
}
- tags = append(tags, tags1...)
for i, tag := range tags {
if tag == "v4.0" {
// Initially we tried to stop at 3.8 because:
diff --git a/pkg/vcs/test_util.go b/pkg/vcs/test_util.go
new file mode 100644
index 000000000..55bd7bccd
--- /dev/null
+++ b/pkg/vcs/test_util.go
@@ -0,0 +1,116 @@
+package vcs
+
+import (
+ "fmt"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/google/syzkaller/pkg/osutil"
+)
+
+const (
+ userEmail = `test@syzkaller.com`
+ userName = `Test Syzkaller`
+ extractFixTagsEmail = `"syzbot" <syzbot@my.mail.com>`
+)
+
+type testWriter testing.T
+
+func (t *testWriter) Write(data []byte) (int, error) {
+ (*testing.T)(t).Log(string(data))
+ return len(data), nil
+}
+
+type TestRepo struct {
+ t *testing.T
+ Dir string
+ name string
+ Commits map[string]map[string]*Commit
+ repo *git
+}
+
+func (repo *TestRepo) git(args ...string) {
+ if _, err := osutil.RunCmd(time.Minute, repo.Dir, "git", args...); err != nil {
+ repo.t.Fatal(err)
+ }
+}
+
+func MakeTestRepo(t *testing.T, dir string) *TestRepo {
+ if err := osutil.MkdirAll(dir); err != nil {
+ t.Fatal(err)
+ }
+ ignoreCC := map[string]bool{
+ "stable@vger.kernel.org": true,
+ }
+ repo := &TestRepo{
+ t: t,
+ Dir: dir,
+ name: filepath.Base(dir),
+ Commits: make(map[string]map[string]*Commit),
+ repo: newGit(dir, ignoreCC),
+ }
+ repo.git("init")
+ repo.git("config", "--add", "user.email", userEmail)
+ repo.git("config", "--add", "user.name", userName)
+ return repo
+}
+
+func (repo *TestRepo) CommitFileChange(branch, change string) {
+ id := fmt.Sprintf("%v-%v-%v", repo.name, branch, change)
+ file := filepath.Join(repo.Dir, "file")
+ if err := osutil.WriteFile(file, []byte(id)); err != nil {
+ repo.t.Fatal(err)
+ }
+ repo.git("add", file)
+ repo.git("commit", "-m", id)
+ if repo.Commits[branch] == nil {
+ repo.Commits[branch] = make(map[string]*Commit)
+ }
+ com, err := repo.repo.HeadCommit()
+ if err != nil {
+ repo.t.Fatal(err)
+ }
+ repo.Commits[branch][change] = com
+}
+
+func (repo *TestRepo) CommitChange(description string) {
+ repo.git("commit", "--allow-empty", "-m", description)
+}
+
+func (repo *TestRepo) SetTag(tag string) {
+ repo.git("tag", tag)
+}
+
+func CreateTestRepo(t *testing.T, baseDir, name string) *TestRepo {
+ repo := MakeTestRepo(t, filepath.Join(baseDir, name))
+ repo.git("checkout", "-b", "master")
+ repo.CommitFileChange("master", "0")
+ for _, branch := range []string{"branch1", "branch2"} {
+ repo.git("checkout", "-b", branch, "master")
+ repo.CommitFileChange(branch, "0")
+ repo.CommitFileChange(branch, "1")
+ }
+ repo.git("checkout", "master")
+ repo.CommitFileChange("master", "1")
+ return repo
+}
+
+func CloneTestRepo(t *testing.T, baseDir string, name string, originRepo *TestRepo) *TestRepo {
+ dir := filepath.Join(baseDir, name)
+ if err := osutil.MkdirAll(dir); err != nil {
+ t.Fatal(err)
+ }
+ ignoreCC := map[string]bool{
+ "stable@vger.kernel.org": true,
+ }
+ repo := &TestRepo{
+ t: t,
+ Dir: dir,
+ name: filepath.Base(dir),
+ Commits: make(map[string]map[string]*Commit),
+ repo: newGit(dir, ignoreCC),
+ }
+ repo.git("clone", originRepo.Dir, repo.Dir)
+ return repo
+}
diff --git a/pkg/vcs/testos.go b/pkg/vcs/testos.go
new file mode 100644
index 000000000..f30dd0799
--- /dev/null
+++ b/pkg/vcs/testos.go
@@ -0,0 +1,23 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package vcs
+
+type testos struct {
+ *git
+}
+
+func newTestos(dir string) *testos {
+ return &testos{
+ git: newGit(dir, nil),
+ }
+}
+
+func (ctx *testos) PreviousReleaseTags(commit string) ([]string, error) {
+ return ctx.git.previousReleaseTags(commit, false)
+}
+func (ctx *testos) EnvForCommit(commit string, kernelConfig []byte) (*BisectEnv, error) {
+ return &BisectEnv{
+ Compiler: "test-compiler-dont-use",
+ }, nil
+}
diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go
index 77b919d4b..28264522f 100644
--- a/pkg/vcs/vcs.go
+++ b/pkg/vcs/vcs.go
@@ -104,6 +104,8 @@ func NewRepo(os, vm, dir string) (Repo, error) {
return newNetBSD(vm, dir), nil
case "freebsd":
return newFreeBSD(vm, dir), nil
+ case "test":
+ return newTestos(dir), nil
}
return nil, fmt.Errorf("vcs is unsupported for %v", os)
}