aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/manager/diff/manager_test.go
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2026-01-25 21:01:16 +0000
committerAleksandr Nogikh <nogikh@google.com>2026-01-26 15:43:37 +0000
commit30c4bb8427fe0d12bfff0fea81bf31d04910129e (patch)
tree6ff01bffdf8f23a3e76a1218a4b4af235e137f29 /pkg/manager/diff/manager_test.go
parentdd170fcec9a15df2760e4cd84d22aab51ef0d172 (diff)
pkg/manager/diff: add tests
Refactor the package to support testing and add a number of basic tests.
Diffstat (limited to 'pkg/manager/diff/manager_test.go')
-rw-r--r--pkg/manager/diff/manager_test.go188
1 files changed, 188 insertions, 0 deletions
diff --git a/pkg/manager/diff/manager_test.go b/pkg/manager/diff/manager_test.go
index 7769fc8fd..89a35c794 100644
--- a/pkg/manager/diff/manager_test.go
+++ b/pkg/manager/diff/manager_test.go
@@ -4,11 +4,20 @@
package diff
import (
+ "context"
+ "errors"
"testing"
+ "time"
+ "github.com/google/syzkaller/pkg/manager"
+ "github.com/google/syzkaller/pkg/report"
+ "github.com/google/syzkaller/pkg/repro"
+ _ "github.com/google/syzkaller/prog/test"
"github.com/stretchr/testify/assert"
)
+const testTimeout = 15 * time.Second
+
func TestNeedReproForTitle(t *testing.T) {
for title, skip := range map[string]bool{
"no output from test machine": false,
@@ -21,3 +30,182 @@ func TestNeedReproForTitle(t *testing.T) {
assert.Equal(t, skip, needReproForTitle(title), "title=%q", title)
}
}
+
+func TestDiffBaseCrashInterception(t *testing.T) {
+ env := newTestEnv(t, nil)
+ defer env.close()
+ env.start()
+
+ env.base.CrashesCh <- &report.Report{Title: "base_crash"}
+
+ select {
+ case title := <-env.diffCtx.cfg.BaseCrashes:
+ assert.Equal(t, "base_crash", title)
+ case <-time.After(testTimeout):
+ t.Error("expected base crash")
+ }
+}
+
+func TestDiffExternalIgnore(t *testing.T) {
+ // Config ignores crash -> Patched crash ignored -> No repro.
+ runReproCalled := false
+ mockRunRepro := mockReproCallback("important_crash", nil, func() {
+ runReproCalled = true
+ })
+
+ env := newTestEnv(t, &Config{
+ IgnoreCrash: func(ctx context.Context, title string) (bool, error) {
+ if title == "ignored_crash" {
+ return true, nil
+ }
+ return false, nil
+ },
+ runRepro: mockRunRepro,
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ env.new.CrashesCh <- &report.Report{Title: "ignored_crash", Report: []byte("log")}
+ env.waitForStatus("ignored_crash", manager.DiffBugStatusIgnored)
+ assert.False(t, runReproCalled, "should not repro ignored crash")
+
+ env.new.CrashesCh <- &report.Report{Title: "important_crash", Report: []byte("log")}
+ env.waitForStatus("important_crash", manager.DiffBugStatusVerifying)
+}
+
+func TestDiffSuccess(t *testing.T) {
+ // Patched kernel crashes -> Repro succeeds -> Base kernel does NOT crash -> PatchedOnly reported.
+
+ // Mock Runner (Repro on Base).
+ env := newTestEnv(t, &Config{
+ runRepro: mockRepro("crash_title", nil),
+ runner: newMockRunner(func(ctx context.Context, k Kernel, r *repro.Result, fullRepro bool) *reproRunnerResult {
+ // Simulate successful run on base without crash.
+ return &reproRunnerResult{
+ reproReport: r.Report,
+ repro: r,
+ crashReport: nil, // No crash on base.
+ fullRepro: true,
+ }
+ }),
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ select {
+ case bug := <-env.diffCtx.patchedOnly:
+ assert.Equal(t, "crash_title", bug.Report.Title)
+ case <-time.After(testTimeout):
+ t.Fatal("expected patched only report")
+ }
+}
+
+func TestDiffFailNoRepro(t *testing.T) {
+ // Patched kernel crashes -> Repro fails -> No report.
+ env := newTestEnv(t, &Config{
+ runRepro: mockRepro("", errors.New("repro failed")),
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ env.waitForStatus("crash_title", manager.DiffBugStatusCompleted)
+}
+
+func TestDiffFailBaseCrash(t *testing.T) {
+ // Patched kernel crashes -> Repro succeeds -> Base also crashes -> No PatchedOnly report.
+ env := newTestEnv(t, &Config{
+ runRepro: mockRepro("crash_title", nil),
+ runner: newMockRunner(func(ctx context.Context, k Kernel, r *repro.Result, fullRepro bool) *reproRunnerResult {
+ return &reproRunnerResult{
+ reproReport: r.Report,
+ repro: r,
+ crashReport: &report.Report{Title: "crash_title"}, // Base crashed.
+ }
+ }),
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+
+ select {
+ case <-env.diffCtx.patchedOnly:
+ t.Fatal("unexpected patched only report")
+ case <-env.diffCtx.cfg.BaseCrashes: // Should report to BaseCrashes.
+ // Expected.
+ case <-time.After(testTimeout):
+ t.Fatal("expected base crash report")
+ }
+
+ env.waitForStatus("crash_title", manager.DiffBugStatusCompleted)
+}
+
+func TestDiffFailBaseCrashEarly(t *testing.T) {
+ // Base crashes first -> Patched crashes same title -> No reproduction attempt.
+ runReproCalled := false
+ mockRunRepro := mockReproCallback("crash_title", nil, func() {
+ runReproCalled = true
+ })
+
+ env := newTestEnv(t, &Config{
+ runRepro: mockRunRepro,
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ env.base.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ select {
+ case <-env.diffCtx.cfg.BaseCrashes:
+ case <-time.After(testTimeout):
+ t.Fatal("expected base crash")
+ }
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ env.waitForStatus("crash_title", manager.DiffBugStatusIgnored)
+ assert.False(t, runReproCalled, "WaitRepro should not be called")
+}
+
+func TestDiffRetryRepro(t *testing.T) {
+ // Patched kernel crashes -> Repro fails -> Retried until max attempts.
+ reproCalled := make(chan struct{}, 10)
+ mockRunRepro := mockReproCallback("", errors.New("repro failed"), func() {
+ reproCalled <- struct{}{}
+ })
+
+ env := newTestEnv(t, &Config{
+ runRepro: mockRunRepro,
+ })
+ defer env.close()
+
+ env.new.FinishCorpusTriage()
+ env.start()
+
+ for i := 0; i <= maxReproAttempts; i++ {
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ select {
+ case <-reproCalled:
+ case <-time.After(testTimeout):
+ t.Fatalf("iteration %d: expected repro", i)
+ }
+ env.waitForStatus("crash_title", manager.DiffBugStatusCompleted)
+ }
+ // Inject one more crash, which should be ignored.
+ env.new.CrashesCh <- &report.Report{Title: "crash_title", Report: []byte("log")}
+ env.waitForStatus("crash_title", manager.DiffBugStatusIgnored)
+ select {
+ case <-reproCalled:
+ t.Fatalf("unexpected repro")
+ default:
+ }
+}