blob: b0b0c908b3dabd358d7d7212ba0a73cf690318f0 [file] [log] [blame]
// Copyright 2017 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 (
"fmt"
"testing"
"time"
"github.com/google/syzkaller/dashboard/dashapi"
)
func TestReportBug(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
crash1 := &dashapi.Crash{
BuildID: "build1",
Title: "title1",
Maintainers: []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`},
Log: []byte("log1"),
Report: []byte("report1"),
}
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
// Must get no reports for "unknown" type.
pr := &dashapi.PollRequest{
Type: "unknown",
}
resp := new(dashapi.PollResponse)
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 0)
// Must get a proper report for "test" type.
pr.Type = "test"
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 1)
rep := resp.Reports[0]
if rep.ID == "" {
t.Fatalf("empty report ID")
}
want := &dashapi.BugReport{
Namespace: "test1",
Config: []byte(`{"Index":1}`),
ID: rep.ID,
First: true,
Title: "title1",
Maintainers: []string{"bar@foo.com", "foo@bar.com"},
CompilerID: "compiler1",
KernelRepo: "repo1",
KernelBranch: "branch1",
KernelCommit: "kernel_commit1",
KernelConfig: []byte("config1"),
Log: []byte("log1"),
Report: []byte("report1"),
}
c.expectEQ(rep, want)
// Since we did not update bug status yet, should get the same report again.
reports := reportAllBugs(c, 1)
c.expectEQ(reports[0], want)
// Now add syz repro and check that we get another bug report.
crash1.ReproOpts = []byte("some opts")
crash1.ReproSyz = []byte("getpid()")
want.First = false
want.ReproSyz = []byte("#some opts\ngetpid()")
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
reports = reportAllBugs(c, 1)
c.expectEQ(reports[0], want)
cmd := &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusOpen,
ReproLevel: dashapi.ReproLevelSyz,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// After bug update should not get the report again.
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 0)
// Now close the bug in the first reporting.
cmd = &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusUpstream,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Check that bug updates for the first reporting fail now.
cmd = &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusOpen,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, false)
// Check that we get the report in the second reporting.
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 1)
rep2 := resp.Reports[0]
if rep2.ID == "" || rep2.ID == rep.ID {
t.Fatalf("bad report ID: %q", rep2.ID)
}
want.ID = rep2.ID
want.First = true
want.Config = []byte(`{"Index":2}`)
c.expectEQ(rep2, want)
// Check that that we can't upstream the bug in the final reporting.
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusUpstream,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, false)
}
func TestInvalidBug(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
crash1 := testCrash(build, 1)
crash1.ReproC = []byte("int main() {}")
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
pr := &dashapi.PollRequest{
Type: "test",
}
resp := new(dashapi.PollResponse)
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 1)
rep := resp.Reports[0]
c.expectEQ(rep.Title, "title1")
cmd := &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusOpen,
ReproLevel: dashapi.ReproLevelC,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Mark the bug as invalid.
cmd = &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusInvalid,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Now it should not be reported in either reporting.
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 0)
// Now a similar crash happens again.
crash2 := &dashapi.Crash{
BuildID: "build1",
Title: "title1",
Log: []byte("log2"),
Report: []byte("report2"),
ReproC: []byte("int main() { return 1; }"),
}
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
// Now it should be reported again.
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 1)
rep = resp.Reports[0]
if rep.ID == "" {
t.Fatalf("empty report ID")
}
want := &dashapi.BugReport{
Namespace: "test1",
Config: []byte(`{"Index":1}`),
ID: rep.ID,
First: true,
Title: "title1 (2)",
CompilerID: "compiler1",
KernelRepo: "repo1",
KernelBranch: "branch1",
KernelCommit: "kernel_commit1",
KernelConfig: []byte("config1"),
Log: []byte("log2"),
Report: []byte("report2"),
ReproC: []byte("int main() { return 1; }"),
}
c.expectEQ(rep, want)
cid := &dashapi.CrashID{
BuildID: build.ID,
Title: crash1.Title,
}
c.expectOK(c.API(client1, key1, "report_failed_repro", cid, nil))
}
func TestReportingQuota(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
const numReports = 8 // quota is 3 per day
for i := 0; i < numReports; i++ {
crash := &dashapi.Crash{
BuildID: "build1",
Title: fmt.Sprintf("title%v", i),
Log: []byte(fmt.Sprintf("log%v", i)),
Report: []byte(fmt.Sprintf("report%v", i)),
}
c.expectOK(c.API(client1, key1, "report_crash", crash, nil))
}
for _, reports := range []int{3, 3, 2, 0, 0} {
c.advanceTime(24 * time.Hour)
pr := &dashapi.PollRequest{
Type: "test",
}
resp := new(dashapi.PollResponse)
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), reports)
for _, rep := range resp.Reports {
cmd := &dashapi.BugUpdate{
ID: rep.ID,
Status: dashapi.BugStatusOpen,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
}
// Out of quota for today, so must get 0 reports.
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 0)
}
}
// Basic dup scenario: mark one bug as dup of another.
func TestReportingDup(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
crash1 := testCrash(build, 1)
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
crash2 := testCrash(build, 2)
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
pr := &dashapi.PollRequest{
Type: "test",
}
resp := new(dashapi.PollResponse)
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 2)
rep1 := resp.Reports[0]
cmd := &dashapi.BugUpdate{
ID: rep1.ID,
Status: dashapi.BugStatusOpen,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
rep2 := resp.Reports[1]
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusOpen,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Dup.
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusDup,
DupOf: rep1.ID,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Undup.
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusOpen,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Dup again.
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusDup,
DupOf: rep1.ID,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
// Dup crash happens again, new bug must not be created.
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 0)
// Now close the original bug, and check that new bugs for dup are now created.
cmd = &dashapi.BugUpdate{
ID: rep1.ID,
Status: dashapi.BugStatusInvalid,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
c.expectOK(c.API(client1, key1, "reporting_poll", pr, resp))
c.expectEQ(len(resp.Reports), 1)
c.expectEQ(resp.Reports[0].Title, crash2.Title+" (2)")
// Unduping after the canonical bugs was closed must not work
// (we already created new bug for this report).
cmd = &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusOpen,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, false)
}
// Dup bug onto a closed bug.
// A new crash report must create a new bug.
func TestReportingDupToClosed(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
crash1 := testCrash(build, 1)
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
crash2 := testCrash(build, 2)
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
reports := reportAllBugs(c, 2)
cmd := &dashapi.BugUpdate{
ID: reports[0].ID,
Status: dashapi.BugStatusInvalid,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
cmd = &dashapi.BugUpdate{
ID: reports[1].ID,
Status: dashapi.BugStatusDup,
DupOf: reports[0].ID,
}
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
reports2 := reportAllBugs(c, 1)
c.expectEQ(reports2[0].Title, crash2.Title+" (2)")
}
// Test that marking dups across reporting levels is not permitted.
func TestReportingDupCrossReporting(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client1, key1, "upload_build", build, nil))
crash1 := testCrash(build, 1)
c.expectOK(c.API(client1, key1, "report_crash", crash1, nil))
crash2 := testCrash(build, 2)
c.expectOK(c.API(client1, key1, "report_crash", crash2, nil))
reports := reportAllBugs(c, 2)
rep1 := reports[0]
rep2 := reports[1]
// Upstream second bug.
cmd := &dashapi.BugUpdate{
ID: rep2.ID,
Status: dashapi.BugStatusUpstream,
}
reply := new(dashapi.BugUpdateReply)
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, true)
reports = reportAllBugs(c, 1)
rep3 := reports[0]
// Duping must fail all ways.
cmds := []*dashapi.BugUpdate{
&dashapi.BugUpdate{ID: rep1.ID, DupOf: rep1.ID},
&dashapi.BugUpdate{ID: rep1.ID, DupOf: rep2.ID},
&dashapi.BugUpdate{ID: rep1.ID, DupOf: rep3.ID},
&dashapi.BugUpdate{ID: rep2.ID, DupOf: rep1.ID},
&dashapi.BugUpdate{ID: rep2.ID, DupOf: rep2.ID},
&dashapi.BugUpdate{ID: rep2.ID, DupOf: rep3.ID},
&dashapi.BugUpdate{ID: rep3.ID, DupOf: rep1.ID},
&dashapi.BugUpdate{ID: rep3.ID, DupOf: rep2.ID},
&dashapi.BugUpdate{ID: rep3.ID, DupOf: rep3.ID},
}
for _, cmd := range cmds {
t.Logf("duping %v -> %v", cmd.ID, cmd.DupOf)
cmd.Status = dashapi.BugStatusDup
c.expectOK(c.API(client1, key1, "reporting_update", cmd, reply))
c.expectEQ(reply.OK, false)
}
}