blob: 6bbe2c1fde44eb10d168e5d286fb995ba504d4a1 [file] [log] [blame]
package catapult
import (
"fmt"
"testing"
"time"
_ "embed"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
apipb "go.chromium.org/luci/swarming/proto/api_v2"
"go.skia.org/infra/go/swarming"
"go.skia.org/infra/go/vcsinfo"
"go.skia.org/infra/pinpoint/go/backends"
"go.skia.org/infra/pinpoint/go/compare"
"go.skia.org/infra/pinpoint/go/midpoint"
"go.skia.org/infra/pinpoint/go/workflows"
"go.skia.org/infra/pinpoint/go/workflows/internal"
pinpoint_proto "go.skia.org/infra/pinpoint/proto/v1"
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
)
//go:embed testdata/main_commit_message.txt
var mainCommitMsg string
//go:embed testdata/modified_dep_commit_message.txt
var modifiedDepCommitMsg string
func TestParseImprovementDirection_Up_0(t *testing.T) {
assert.Equal(t, int32(0), parseImprovementDir(compare.Up))
}
func TestParseImprovementDirection_Down_1(t *testing.T) {
assert.Equal(t, int32(1), parseImprovementDir(compare.Down))
}
func TestParseImprovementDirection_UnknownDir_4(t *testing.T) {
assert.Equal(t, int32(4), parseImprovementDir(compare.UnknownDir))
}
// mockParseRunDataWorkflow is a helper function to wrap parseRunData() under a workflow to mock the FetchTaskActivity calls.
func mockParseRunDataWorkflow(ctx workflow.Context, runData []*internal.BisectRun, chart string) (*pinpoint_proto.LegacyJobResponse, error) {
ctx = workflow.WithChildOptions(ctx, childWorkflowOptions)
ctx = workflow.WithActivityOptions(ctx, regularActivityOptions)
commitToAttempts, bots, _ := parseRunData(ctx, runData, chart)
resp := &pinpoint_proto.LegacyJobResponse{
Bots: bots,
State: []*pinpoint_proto.LegacyJobResponse_State{},
}
for _, attempts := range commitToAttempts {
state := &pinpoint_proto.LegacyJobResponse_State{
Attempts: attempts,
}
resp.State = append(resp.State, state)
}
return resp, nil
}
func TestParseRunData_RunData_StatesAndAttempts(t *testing.T) {
swarmingTaskID := "69067f22f5cc6710"
botID := "build123-h1"
chart := "chart"
bisectRuns := []*internal.BisectRun{
{
CommitRun: internal.CommitRun{
Build: &workflows.Build{
BuildChromeParams: workflows.BuildChromeParams{
Commit: midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("d9ac8dd553c566b8fe107dd8c8b2275c2c9c27f1")),
},
CAS: &apipb.CASReference{
CasInstance: "projects/chrome-swarming/instances/default_instance",
Digest: &apipb.Digest{
Hash: "25009b847133c029dc585020ed7b60b6573fe12123559319ea5c04fec3b6e06c",
SizeBytes: int64(183),
},
},
},
Runs: []*workflows.TestRun{
{
TaskID: swarmingTaskID,
Status: swarming.TASK_STATE_COMPLETED,
CAS: &apipb.CASReference{
CasInstance: "projects/chrome-swarming/instances/default_instance",
Digest: &apipb.Digest{
Hash: "25009b847133c029dc585020ed7b60b6573fe12123559319ea5c04fec3b6e06c",
SizeBytes: int64(183),
},
},
Values: map[string][]float64{
chart: {
0.1, 0.2, 0.3,
},
},
},
{
TaskID: swarmingTaskID,
Status: backends.RunBenchmarkFailure,
},
},
},
},
}
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
env.RegisterWorkflow(mockParseRunDataWorkflow)
env.RegisterActivity(FetchTaskActivity)
env.OnActivity(FetchTaskActivity, mock.Anything, swarmingTaskID).Return(&apipb.TaskResultResponse{
BotId: botID,
TaskId: swarmingTaskID,
}, nil)
env.ExecuteWorkflow(mockParseRunDataWorkflow, bisectRuns, chart)
require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
var actual *pinpoint_proto.LegacyJobResponse
require.NoError(t, env.GetWorkflowResult(&actual))
require.NotNil(t, actual)
assert.Equal(t, 1, len(actual.State))
executions := actual.State[0].Attempts[0].Executions
// TODO(jeffyoon@) - add checks for execution[0], which is the Build quest detail once Build is appended to CommitRun.
testQuestDetail := executions[1].Details
assert.Equal(t, botID, testQuestDetail[0].Value)
assert.Equal(t, swarmingTaskID, testQuestDetail[1].Value)
assert.Equal(t, 1, len(actual.Bots))
assert.Equal(t, botID, actual.Bots[0])
assert.Equal(t, fmt.Sprintf(casIsolateHashTemplate, "25009b847133c029dc585020ed7b60b6573fe12123559319ea5c04fec3b6e06c", int64(183)), testQuestDetail[2].Value)
questDetail2 := actual.State[0].Attempts[1].Executions[1].Details
assert.Empty(t, questDetail2[2].Value)
}
// createCombinedResults a helper function to generate combinedresults
func createCombinedResults(lower string, lowerValues []float64, higher string, higherValues []float64) *internal.CombinedResults {
return &internal.CombinedResults{
ResultType: "Performance",
CommitPairValues: internal.CommitPairValues{
Lower: internal.CommitValues{
Commit: midpoint.NewCombinedCommit(midpoint.NewChromiumCommit(lower)),
Values: lowerValues,
},
Higher: internal.CommitValues{
Commit: midpoint.NewCombinedCommit(midpoint.NewChromiumCommit(higher)),
Values: higherValues,
},
},
}
}
func TestParseResultValuesPerCommit_FunctionalResult_Nothing(t *testing.T) {
// commit order from oldest to newest: 493a946, 2887740, 93dd3db, 836476df, f8e1800
comparisons := []*internal.CombinedResults{
{
ResultType: internal.Functional,
},
}
res := parseResultValuesPerCommit(comparisons)
require.NotNil(t, res)
assert.Empty(t, res)
}
func TestParseResultValuesPerCommit_ListOfCombinedResults_MapOfKeysToValues(t *testing.T) {
// commit order from oldest to newest: 493a946, 2887740, 93dd3db, 836476df, f8e1800
comparisons := []*internal.CombinedResults{
// initial range
createCombinedResults("493a946", []float64{0.0}, "f8e1800", []float64{5.0}),
// midpoint comparisons with 93dd3db
createCombinedResults("93dd3db", []float64{3.0}, "f8e1800", []float64{5.0}),
createCombinedResults("493a946", []float64{0.0}, "93dd3db", []float64{3.0}),
// lower side midpoint 2887740 comparisons
createCombinedResults("493a946", []float64{0.0}, "2887740", []float64{1.0}),
createCombinedResults("2887740", []float64{1.0}, "93dd3db", []float64{3.0}),
// higher side midpoint 8d36476df comparisons
createCombinedResults("93dd3db", []float64{3.0}, "836476df", []float64{4.0}),
createCombinedResults("836476df", []float64{4.0}, "f8e1800", []float64{5.0}),
}
res := parseResultValuesPerCommit(comparisons)
require.NotNil(t, res)
assert.Equal(t, 5, len(res))
// spot checking a few
baseCommit := midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("493a946"))
assert.Equal(t, 0.0, res[baseCommit.Key()][0])
fourthCommit := midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("836476df"))
assert.Equal(t, 4.0, res[fourthCommit.Key()][0])
}
func TestParseArguments(t *testing.T) {
request := &pinpoint_proto.ScheduleBisectRequest{
ComparisonMode: "performance",
StartGitHash: "d9ac8dd553c566b8fe107dd8c8b2275c2c9c27f1",
EndGitHash: "81a6a08061d9a2da7413021bce961d125dc40ca2",
Configuration: "win-10_laptop_low_end-perf",
Benchmark: "blink_perf.owp_storage",
Story: "blob-perf-shm.html",
Chart: "blob-perf-shm",
ComparisonMagnitude: "21.9925",
Project: "chromium",
InitialAttemptCount: "20",
ImprovementDirection: "DOWN",
}
arguments, err := parseArguments(request)
require.NoError(t, err)
assert.Equal(t, "performance_test_suite", arguments.Target)
assert.Equal(t, float64(21.9925), arguments.ComparisonMagnitude)
assert.Equal(t, int32(20), arguments.InitialAttemptCount)
assert.Nil(t, arguments.Tags)
}
func TestParseToSortedCombinedCommits_LowerLower_SortedOrder(t *testing.T) {
// order from oldest to newest:
// 493a946, 2887740, 93dd3db, 836476df, f8e1800, 6649fc3, 58079459, 2604df7, 4b233ef7
// lower lower meaning:
// midpoint f8e1800, then 93dd3db, then 288740
comparisons := []*internal.CombinedResults{
// initial range
createCombinedResults("493a946", nil, "4b233ef7", nil),
// midpoint comparisons with 93dd3db
createCombinedResults("493a946", nil, "f8e1800", nil),
createCombinedResults("f8e1800", nil, "4b233ef7", nil),
// lower side midpoint 93dd3db comparisons
createCombinedResults("93dd3db", nil, "f8e1800", nil),
createCombinedResults("493a946", nil, "93dd3db", nil),
// lower side midpoint 288740 comparison
createCombinedResults("493a946", nil, "2887740", nil),
createCombinedResults("2887740", nil, "93dd3db", nil),
}
expected := []*midpoint.CombinedCommit{
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("493a946")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("2887740")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("93dd3db")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("f8e1800")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("4b233ef7")),
}
result := parseToSortedCombinedCommits(comparisons)
for i, combinedCommit := range result {
assert.Equal(t, expected[i].Key(), combinedCommit.Key())
}
}
func TestParseToSortedCombinedCommits_LowerUpper_SortedOrder(t *testing.T) {
// order from oldest to newest:
// [493a946, 2887740, 93dd3db, 836476df, f8e1800, 6649fc3, 58079459, 2604df7, 4b233ef7]
// lower upper meaning:
// midpoint f8e1800, then 93dd3db, then 836476df
comparisons := []*internal.CombinedResults{
// initial range
createCombinedResults("493a946", nil, "4b233ef7", nil),
// midpoint comparisons with f8e1800
createCombinedResults("493a946", nil, "f8e1800", nil),
createCombinedResults("f8e1800", nil, "4b233ef7", nil),
// lower side midpoint 93dd3db comparisons
createCombinedResults("93dd3db", nil, "f8e1800", nil),
createCombinedResults("493a946", nil, "93dd3db", nil),
// lower side midpoint 836476df comparison
createCombinedResults("93dd3db", nil, "836476df", nil),
createCombinedResults("836476df", nil, "f8e1800", nil),
}
expected := []*midpoint.CombinedCommit{
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("493a946")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("93dd3db")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("836476df")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("f8e1800")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("4b233ef7")),
}
result := parseToSortedCombinedCommits(comparisons)
for i, combinedCommit := range result {
assert.Equal(t, expected[i].Key(), combinedCommit.Key())
}
}
func TestParseToSortedCombinedCommits_UpperLower_SortedOrder(t *testing.T) {
// order from oldest to newest:
// [493a946, 2887740, 93dd3db, 836476df, f8e1800, 6649fc3, 58079459, 2604df7, 4b233ef7]
// upper lower meaning:
// midpoint f8e1800, then 58079459, then 6649fc3
comparisons := []*internal.CombinedResults{
// initial range
createCombinedResults("493a946", nil, "4b233ef7", nil),
// midpoint comparisons with f8e1800
createCombinedResults("493a946", nil, "f8e1800", nil),
createCombinedResults("f8e1800", nil, "4b233ef7", nil),
// lower side midpoint 58079459 comparisons
createCombinedResults("58079459", nil, "4b233ef7", nil),
createCombinedResults("f8e1800", nil, "58079459", nil),
// lower side midpoint 6649fc3 comparison
createCombinedResults("6649fc3", nil, "58079459", nil),
createCombinedResults("f8e1800", nil, "6649fc3", nil),
}
expected := []*midpoint.CombinedCommit{
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("493a946")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("f8e1800")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("6649fc3")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("58079459")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("4b233ef7")),
}
result := parseToSortedCombinedCommits(comparisons)
for i, combinedCommit := range result {
assert.Equal(t, expected[i].Key(), combinedCommit.Key())
}
}
func TestParseToSortedCombinedCommits_UpperUpper_SortedOrder(t *testing.T) {
// order from oldest to newest:
// [493a946, 2887740, 93dd3db, 836476df, f8e1800, 6649fc3, 58079459, 2604df7, 4b233ef7]
// upper upper meaning:
// midpoint f8e1800, then 58079459, then 2604df7
comparisons := []*internal.CombinedResults{
// initial range
createCombinedResults("493a946", nil, "4b233ef7", nil),
// midpoint comparisons with f8e1800
createCombinedResults("493a946", nil, "f8e1800", nil),
createCombinedResults("f8e1800", nil, "4b233ef7", nil),
// lower side midpoint 58079459 comparisons
createCombinedResults("f8e1800", nil, "58079459", nil),
createCombinedResults("58079459", nil, "4b233ef7", nil),
// lower side midpoint 2604df7 comparison
createCombinedResults("2604df7", nil, "4b233ef7", nil),
createCombinedResults("58079459", nil, "2604df7", nil),
}
expected := []*midpoint.CombinedCommit{
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("493a946")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("f8e1800")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("58079459")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("2604df7")),
midpoint.NewCombinedCommit(midpoint.NewChromiumCommit("4b233ef7")),
}
result := parseToSortedCombinedCommits(comparisons)
for i, combinedCommit := range result {
assert.Equal(t, expected[i].Key(), combinedCommit.Key())
}
}
// mockParseCommitDataWorkflow is a helper function to wrap parseCommitData() under a workflow to mock the FetchCommitActivity behavior
func mockParseCommitDataWorkflow(ctx workflow.Context, combinedCommit *midpoint.CombinedCommit) ([]*pinpoint_proto.Commit, error) {
ctx = workflow.WithChildOptions(ctx, childWorkflowOptions)
ctx = workflow.WithActivityOptions(ctx, regularActivityOptions)
return parseCommitData(ctx, combinedCommit)
}
func TestParseCommitData_CombinedCommitWithModifiedDeps_Commits(t *testing.T) {
chromiumHash := "493a946"
depRepo := "https://chromium-review.googlesource.com/c/v8/v8"
depHash := "f8e1800"
chromiumCommit := midpoint.NewChromiumCommit(chromiumHash)
modifiedDepCommit := &pinpoint_proto.Commit{
Repository: depRepo,
GitHash: depHash,
}
combinedCommit := midpoint.NewCombinedCommit(chromiumCommit, modifiedDepCommit)
timeNow := time.Now().UTC()
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
env.RegisterWorkflow(mockParseCommitDataWorkflow)
env.RegisterActivity(FetchCommitActivity)
mainCommitResp := &vcsinfo.LongCommit{
ShortCommit: &vcsinfo.ShortCommit{
Hash: chromiumHash,
Author: "John Doe (johndoe@gmail.com)",
Subject: "[anchor-position] Implements resolving anchor-center.",
},
Body: mainCommitMsg,
Timestamp: timeNow,
}
modDepCommitResp := &vcsinfo.LongCommit{
ShortCommit: &vcsinfo.ShortCommit{
Hash: depHash,
Author: "Jane Doe (janedoe@gmail.com)",
Subject: "[bazel] Fix some bazel buildifier complains",
},
Body: modifiedDepCommitMsg,
Timestamp: timeNow,
}
// context mocked w/ mock.Anything
env.OnActivity(FetchCommitActivity, mock.Anything, chromiumCommit).Return(mainCommitResp, nil)
env.OnActivity(FetchCommitActivity, mock.Anything, modifiedDepCommit).Return(modDepCommitResp, nil)
env.ExecuteWorkflow(mockParseCommitDataWorkflow, combinedCommit)
require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
var actual []*pinpoint_proto.Commit
require.NoError(t, env.GetWorkflowResult(&actual))
require.NotNil(t, actual)
require.NotEmpty(t, actual)
assert.Equal(t, 2, len(actual))
mainCommit := actual[0]
assert.Equal(t, fmt.Sprintf(repositoryUrlTemplate, midpoint.ChromiumSrcGit, chromiumHash), mainCommit.Url)
assert.Equal(t, "johndoe@gmail.com", mainCommit.Author)
assert.Equal(t, "I40cc1e697cd8f8f0759f18ba814e19321e19702b", mainCommit.ChangeId)
assert.Equal(t, "refs/heads/main", mainCommit.CommitBranch)
assert.Equal(t, int32(1252779), mainCommit.CommitPosition)
assert.Equal(t, "https://chromium-review.googlesource.com/c/chromium/src/+/5196073", mainCommit.ReviewUrl)
modDepCommit := actual[1]
assert.Equal(t, int32(92657), modDepCommit.CommitPosition)
assert.Equal(t, "https://chromium-review.googlesource.com/c/v8/v8/+/5342761", modDepCommit.ReviewUrl)
}