blob: a08a9b4354ba014d72ca4e32afd5a8f5bfb7b35f [file]
package internal
import (
"context"
"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/worker"
"go.temporal.io/sdk/workflow"
"go.skia.org/infra/pinpoint/go/common"
"go.skia.org/infra/pinpoint/go/compare"
"go.skia.org/infra/pinpoint/go/workflows"
pb "go.skia.org/infra/pinpoint/proto/v1"
)
func mockedSingleCommitRun(ctx workflow.Context, p *SingleCommitRunnerParams) (*CommitRun, error) {
return &CommitRun{
Build: &workflows.Build{
BuildParams: workflows.BuildParams{
Commit: p.CombinedCommit,
},
},
Runs: make([]*workflows.TestRun, p.Iterations),
}, nil
}
func mockedGetAllDataForCompareLocalActivity(ctx context.Context, lbr, hbr *BisectRun, chart string) (*CommitPairValues, error) {
return &CommitPairValues{
Lower: CommitValues{lbr.Build.Commit, make([]float64, 0), make([]float64, 0)},
Higher: CommitValues{hbr.Build.Commit, make([]float64, 0), make([]float64, 0)},
}, nil
}
// TODO(b/327019543): More tests and test data should be added here
//
// This is only to validate the dependent workflow signature and the workflow can connect.
func TestBisectWorkflow_SimpleNoDiffCommits_ShouldReturnEmptyCommit(t *testing.T) {
mockResult := &CombinedResults{
Result: &compare.CompareResults{
Verdict: compare.Different,
},
}
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
freeBots := []string{
"lin-1-h516--device1",
"build60-h7--device2",
"lin-2-h516--device1",
"build65-h7--device4",
"build59-h7--device2",
}
env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner})
env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.Anything).Return(mockedSingleCommitRun).Times(2)
env.OnActivity(GetAllDataForCompareLocalActivity, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockedGetAllDataForCompareLocalActivity).Once()
env.OnActivity(CompareActivity, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockResult, nil).Once()
env.OnActivity(FindAvailableBotsActivity, mock.Anything, mock.Anything, mock.Anything).Return(freeBots, nil).Once()
env.ExecuteWorkflow(BisectWorkflow, &workflows.BisectParams{
Request: &pb.ScheduleBisectRequest{
ComparisonMagnitude: "1",
},
})
require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
var be *BisectExecution
require.NoError(t, env.GetWorkflowResult(&be))
require.NotNil(t, be)
require.NotEmpty(t, be.JobId)
require.Empty(t, be.Culprits)
env.AssertExpectations(t)
}
func TestBisectRunTracker_NewIdx_ReturnSameRun(t *testing.T) {
tracker := bisectRunTracker{}
idx, run := tracker.newRun(&common.CombinedCommit{})
require.Same(t, run, tracker.get(idx), "should be exact same addresses")
}
func TestBisectRunTracker_TwoRuns_ReturnDiffIndex(t *testing.T) {
tracker := bisectRunTracker{}
idx1, run1 := tracker.newRun(&common.CombinedCommit{})
idx2, run2 := tracker.newRun(&common.CombinedCommit{})
require.NotEqualValues(t, idx1, idx2)
require.NotSame(t, run1, run2, "pointers should be different")
require.NotSame(t, tracker.get(idx1), tracker.get(idx2), "pointers should be different")
}
func TestBisectRunTracker_NonExistIndex_ReturnNil(t *testing.T) {
nonExist := BisectRunIndex(1000)
tracker := bisectRunTracker{}
require.Nil(t, tracker.get(nonExist))
_, _ = tracker.newRun(&common.CombinedCommit{})
require.Nil(t, tracker.get(nonExist))
}
func TestBisectRunTracker_ManyRuns_ReturnIndex(t *testing.T) {
tracker := bisectRunTracker{}
for i := 0; i < 100; i++ {
idx, run := tracker.newRun(&common.CombinedCommit{})
require.Same(t, run, tracker.get(idx), "should be exact same addresses")
}
}
func TestBisectWorkflow_ReplayEvents_ShouldAlwaysPass(t *testing.T) {
replayer := worker.NewWorkflowReplayer()
replayer.RegisterWorkflowWithOptions(BisectWorkflow, workflow.RegisterOptions{Name: workflows.Bisect})
err := replayer.ReplayWorkflowHistoryFromJSONFile(nil, "testdata/bisect_event_history_20240627.json")
assert.NoError(t, err)
}
func TestBisectWorkflow_WithExtraArgs_ShouldPassToSingleCommitRunner(t *testing.T) {
mockResult := &CombinedResults{
Result: &compare.CompareResults{
Verdict: compare.Different,
},
}
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
freeBots := []string{"bot1", "bot2"}
env.RegisterWorkflowWithOptions(SingleCommitRunner, workflow.RegisterOptions{Name: workflows.SingleCommitRunner})
// Assert that the mock SingleCommitRunner is invoked with the expected ExtraArgs
env.OnWorkflow(workflows.SingleCommitRunner, mock.Anything, mock.MatchedBy(func(p *SingleCommitRunnerParams) bool {
return len(p.ExtraArgs) == 2 && p.ExtraArgs[0] == "--custom-arg" && p.ExtraArgs[1] == "value"
})).Return(mockedSingleCommitRun).Times(2)
env.OnActivity(GetAllDataForCompareLocalActivity, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockedGetAllDataForCompareLocalActivity).Once()
env.OnActivity(CompareActivity, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockResult, nil).Once()
env.OnActivity(FindAvailableBotsActivity, mock.Anything, mock.Anything, mock.Anything).Return(freeBots, nil).Once()
env.ExecuteWorkflow(BisectWorkflow, &workflows.BisectParams{
Request: &pb.ScheduleBisectRequest{
ComparisonMagnitude: "1",
ExtraArgs: "--custom-arg value",
},
})
require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
env.AssertExpectations(t)
}
func TestBisectWorkflow_WithInvalidExtraArgs_ShouldFailEarly(t *testing.T) {
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
env.ExecuteWorkflow(BisectWorkflow, &workflows.BisectParams{
Request: &pb.ScheduleBisectRequest{
ComparisonMagnitude: "1",
ExtraArgs: "--custom-arg='value", // Mismatched quote
},
})
require.True(t, env.IsWorkflowCompleted())
err := env.GetWorkflowError()
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid extra_args format")
}