diff options
| -rw-r--r-- | dashboard/app/jobs.go | 43 | ||||
| -rw-r--r-- | dashboard/app/jobs_test.go | 31 | ||||
| -rw-r--r-- | dashboard/app/reporting_email.go | 12 | ||||
| -rw-r--r-- | pkg/email/parser.go | 23 | ||||
| -rw-r--r-- | pkg/email/parser_test.go | 15 |
5 files changed, 90 insertions, 34 deletions
diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go index e18c9838b..187b5aae3 100644 --- a/dashboard/app/jobs.go +++ b/dashboard/app/jobs.go @@ -98,8 +98,10 @@ type testJobArgs struct { func addTestJob(c context.Context, args *testJobArgs) (*Job, *db.Key, error) { now := timeNow(c) - if reason := checkTestJob(c, args.bug, args.bugReporting, args.crash, - args.repo, args.branch); reason != "" { + if err := patchTestJobArgs(c, args); err != nil { + return nil, nil, err + } + if reason := checkTestJob(args); reason != "" { return nil, nil, &BadTestRequestError{reason} } manager, mgrConfig := activeManager(args.crash.Manager, args.bug.Namespace) @@ -208,24 +210,37 @@ func saveJob(c context.Context, job *Job, bugKey *db.Key) (*db.Key, error) { CrashReference{CrashReferenceJob, extJobID(jobKey), timeNow(c)}) } -func checkTestJob(c context.Context, bug *Bug, bugReporting *BugReporting, crash *Crash, - repo, branch string) string { +func patchTestJobArgs(c context.Context, args *testJobArgs) error { + if args.branch == "" && args.repo == "" { + // If no arguments were passed, we need to auto-guess them. + build, err := loadBuild(c, args.bug.Namespace, args.crash.BuildID) + if err != nil { + return fmt.Errorf("failed to find the bug reporting object: %w", err) + } + args.branch = build.KernelBranch + args.repo = build.KernelRepo + } + return nil +} + +func checkTestJob(args *testJobArgs) string { + crash, bug := args.crash, args.bug needRepro := !strings.Contains(crash.Title, "boot error:") && !strings.Contains(crash.Title, "test error:") && !strings.Contains(crash.Title, "build error") switch { case needRepro && crash.ReproC == 0 && crash.ReproSyz == 0: return "This crash does not have a reproducer. I cannot test it." - case !vcs.CheckRepoAddress(repo): - return fmt.Sprintf("%q does not look like a valid git repo address.", repo) - case !vcs.CheckBranch(branch) && !vcs.CheckCommitHash(branch): - return fmt.Sprintf("%q does not look like a valid git branch or commit.", branch) + case !vcs.CheckRepoAddress(args.repo): + return fmt.Sprintf("%q does not look like a valid git repo address.", args.repo) + case !vcs.CheckBranch(args.branch) && !vcs.CheckCommitHash(args.branch): + return fmt.Sprintf("%q does not look like a valid git branch or commit.", args.branch) case bug.Status == BugStatusFixed: return "This bug is already marked as fixed. No point in testing." case bug.Status == BugStatusInvalid: return "This bug is already marked as invalid. No point in testing." // TODO(dvyukov): for BugStatusDup check status of the canonical bug. - case bugReporting != nil && !bugReporting.Closed.IsZero(): + case args.bugReporting != nil && !args.bugReporting.Closed.IsZero(): return "This bug is already upstreamed. Please test upstream." } return "" @@ -1398,16 +1413,6 @@ func handleExternalTestRequest(c context.Context, req *dashapi.TestPatchRequest) if err != nil { return fmt.Errorf("failed to find a crash: %w", err) } - if req.Branch == "" && req.Repo == "" { - build, err := loadBuild(c, bug.Namespace, crash.BuildID) - if err != nil { - return fmt.Errorf("failed to find the bug reporting object: %w", err) - } - req.Branch = build.KernelBranch - req.Repo = build.KernelRepo - } else if req.Branch == "" || req.Repo == "" { - return fmt.Errorf("branch and repo should be either both set or both empty") - } _, _, err = addTestJob(c, &testJobArgs{ crash: crash, crashKey: crashKey, diff --git a/dashboard/app/jobs_test.go b/dashboard/app/jobs_test.go index 180b27149..3327d46db 100644 --- a/dashboard/app/jobs_test.go +++ b/dashboard/app/jobs_test.go @@ -64,13 +64,13 @@ func TestJob(t *testing.T) { c.incomingEmail(sender, "#syz test: repo", EmailOptFrom("test@requester.com"), EmailOptSubject("my-subject"), EmailOptCC([]string{mailingList})) msg := c.pollEmailBug() - c.expectEQ(strings.Contains(msg.Body, "want 2 args"), true) + c.expectEQ(strings.Contains(msg.Body, "want either no args or 2 args"), true) c.expectEQ(msg.Subject, "Re: my-subject") c.incomingEmail(sender, "#syz test: repo branch commit", EmailOptFrom("test@requester.com"), EmailOptSubject("Re: my-subject"), EmailOptCC([]string{mailingList})) msg = c.pollEmailBug() - c.expectEQ(strings.Contains(msg.Body, "want 2 args"), true) + c.expectEQ(strings.Contains(msg.Body, "want either no args or 2 args"), true) c.expectEQ(msg.Subject, "Re: my-subject") c.incomingEmail(sender, "#syz test: repo branch", @@ -1236,3 +1236,30 @@ func TestJobCauseRetry(t *testing.T) { msg := c.pollEmailBug() c.expectTrue(strings.Contains(msg.Body, "syzbot has bisected this issue to:")) } + +// Test that we accept `#syz test` commands without arguments. +func TestEmailTestCommandNoArgs(t *testing.T) { + c := NewCtx(t) + defer c.Close() + + client := c.publicClient + build := testBuild(1) + build.KernelRepo = "git://git.git/git.git" + build.KernelBranch = "kernel-branch" + client.UploadBuild(build) + + crash := testCrashWithRepro(build, 2) + client.ReportCrash(crash) + + sender := c.pollEmailBug().Sender + mailingList := config.Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + + c.incomingEmail(sender, "#syz test\n"+sampleGitPatch, + EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList})) + c.expectNoEmail() + pollResp := client.pollJobs(build.Manager) + c.expectEQ(pollResp.Type, dashapi.JobTestPatch) + c.expectEQ(pollResp.KernelRepo, build.KernelRepo) + c.expectEQ(pollResp.KernelBranch, build.KernelBranch) + c.expectEQ(pollResp.Patch, []byte(sampleGitPatch)) +} diff --git a/dashboard/app/reporting_email.go b/dashboard/app/reporting_email.go index cb8b95e4e..17c8efcec 100644 --- a/dashboard/app/reporting_email.go +++ b/dashboard/app/reporting_email.go @@ -700,9 +700,13 @@ var emailCmdToStatus = map[email.Command]dashapi.BugStatus{ func handleTestCommand(c context.Context, info *bugInfoResult, msg *email.Email, command *email.SingleCommand) string { - args := strings.Split(command.Args, " ") - if len(args) != 2 { - return fmt.Sprintf("want 2 args (repo, branch), got %v", len(args)) + args := strings.Fields(command.Args) + if len(args) != 0 && len(args) != 2 { + return fmt.Sprintf("want either no args or 2 args (repo, branch), got %v", len(args)) + } + repo, branch := "", "" + if len(args) == 2 { + repo, branch = args[0], args[1] } if info.bug.sanitizeAccess(AccessPublic) != AccessPublic { log.Warningf(c, "%v: bug is not AccessPublic, patch testing request is denied", info.bug.Title) @@ -712,7 +716,7 @@ func handleTestCommand(c context.Context, info *bugInfoResult, err := handleTestRequest(c, &testReqArgs{ bug: info.bug, bugKey: info.bugKey, bugReporting: info.bugReporting, user: msg.Author, extID: msg.MessageID, link: msg.Link, - patch: []byte(msg.Patch), repo: args[0], branch: args[1], jobCC: msg.Cc}) + patch: []byte(msg.Patch), repo: repo, branch: branch, jobCC: msg.Cc}) if err != nil { var testDenied *TestRequestDeniedError var badTest *BadTestRequestError diff --git a/pkg/email/parser.go b/pkg/email/parser.go index 2ed9495fd..efc63391b 100644 --- a/pkg/email/parser.go +++ b/pkg/email/parser.go @@ -296,13 +296,21 @@ func extractCommand(body string) (*SingleCommand, int) { // For "fix:"/"dup:" we need a whole non-empty line of text. switch cmd { case CmdTest: - args = extractArgsTokens(body[cmdPos+cmdEnd:], 2) + if strings.HasSuffix(str, ":") { + // For "#syz test:", we do want to query 2 arguments. + args = extractArgsTokens(body[cmdPos+cmdEnd:], 2) + } else { + // For "#syz test", it's likely there won't be anything else, so let's only parse + // the first line. + fmt.Printf("line: %s", body[cmdPos+cmdEnd:]) + args = extractArgsLine(body[cmdPos+cmdEnd:], false) + } case CmdSet, CmdUnset: - args = extractArgsLine(body[cmdPos+cmdEnd:]) + args = extractArgsLine(body[cmdPos+cmdEnd:], true) case cmdTest5: args = extractArgsTokens(body[cmdPos+cmdEnd:], 5) case CmdFix, CmdDup: - args = extractArgsLine(body[cmdPos+cmdEnd:]) + args = extractArgsLine(body[cmdPos+cmdEnd:], true) } return &SingleCommand{ Command: cmd, @@ -365,11 +373,12 @@ func extractArgsTokens(body string, num int) string { return strings.TrimSpace(strings.Join(args, " ")) } -func extractArgsLine(body string) string { +func extractArgsLine(body string, skipWs bool) string { pos := 0 - for pos < len(body) && (body[pos] == ' ' || body[pos] == '\t' || - body[pos] == '\n' || body[pos] == '\r') { - pos++ + if skipWs { + for pos < len(body) && unicode.IsSpace(rune(body[pos])) { + pos++ + } } lineEnd := strings.IndexByte(body[pos:], '\n') if lineEnd == -1 { diff --git a/pkg/email/parser_test.go b/pkg/email/parser_test.go index 0d3c54c87..239b795a3 100644 --- a/pkg/email/parser_test.go +++ b/pkg/email/parser_test.go @@ -217,7 +217,8 @@ git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core cmd: &SingleCommand{ Command: CmdTest, Str: "test", - Args: "git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core", + // We only look for arguments if there's ":" after "#syz test". + Args: "", }, }, { @@ -250,6 +251,16 @@ locking/core }, }, { + body: `#syz test +patch-begins +`, + cmd: &SingleCommand{ + Command: CmdTest, + Str: "test", + Args: "", + }, + }, + { body: ` #syz test_5_arg_cmd arg1 @@ -696,7 +707,7 @@ index 3d85747bd86e..a257b872a53d 100644 { Command: CmdTest, Str: "test", - Args: "commit 59372bbf3abd5b24a7f6f676a3968685c280f955", + Args: "", }, }, }}, |
