aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dashboard/app/jobs.go43
-rw-r--r--dashboard/app/jobs_test.go31
-rw-r--r--dashboard/app/reporting_email.go12
-rw-r--r--pkg/email/parser.go23
-rw-r--r--pkg/email/parser_test.go15
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: "",
},
},
}},