From 1a2f6297df2e11f3ef37e97803568cb1b9ef875b Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 5 Jul 2023 12:03:33 +0200 Subject: dashboard: order bugs in a report by their priority Display high prio bugs on top of the list and low prio bugs at the bottom. --- dashboard/app/label.go | 31 +++++++++++++- dashboard/app/reporting_lists.go | 4 ++ dashboard/app/subsystem_test.go | 93 ++++++++++++++++++++++++++++++++++++++++ dashboard/app/util_test.go | 8 +++- 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/dashboard/app/label.go b/dashboard/app/label.go index 0b911bdc3..01d360755 100644 --- a/dashboard/app/label.go +++ b/dashboard/app/label.go @@ -20,13 +20,25 @@ const ( MissingBackportLabel BugLabelType = "missing-backport" ) +type BugPrio string + +const ( + LowPrioBug BugPrio = "low" + NormalPrioBug BugPrio = "normal" + HighPrioBug BugPrio = "high" +) + type oneOf []string type subsetOf []string type trueFalse struct{} func makeLabelSet(c context.Context, ns string) *labelSet { ret := map[BugLabelType]interface{}{ - PriorityLabel: oneOf([]string{"low", "normal", "high"}), + PriorityLabel: oneOf([]string{ + string(LowPrioBug), + string(NormalPrioBug), + string(HighPrioBug), + }), NoRemindersLabel: trueFalse{}, MissingBackportLabel: trueFalse{}, } @@ -223,3 +235,20 @@ func (bug *Bug) HasUserLabel(label BugLabelType) bool { } return false } + +func (bug *Bug) prio() BugPrio { + for _, label := range bug.LabelValues(PriorityLabel) { + return BugPrio(label.Value) + } + return NormalPrioBug +} + +var bugPrioOrder = map[BugPrio]int{ + LowPrioBug: 1, + NormalPrioBug: 2, + HighPrioBug: 3, +} + +func (bp BugPrio) LessThan(other BugPrio) bool { + return bugPrioOrder[bp] < bugPrioOrder[other] +} diff --git a/dashboard/app/reporting_lists.go b/dashboard/app/reporting_lists.go index b8cc15d2b..7e9abef81 100644 --- a/dashboard/app/reporting_lists.go +++ b/dashboard/app/reporting_lists.go @@ -300,6 +300,10 @@ func querySubsystemReport(c context.Context, subsystem *Subsystem, reporting *Re }) takeBugs := append(withRepro, noRepro[:takeNoRepro]...) sort.Slice(takeBugs, func(i, j int) bool { + firstPrio, secondPrio := takeBugs[i].prio(), takeBugs[j].prio() + if firstPrio != secondPrio { + return !firstPrio.LessThan(secondPrio) + } if takeBugs[i].NumCrashes != takeBugs[j].NumCrashes { return takeBugs[i].NumCrashes > takeBugs[j].NumCrashes } diff --git a/dashboard/app/subsystem_test.go b/dashboard/app/subsystem_test.go index 806c37cf2..9eb6dde56 100644 --- a/dashboard/app/subsystem_test.go +++ b/dashboard/app/subsystem_test.go @@ -920,3 +920,96 @@ To regenerate the report, reply with: You may send multiple commands in a single email message. `, bugToExtID["WARNING: a first"], bugToExtID["WARNING: a second"])) } + +// nolint: goconst +func TestRemindersPriority(t *testing.T) { + c := NewCtx(t) + defer c.Close() + + client := c.makeClient(clientSubsystemRemind, keySubsystemRemind, true) + mailingList := config.Namespaces["subsystem-reminders"].Reporting[1].Config.(*EmailConfig).Email + build := testBuild(1) + client.UploadBuild(build) + + // WARNING: a first, low prio, has repro + aFirst := testCrash(build, 1) + aFirst.Title = `WARNING: a first` + aFirst.GuiltyFiles = []string{"a.c"} + aFirst.ReproOpts = []byte("some opts") + aFirst.ReproSyz = []byte("getpid()") + client.ReportCrash(aFirst) + sender, firstExtID := client.pollEmailAndExtID() + c.incomingEmail(sender, "#syz set prio: low\n", + EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) + c.advanceTime(time.Hour) + + // WARNING: a second, normal prio + aSecond := testCrash(build, 1) + aSecond.Title = `WARNING: a second` + aSecond.GuiltyFiles = []string{"a.c"} + client.ReportCrash(aSecond) + secondExtID := client.pollEmailExtID() + c.advanceTime(time.Hour) + + // WARNING: a third, high prio + aThird := testCrash(build, 1) + aThird.Title = `WARNING: a third` + aThird.GuiltyFiles = []string{"a.c"} + client.ReportCrash(aThird) + sender, thirdExtID := client.pollEmailAndExtID() + c.incomingEmail(sender, "#syz set prio: high\n", + EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) + c.advanceTime(time.Hour) + + // Report bugs once more to pretend they're still valid. + c.advanceTime(time.Hour * 24 * 10) + client.ReportCrash(aFirst) + client.ReportCrash(aSecond) + client.ReportCrash(aThird) + + _, err := c.GET("/cron/subsystem_reports") + c.expectOK(err) + + reply := client.pollEmailBug() + // Verify that the second bug is not present. + c.expectEQ(reply.Body, fmt.Sprintf(`Hello subsystemA maintainers/developers, + +This is a 30-day syzbot report for the subsystemA subsystem. +All related reports/information can be found at: +https://testapp.appspot.com/subsystem-reminders/s/subsystemA + +During the period, 3 new issues were detected and 0 were fixed. +In total, 3 issues are still open. + +Some of the still happening issues: + +Ref Crashes Repro Title +<1> 2 No WARNING: a third + https://testapp.appspot.com/bug?extid=%[1]v +<2> 2 No WARNING: a second + https://testapp.appspot.com/bug?extid=%[2]v +<3> 2 Yes WARNING: a first + https://testapp.appspot.com/bug?extid=%[3]v + +The report will be sent to: [subsystemA@list.com subsystemA@person.com]. + +--- +This report is generated by a bot. It may contain errors. +See https://goo.gl/tpsmEJ for more information about syzbot. +syzbot engineers can be reached at syzkaller@googlegroups.com. + +To disable reminders for individual bugs, reply with the following command: +#syz set no-reminders + +To change bug's subsystems, reply with: +#syz set subsystems: new-subsystem + +If the report looks fine to you, reply with: +#syz upstream + +To regenerate the report, reply with: +#syz regenerate + +You may send multiple commands in a single email message. +`, thirdExtID, secondExtID, firstExtID)) +} diff --git a/dashboard/app/util_test.go b/dashboard/app/util_test.go index 790984fc6..54b63e949 100644 --- a/dashboard/app/util_test.go +++ b/dashboard/app/util_test.go @@ -397,13 +397,19 @@ func (c *Ctx) pollEmailBug() *aemail.Message { } func (c *Ctx) pollEmailExtID() string { + c.t.Helper() + _, extBugID := c.pollEmailAndExtID() + return extBugID +} + +func (c *Ctx) pollEmailAndExtID() (string, string) { c.t.Helper() msg := c.pollEmailBug() _, extBugID, err := email.RemoveAddrContext(msg.Sender) if err != nil { c.t.Fatalf("failed to remove addr context: %v", err) } - return extBugID + return msg.Sender, extBugID } func (c *Ctx) expectNoEmail() { -- cgit mrf-deployment