| package internal |
| |
| import ( |
| "testing" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/mock" |
| "github.com/stretchr/testify/require" |
| "go.temporal.io/sdk/testsuite" |
| "go.temporal.io/sdk/workflow" |
| |
| "go.skia.org/infra/pinpoint/go/common" |
| "go.skia.org/infra/pinpoint/go/workflows" |
| pb "go.skia.org/infra/pinpoint/proto/v1" |
| ) |
| |
| func generateSingleCommitRuns(hash string, count int) *CommitRun { |
| return &CommitRun{ |
| Build: &workflows.Build{ |
| BuildParams: workflows.BuildParams{ |
| Commit: common.NewCombinedCommit(common.NewChromiumCommit(hash)), |
| }, |
| }, |
| Runs: make([]*workflows.TestRun, count), |
| } |
| } |
| |
| func makeDefaultBisectParams() workflows.BisectParams { |
| return workflows.BisectParams{Request: &pb.ScheduleBisectRequest{}} |
| } |
| |
| func totalRunsOf(t *testing.T, br *BisectRun) int32 { |
| tr, err := br.totalRuns() |
| require.NoError(t, err) |
| return tr |
| } |
| |
| func nextRunSizeOf(t *testing.T, br1, br2 *BisectRun, minSampleSize int32) int32 { |
| res, err := nextRunSize(br1, br2, minSampleSize) |
| require.NoError(t, err) |
| return res |
| } |
| |
| func TestBisectRun_ScheduleZeroOrLessRun_ReturnNil(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| bp := makeDefaultBisectParams() |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun, nil) |
| |
| br := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit("fake-hash"))) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| f, err := br.scheduleRuns(ctx, jobID, &bp, 0) |
| require.Nil(t, f) |
| require.NoError(t, err) |
| require.EqualValues(t, 0, br.totalScheduledRuns()) |
| require.EqualValues(t, 0, totalRunsOf(t, br)) |
| require.Empty(t, br.Runs) |
| |
| f, err = br.scheduleRuns(ctx, jobID, &bp, -1) |
| require.Nil(t, f) |
| require.NoError(t, err) |
| require.NoError(t, br.updateRuns(ctx, f), "update with nil runs should be no-op") |
| |
| require.EqualValues(t, 0, br.totalScheduledRuns()) |
| require.EqualValues(t, 0, totalRunsOf(t, br)) |
| require.Empty(t, br.Runs) |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| } |
| |
| func TestBisectRun_TotalRuns_WithScheduled_ReturnTotal(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| bp := makeDefaultBisectParams() |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun) |
| |
| br := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit("fake-hash"))) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| require.Empty(t, br.Runs) |
| require.EqualValues(t, 0, totalRunsOf(t, br)) |
| |
| f, err := br.scheduleRuns(ctx, jobID, &bp, 10) |
| require.NoError(t, err) |
| require.NotNil(t, f) |
| require.EqualValues(t, 10, totalRunsOf(t, br)) |
| |
| mf, err := br.scheduleRuns(ctx, jobID, &bp, 20) |
| require.EqualValues(t, 30, totalRunsOf(t, br)) |
| require.NotNil(t, mf) |
| require.NoError(t, err) |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| } |
| |
| func TestBisectRun_ScheduleAndUpdate(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| bp := makeDefaultBisectParams() |
| |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun, nil) |
| |
| br := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit("fake-hash"))) |
| const ( |
| firstRuns = 10 |
| secondRuns = 5 |
| thirdRuns = 15 |
| ) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| firstFuture, err := br.scheduleRuns(ctx, jobID, &bp, firstRuns) |
| require.NotNil(t, firstFuture) |
| require.NoError(t, err) |
| |
| secondFuture, err := br.scheduleRuns(ctx, jobID, &bp, secondRuns) |
| require.EqualValues(t, firstRuns+secondRuns, totalRunsOf(t, br)) |
| require.EqualValues(t, firstRuns+secondRuns, br.totalScheduledRuns()) |
| require.NotNil(t, secondFuture) |
| require.NoError(t, err) |
| |
| require.NoError(t, br.updateRuns(ctx, secondFuture)) |
| require.Len(t, br.Runs, secondRuns) |
| require.EqualValues(t, firstRuns+secondRuns, totalRunsOf(t, br)) |
| require.EqualValues(t, firstRuns, br.totalScheduledRuns()) |
| |
| require.NoError(t, br.updateRuns(ctx, firstFuture)) |
| require.EqualValues(t, 0, br.totalScheduledRuns()) |
| require.EqualValues(t, firstRuns+secondRuns, totalRunsOf(t, br)) |
| require.Len(t, br.Runs, firstRuns+secondRuns) |
| |
| thirdFuture, err := br.scheduleRuns(ctx, jobID, &bp, thirdRuns) |
| require.EqualValues(t, thirdRuns, br.totalScheduledRuns()) |
| require.EqualValues(t, firstRuns+secondRuns+thirdRuns, totalRunsOf(t, br)) |
| require.NotNil(t, thirdFuture) |
| require.NoError(t, err) |
| require.NoError(t, br.updateRuns(ctx, thirdFuture)) |
| require.Len(t, br.Runs, firstRuns+secondRuns+thirdRuns) |
| require.EqualValues(t, firstRuns+secondRuns+thirdRuns, totalRunsOf(t, br)) |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| require.Len(t, br.Runs, firstRuns+secondRuns+thirdRuns) |
| } |
| |
| func TestBisectRun_WithIncompleteRun_ReturnRuns(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const hash1 = "fake-ffcaaab85ecf1a896da1d635aeca929edbe" |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| const ( |
| partialComplete = 8 |
| schedule = 10 |
| ) |
| bp := makeDefaultBisectParams() |
| |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(generateSingleCommitRuns(hash1, partialComplete), nil) |
| |
| br := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit(hash1))) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| f1, err := br.scheduleRuns(ctx, jobID, &bp, schedule) |
| require.NoError(t, err) |
| |
| f2, err := br.scheduleRuns(ctx, jobID, &bp, schedule) |
| require.NotNil(t, f2) |
| require.NoError(t, err) |
| |
| require.NoError(t, br.updateRuns(ctx, f1)) |
| require.EqualValues(t, schedule, br.totalScheduledRuns(), "partial completed runs should remove the initial scheduled runs.") |
| require.EqualValues(t, schedule+partialComplete, totalRunsOf(t, br), "should only update with partially completed runs.") |
| |
| require.NoError(t, br.updateRuns(ctx, f2)) |
| require.EqualValues(t, 0, br.totalScheduledRuns()) |
| require.EqualValues(t, partialComplete+partialComplete, totalRunsOf(t, br), "should only update with partially completed runs.") |
| require.Len(t, br.Runs, partialComplete+partialComplete) |
| |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| require.Len(t, br.Runs, partialComplete+partialComplete) |
| } |
| |
| func TestBisectRun_UnmatchedCommit_ShouldError(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| bp := makeDefaultBisectParams() |
| |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun, nil) |
| |
| const hash1 = "fake-ffcaaab85ecf1a896da1d635aeca929edbe" |
| const other = "other-e0c1a4e8cae6103adbd4c2feacbf0c99bb" |
| |
| br1 := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit(hash1))) |
| br2 := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit(other))) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| f1, err := br1.scheduleRuns(ctx, jobID, &bp, 10) |
| require.NoError(t, err) |
| |
| require.ErrorContains(t, br2.updateRuns(ctx, f1), "different") |
| |
| require.EqualValues(t, 10, br1.totalScheduledRuns()) |
| require.NoError(t, br1.updateRuns(ctx, f1)) |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| require.Len(t, br1.Runs, 10) |
| } |
| |
| func TestBisectRun_WithAlreadyUpdated_ShouldError(t *testing.T) { |
| testSuite := &testsuite.WorkflowTestSuite{} |
| env := testSuite.NewTestWorkflowEnvironment() |
| |
| const jobID = "fake-job-0000-aaaa-9999-0123456789ab" |
| bp := makeDefaultBisectParams() |
| |
| env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner}) |
| env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun, nil) |
| |
| const hash1 = "fake-ffcaaab85ecf1a896da1d635aeca929edbe" |
| br := newBisectRun(common.NewCombinedCommit(common.NewChromiumCommit(hash1))) |
| env.ExecuteWorkflow(func(ctx workflow.Context) error { |
| f1, err := br.scheduleRuns(ctx, jobID, &bp, 10) |
| require.EqualValues(t, 10, br.totalScheduledRuns()) |
| require.NoError(t, err) |
| require.NoError(t, br.updateRuns(ctx, f1)) |
| |
| require.EqualValues(t, 0, br.totalScheduledRuns()) |
| require.ErrorContains(t, br.updateRuns(ctx, f1), "already updated") |
| return nil |
| }) |
| |
| require.NoError(t, env.GetWorkflowError()) |
| require.Len(t, br.Runs, 10) |
| } |
| |
| func TestNextRunSize_BothEqual_MoreRunsForBoth(t *testing.T) { |
| const hash1 = "fake-ffcaaab85ecf1a896da1d635aeca929edbe" |
| test := func(name string, runs, minSampleSize, expected int) { |
| t.Run(name, func(t *testing.T) { |
| br := &BisectRun{CommitRun: *generateSingleCommitRuns(hash1, runs)} |
| assert.EqualValues(t, expected, nextRunSizeOf(t, br, br, int32(minSampleSize))) |
| }) |
| } |
| // see benchmarkRunIterations for how these run iterations are calculated |
| test("0 runs each should expect 10 runs", 0, 10, 10) |
| test("0 runs each with minSampleSize 20 should expect 20 runs", 0, 20, 20) |
| test("15 runs each with minSampleSize 15 should expect 20 runs", 15, 15, 20) |
| test("10 runs each should expect 20 runs", 10, 10, 20) |
| test("20 runs each should expect 40 runs", 20, 10, 40) |
| } |
| |
| func TestNextRunSize_LowerCommitMoreRuns_OnlySchedulesMoreRunsForHigherCommit(t *testing.T) { |
| const hash1 = "fake1-1111111111111196da1d635aeca929edbe" |
| const hash2 = "fake2-2222222222222296da1d635aeca929edbe" |
| br1 := &BisectRun{CommitRun: *generateSingleCommitRuns(hash1, 0)} |
| br2 := &BisectRun{CommitRun: *generateSingleCommitRuns(hash2, 10)} |
| require.EqualValues(t, 10, nextRunSizeOf(t, br1, br2, 0)) |
| require.EqualValues(t, 10, nextRunSizeOf(t, br2, br1, 0)) |
| } |
| |
| func TestNextRunSize_WithNonZero_ReturnMinSampleSize(t *testing.T) { |
| const hash1 = "fake1-fcaaab85ecf1a896da1d635aeca929edbe" |
| br := &BisectRun{CommitRun: *generateSingleCommitRuns(hash1, 0)} |
| require.EqualValues(t, benchmarkRunIterations[0], nextRunSizeOf(t, br, br, 0)) |
| require.EqualValues(t, 10, nextRunSizeOf(t, br, br, 10)) |
| require.EqualValues(t, 20, nextRunSizeOf(t, br, br, 20)) |
| } |