| // Copyright 2023 Google LLC |
| // |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "context" |
| "strings" |
| "testing" |
| "time" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/exec" |
| exec_testutils "go.skia.org/infra/go/exec/testutils" |
| "go.skia.org/infra/go/gcs" |
| "go.skia.org/infra/go/gcs/mocks" |
| "go.skia.org/infra/go/now" |
| infra_testutils "go.skia.org/infra/go/testutils" |
| "go.skia.org/infra/task_driver/go/lib/os_steps" |
| "go.skia.org/infra/task_driver/go/td" |
| "go.skia.org/skia/infra/bots/task_drivers/common" |
| "go.skia.org/skia/infra/bots/task_drivers/testutils" |
| ) |
| |
| func TestRun_UnitTest_Success(t *testing.T) { |
| commandCollector := exec.CommandCollector{} |
| res := td.RunTestSteps(t, false, func(ctx context.Context) error { |
| ctx = td.WithExecRunFn(ctx, commandCollector.Run) |
| |
| err := run(ctx, taskDriverArgs{ |
| commandPath: "/path/to/command", |
| commandWorkDir: "/path/to/workdir", |
| testKind: unitTest, |
| }) |
| |
| assert.NoError(t, err) |
| return err |
| }) |
| |
| require.Empty(t, res.Errors) |
| require.Empty(t, res.Exceptions) |
| |
| testutils.AssertStepNames(t, res, "/path/to/command") |
| |
| assert.Equal(t, "/path/to/workdir", commandCollector.Commands()[0].Dir) |
| exec_testutils.AssertCommandsMatch(t, [][]string{{"/path/to/command"}}, commandCollector.Commands()) |
| } |
| |
| func TestRun_GMTest_Success(t *testing.T) { |
| // Given that we have tests for common.UploadToGold(), it suffices to test a couple of |
| // "happy" cases here. |
| |
| test := func(name string, tdArgs taskDriverArgs, goldctlWorkDir string, goldctlImgtestInitStepName string, goldctlImgtestInitArgs []string) { |
| t.Run(name, func(t *testing.T) { |
| // Create directory with fake undeclared test outputs. |
| testutils.PopulateDir(t, tdArgs.undeclaredOutputsDir, map[string]string{ |
| // The contents of PNG files does not matter for this test. |
| "alfa.png": "fake PNG", |
| "alfa.json": `{ |
| "md5": "a01a01a01a01a01a01a01a01a01a01a0", |
| "keys": { |
| "build_system": "bazel", |
| "name": "alfa", |
| "source_type": "gm" |
| } |
| }`, |
| "beta.png": "fake PNG", |
| "beta.json": `{ |
| "md5": "b02b02b02b02b02b02b02b02b02b02b0", |
| "keys": { |
| "build_system": "bazel", |
| "name": "beta", |
| "source_type": "gm" |
| } |
| }`, |
| }) |
| |
| commandCollector := exec.CommandCollector{} |
| res := td.RunTestSteps(t, false, func(ctx context.Context) error { |
| ctx = td.WithExecRunFn(ctx, commandCollector.Run) |
| |
| // We don't need to assert the exact number of times that os_steps.TempDir() is called |
| // because said function produces a "Creating TempDir" task driver step, and we check the |
| // exact set of steps produced. |
| ctx = context.WithValue(ctx, os_steps.TempDirContextKey, testutils.MakeTempDirMockFn(t, goldctlWorkDir)) |
| |
| err := run(ctx, tdArgs) |
| |
| assert.NoError(t, err) |
| return err |
| }) |
| |
| require.Empty(t, res.Errors) |
| require.Empty(t, res.Exceptions) |
| |
| testutils.AssertStepNames(t, res, |
| "/path/to/command --device-specific-bazel-config Pixel5 --key arch arm64 model Pixel5 os Android --gpuName Adreno620", |
| "Gather JSON and PNG files produced by GMs", |
| "Gather \"alfa.png\"", |
| "Gather \"beta.png\"", |
| "Upload GM outputs to Gold", |
| "Creating TempDir", |
| "/path/to/goldctl auth --work-dir "+goldctlWorkDir+" --luci", |
| goldctlImgtestInitStepName, |
| "/path/to/goldctl imgtest add --work-dir "+goldctlWorkDir+" --test-name alfa --png-file "+tdArgs.undeclaredOutputsDir+"/alfa.png --png-digest a01a01a01a01a01a01a01a01a01a01a0 --add-test-key build_system:bazel --add-test-key name:alfa --add-test-key source_type:gm", |
| "/path/to/goldctl imgtest add --work-dir "+goldctlWorkDir+" --test-name beta --png-file "+tdArgs.undeclaredOutputsDir+"/beta.png --png-digest b02b02b02b02b02b02b02b02b02b02b0 --add-test-key build_system:bazel --add-test-key name:beta --add-test-key source_type:gm", |
| "/path/to/goldctl imgtest finalize --work-dir "+goldctlWorkDir, |
| ) |
| |
| assert.Equal(t, "/path/to/workdir", commandCollector.Commands()[0].Dir) |
| exec_testutils.AssertCommandsMatch(t, [][]string{ |
| { |
| "/path/to/command", |
| "--device-specific-bazel-config", "Pixel5", |
| "--key", |
| "arch", "arm64", |
| "model", "Pixel5", |
| "os", "Android", |
| "--gpuName", "Adreno620", |
| }, |
| { |
| "/path/to/goldctl", |
| "auth", |
| "--work-dir", goldctlWorkDir, |
| "--luci", |
| }, |
| append([]string{"/path/to/goldctl"}, goldctlImgtestInitArgs...), |
| { |
| "/path/to/goldctl", |
| "imgtest", |
| "add", |
| "--work-dir", goldctlWorkDir, |
| "--test-name", "alfa", |
| "--png-file", tdArgs.undeclaredOutputsDir + "/alfa.png", |
| "--png-digest", "a01a01a01a01a01a01a01a01a01a01a0", |
| "--add-test-key", "build_system:bazel", |
| "--add-test-key", "name:alfa", |
| "--add-test-key", "source_type:gm", |
| }, |
| { |
| "/path/to/goldctl", |
| "imgtest", |
| "add", |
| "--work-dir", goldctlWorkDir, |
| "--test-name", "beta", |
| "--png-file", tdArgs.undeclaredOutputsDir + "/beta.png", |
| "--png-digest", "b02b02b02b02b02b02b02b02b02b02b0", |
| "--add-test-key", "build_system:bazel", |
| "--add-test-key", "name:beta", |
| "--add-test-key", "source_type:gm", |
| }, |
| { |
| "/path/to/goldctl", |
| "imgtest", |
| "finalize", |
| "--work-dir", goldctlWorkDir, |
| }, |
| }, commandCollector.Commands()) |
| }) |
| } |
| |
| goldctlWorkDir := t.TempDir() |
| test( |
| "post-submit task", |
| taskDriverArgs{ |
| UploadToGoldArgs: common.UploadToGoldArgs{ |
| TestOnlyAllowAnyBazelLabel: true, |
| BazelLabel: "//some/test:target", |
| DeviceSpecificBazelConfig: "Pixel5", |
| GoldctlPath: "/path/to/goldctl", |
| GitCommit: "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| }, |
| commandPath: "/path/to/command", |
| commandWorkDir: "/path/to/workdir", |
| testKind: gmTest, |
| deviceSpecificBazelConfigName: "Pixel5", |
| undeclaredOutputsDir: t.TempDir(), |
| }, |
| goldctlWorkDir, |
| "/path/to/goldctl imgtest init --work-dir "+goldctlWorkDir+" --instance skia --url https://gold.skia.org --bucket skia-infra-gm --git_hash ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99 --key arch:arm64 --key model:Pixel5 --key os:Android", |
| []string{ |
| "imgtest", |
| "init", |
| "--work-dir", goldctlWorkDir, |
| "--instance", "skia", |
| "--url", "https://gold.skia.org", |
| "--bucket", "skia-infra-gm", |
| "--git_hash", "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| "--key", "arch:arm64", |
| "--key", "model:Pixel5", |
| "--key", "os:Android", |
| }, |
| ) |
| |
| goldctlWorkDir = t.TempDir() |
| test( |
| "CL task", |
| taskDriverArgs{ |
| UploadToGoldArgs: common.UploadToGoldArgs{ |
| TestOnlyAllowAnyBazelLabel: true, |
| BazelLabel: "//some/test:target", |
| DeviceSpecificBazelConfig: "Pixel5", |
| GoldctlPath: "/path/to/goldctl", |
| GitCommit: "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| ChangelistID: "changelist-id", |
| PatchsetOrder: "1", |
| TryjobID: "tryjob-id", |
| }, |
| commandPath: "/path/to/command", |
| commandWorkDir: "/path/to/workdir", |
| testKind: gmTest, |
| deviceSpecificBazelConfigName: "Pixel5", |
| undeclaredOutputsDir: t.TempDir(), |
| }, |
| goldctlWorkDir, |
| "/path/to/goldctl imgtest init --work-dir "+goldctlWorkDir+" --instance skia --url https://gold.skia.org --bucket skia-infra-gm --git_hash ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99 --crs gerrit --cis buildbucket --changelist changelist-id --patchset 1 --jobid tryjob-id --key arch:arm64 --key model:Pixel5 --key os:Android", |
| []string{ |
| "imgtest", |
| "init", |
| "--work-dir", goldctlWorkDir, |
| "--instance", "skia", |
| "--url", "https://gold.skia.org", |
| "--bucket", "skia-infra-gm", |
| "--git_hash", "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| "--crs", "gerrit", |
| "--cis", "buildbucket", |
| "--changelist", "changelist-id", |
| "--patchset", "1", |
| "--jobid", "tryjob-id", |
| "--key", "arch:arm64", |
| "--key", "model:Pixel5", |
| "--key", "os:Android", |
| }, |
| ) |
| } |
| |
| func TestRun_BenchmarkTest_Success(t *testing.T) { |
| // Given that we have tests for common.UploadToPerf(), it suffices to test a couple of "happy" |
| // cases here. |
| |
| test := func(name string, tdArgs taskDriverArgs, benchmarkInvocation []string) { |
| t.Run(name, func(t *testing.T) { |
| // Create directory with fake undeclared test outputs. |
| resultsJSONFileContents := `{"foo": "this test requires that this file exists; its contents do not matter"}` |
| testutils.PopulateDir(t, tdArgs.undeclaredOutputsDir, map[string]string{ |
| "results.json": resultsJSONFileContents, |
| "some-image.png": "fake PNG", |
| "some-plaintext.txt": "fake TXT", |
| }) |
| |
| commandCollector := exec.CommandCollector{} |
| |
| gcsClient := mocks.NewGCSClient(t) |
| gcsClient.On("Bucket").Return("skia-perf") |
| gcsClient.On( |
| "SetFileContents", |
| infra_testutils.AnyContext, |
| "nano-json-v1/2022/01/31/01/ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99/BazelTest-Foo-Bar/results_1234567890.json", |
| gcs.FILE_WRITE_OPTS_TEXT, |
| []byte(resultsJSONFileContents)). |
| Return(nil).Once() |
| tdArgs.gcsClient = gcsClient |
| |
| res := td.RunTestSteps(t, false, func(ctx context.Context) error { |
| // Make sure we use UTC instead of the system timezone. The GCS path reflects the fact that |
| // we convert from UTC+1 to UTC. |
| fakeNow := time.Date(2022, time.January, 31, 2, 2, 3, 0, time.FixedZone("UTC+1", 60*60)) |
| ctx = now.TimeTravelingContext(fakeNow).WithContext(ctx) |
| ctx = td.WithExecRunFn(ctx, commandCollector.Run) |
| |
| err := run(ctx, tdArgs) |
| |
| assert.NoError(t, err) |
| return err |
| }) |
| |
| require.Empty(t, res.Errors) |
| require.Empty(t, res.Exceptions) |
| |
| testutils.AssertStepNames(t, res, |
| strings.Join(benchmarkInvocation, " "), |
| "Stat "+tdArgs.undeclaredOutputsDir+"/results.json", |
| "Read "+tdArgs.undeclaredOutputsDir+"/results.json", |
| "Upload gs://skia-perf/nano-json-v1/2022/01/31/01/ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99/BazelTest-Foo-Bar/results_1234567890.json", |
| ) |
| |
| assert.Equal(t, "/path/to/workdir", commandCollector.Commands()[0].Dir) |
| exec_testutils.AssertCommandsMatch(t, [][]string{benchmarkInvocation}, commandCollector.Commands()) |
| }) |
| } |
| |
| test( |
| "post-submit task", |
| taskDriverArgs{ |
| BenchmarkInfo: common.BenchmarkInfo{ |
| GitCommit: "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| TaskName: "BazelTest-Foo-Bar", |
| TaskID: "1234567890", |
| }, |
| commandPath: "/path/to/command", |
| commandWorkDir: "/path/to/workdir", |
| testKind: benchmarkTest, |
| deviceSpecificBazelConfigName: "Pixel5", |
| undeclaredOutputsDir: t.TempDir(), |
| }, |
| []string{ |
| "/path/to/command", |
| "--gitHash", "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| "--links", |
| "task", "https://task-scheduler.skia.org/task/1234567890", |
| "--device-specific-bazel-config", "Pixel5", |
| "--key", |
| "arch", "arm64", |
| "model", "Pixel5", |
| "os", "Android", |
| "--gpuName", "Adreno620", |
| }, |
| ) |
| |
| test( |
| "CL task", |
| taskDriverArgs{ |
| BenchmarkInfo: common.BenchmarkInfo{ |
| GitCommit: "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| TaskName: "BazelTest-Foo-Bar", |
| TaskID: "1234567890", |
| ChangelistID: "12345", |
| PatchsetOrder: "3", |
| }, |
| commandPath: "/path/to/command", |
| commandWorkDir: "/path/to/workdir", |
| testKind: benchmarkTest, |
| deviceSpecificBazelConfigName: "Pixel5", |
| undeclaredOutputsDir: t.TempDir(), |
| }, |
| []string{ |
| "/path/to/command", |
| "--gitHash", "ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99", |
| "--issue", "12345", |
| "--patchset", "3", |
| "--links", |
| "task", "https://task-scheduler.skia.org/task/1234567890", |
| "changelist", "https://skia-review.googlesource.com/c/skia/+/12345/3", |
| "--device-specific-bazel-config", "Pixel5", |
| "--key", |
| "arch", "arm64", |
| "model", "Pixel5", |
| "os", "Android", |
| "--gpuName", "Adreno620", |
| }, |
| ) |
| } |