blob: 25f0ad938fa614eee69938dacbfb778532365375 [file] [log] [blame]
// Copyright 2018 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.
// +build aetest
package dash
import (
"testing"
"time"
"github.com/google/syzkaller/dashboard/dashapi"
)
// Normal workflow:
// - upload crash -> need repro
// - upload syz repro -> still need repro
// - upload C repro -> don't need repro
func testNeedRepro1(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
c := NewCtx(t)
defer c.Close()
crash1 := crashCtor(c)
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
cid := testCrashID(crash1)
needRepro, _ := c.client.NeedRepro(cid)
c.expectEQ(needRepro, true)
// Still need repro for this crash.
resp, _ = c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
needRepro, _ = c.client.NeedRepro(cid)
c.expectEQ(needRepro, true)
crash2 := new(dashapi.Crash)
*crash2 = *crash1
crash2.ReproOpts = []byte("opts")
crash2.ReproSyz = []byte("repro syz")
resp, _ = c.client.ReportCrash(crash2)
c.expectEQ(resp.NeedRepro, true)
needRepro, _ = c.client.NeedRepro(cid)
c.expectEQ(needRepro, true)
crash2.ReproC = []byte("repro C")
resp, _ = c.client.ReportCrash(crash2)
c.expectEQ(resp.NeedRepro, false)
needRepro, _ = c.client.NeedRepro(cid)
c.expectEQ(needRepro, false)
resp, _ = c.client.ReportCrash(crash2)
c.expectEQ(resp.NeedRepro, false)
}
func TestNeedRepro1_normal(t *testing.T) { testNeedRepro1(t, normalCrash) }
func TestNeedRepro1_dup(t *testing.T) { testNeedRepro1(t, dupCrash) }
func TestNeedRepro1_closed(t *testing.T) { testNeedRepro1(t, closedCrash) }
func TestNeedRepro1_closedRepro(t *testing.T) { testNeedRepro1(t, closedWithReproCrash) }
// Upload C repro with first crash -> don't need repro.
func testNeedRepro2(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
c := NewCtx(t)
defer c.Close()
crash1 := crashCtor(c)
crash1.ReproOpts = []byte("opts")
crash1.ReproSyz = []byte("repro syz")
crash1.ReproC = []byte("repro C")
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, false)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, false)
}
func TestNeedRepro2_normal(t *testing.T) { testNeedRepro2(t, normalCrash) }
func TestNeedRepro2_dup(t *testing.T) { testNeedRepro2(t, dupCrash) }
func TestNeedRepro2_closed(t *testing.T) { testNeedRepro2(t, closedCrash) }
func TestNeedRepro2_closedRepro(t *testing.T) { testNeedRepro2(t, closedWithReproCrash) }
// Test that after uploading 5 failed repros, app stops requesting repros.
func testNeedRepro3(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
c := NewCtx(t)
defer c.Close()
crash1 := crashCtor(c)
for i := 0; i < maxReproPerBug; i++ {
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, true)
c.client.ReportFailedRepro(testCrashID(crash1))
}
for i := 0; i < 3; i++ {
// No more repros today.
c.advanceTime(time.Hour)
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, false)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, false)
// Then another repro after a day.
c.advanceTime(25 * time.Hour)
for j := 0; j < 2; j++ {
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, true)
}
c.client.ReportFailedRepro(testCrashID(crash1))
}
}
func TestNeedRepro3_normal(t *testing.T) { testNeedRepro3(t, normalCrash) }
func TestNeedRepro3_dup(t *testing.T) { testNeedRepro3(t, dupCrash) }
func TestNeedRepro3_closed(t *testing.T) { testNeedRepro3(t, closedCrash) }
func TestNeedRepro3_closedRepro(t *testing.T) { testNeedRepro3(t, closedWithReproCrash) }
// Test that after uploading 5 syz repros, app stops requesting repros.
func testNeedRepro4(t *testing.T, crashCtor func(c *Ctx) *dashapi.Crash) {
c := NewCtx(t)
defer c.Close()
crash1 := crashCtor(c)
crash1.ReproOpts = []byte("opts")
crash1.ReproSyz = []byte("repro syz")
for i := 0; i < maxReproPerBug-1; i++ {
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, true)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, true)
}
resp, _ := c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, false)
needRepro, _ := c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, false)
// No more repros even after a day.
c.advanceTime(25 * time.Hour)
crash1.ReproOpts = nil
crash1.ReproSyz = nil
resp, _ = c.client.ReportCrash(crash1)
c.expectEQ(resp.NeedRepro, false)
needRepro, _ = c.client.NeedRepro(testCrashID(crash1))
c.expectEQ(needRepro, false)
}
func TestNeedRepro4_normal(t *testing.T) { testNeedRepro4(t, normalCrash) }
func TestNeedRepro4_dup(t *testing.T) { testNeedRepro4(t, dupCrash) }
func TestNeedRepro4_closed(t *testing.T) { testNeedRepro4(t, closedCrash) }
func TestNeedRepro4_closedRepro(t *testing.T) { testNeedRepro4(t, closedWithReproCrash) }
func normalCrash(c *Ctx) *dashapi.Crash {
build := testBuild(1)
c.client.UploadBuild(build)
return testCrash(build, 1)
}
func dupCrash(c *Ctx) *dashapi.Crash {
build := testBuild(1)
c.client.UploadBuild(build)
c.client.ReportCrash(testCrash(build, 1))
crash2 := testCrash(build, 2)
c.client.ReportCrash(crash2)
reports := c.client.pollBugs(2)
c.client.updateBug(reports[1].ID, dashapi.BugStatusDup, reports[0].ID)
return crash2
}
func closedCrash(c *Ctx) *dashapi.Crash {
return closedCrashImpl(c, false)
}
func closedWithReproCrash(c *Ctx) *dashapi.Crash {
return closedCrashImpl(c, true)
}
func closedCrashImpl(c *Ctx, withRepro bool) *dashapi.Crash {
build := testBuild(1)
c.client.UploadBuild(build)
crash := testCrash(build, 1)
if withRepro {
crash.ReproC = []byte("repro C")
}
resp, _ := c.client.ReportCrash(crash)
c.expectEQ(resp.NeedRepro, !withRepro)
rep := c.client.pollBug()
c.client.updateBug(rep.ID, dashapi.BugStatusInvalid, "")
crash.ReproC = nil
return crash
}