Golang程序  |  199行  |  6.06 KB

// 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
}