1// Copyright 2022 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "context" 9 "io/ioutil" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "go.skia.org/infra/go/exec" 17 "go.skia.org/infra/go/gcs" 18 "go.skia.org/infra/go/gcs/test_gcsclient" 19 "go.skia.org/infra/go/gerrit" 20 gerrit_testutils "go.skia.org/infra/go/gerrit/testutils" 21 "go.skia.org/infra/go/git" 22 git_testutils "go.skia.org/infra/go/git/testutils" 23 "go.skia.org/infra/go/gitiles" 24 gitiles_testutils "go.skia.org/infra/go/gitiles/testutils" 25 "go.skia.org/infra/go/mockhttpclient" 26 "go.skia.org/infra/go/now" 27 "go.skia.org/infra/go/testutils" 28 "go.skia.org/infra/task_driver/go/td" 29 "go.skia.org/infra/task_scheduler/go/types" 30) 31 32func TestRunSteps_PostSubmit_Success(t *testing.T) { 33 // An empty inputPatch indicates this is a post-submit task. 34 inputPatch := types.Patch{} 35 36 // The revision is assigned deterministically by the GitBuilder in test(). 37 const ( 38 expectedBloatyFileGCSPath = "2022/01/31/693abc06538769c662ca1871d347323b133a5d3c/Build-Debian10-Clang-x86_64-Release/dm.tsv" 39 expectedJSONMetadataFileGCSPath = "2022/01/31/693abc06538769c662ca1871d347323b133a5d3c/Build-Debian10-Clang-x86_64-Release/dm.json" 40 ) 41 42 // The revision and author are assigned deterministically by the GitBuilder in test(). 43 const expectedJSONMetadataFileContents = `{ 44 "version": 1, 45 "timestamp": "2022-01-31T00:00:00Z", 46 "swarming_task_id": "58dccb0d6a3f0411", 47 "swarming_server": "https://chromium-swarm.appspot.com", 48 "task_id": "CkPp9ElAaEXyYWNHpXHU", 49 "task_name": "CodeSize-dm-Debian10-Clang-x86_64-Release", 50 "compile_task_name": "Build-Debian10-Clang-x86_64-Release", 51 "binary_name": "dm", 52 "bloaty_cipd_version": "1", 53 "bloaty_args": [ 54 "build/dm", 55 "-d", 56 "compileunits,symbols", 57 "-n", 58 "0", 59 "--tsv" 60 ], 61 "patch_issue": "", 62 "patch_server": "", 63 "patch_set": "", 64 "repo": "https://skia.googlesource.com/skia.git", 65 "revision": "693abc06538769c662ca1871d347323b133a5d3c", 66 "commit_timestamp": "2022-01-30T23:59:00Z", 67 "author": "test (test@google.com)", 68 "subject": "Fake commit subject" 69}` 70 71 test(t, inputPatch, expectedBloatyFileGCSPath, expectedJSONMetadataFileGCSPath, expectedJSONMetadataFileContents) 72} 73 74func TestRunSteps_Tryjob_Success(t *testing.T) { 75 inputPatch := types.Patch{ 76 Issue: "12345", 77 PatchRepo: "https://skia.googlesource.com/skia.git", 78 Patchset: "3", 79 Server: "https://skia-review.googlesource.com", 80 } 81 82 const ( 83 expectedBloatyFileGCSPath = "2022/01/31/tryjob/12345/3/CkPp9ElAaEXyYWNHpXHU/Build-Debian10-Clang-x86_64-Release/dm.tsv" 84 expectedJSONMetadataFileGCSPath = "2022/01/31/tryjob/12345/3/CkPp9ElAaEXyYWNHpXHU/Build-Debian10-Clang-x86_64-Release/dm.json" 85 ) 86 87 // The revision and author are assigned deterministically by the GitBuilder in test(). 88 const expectedJSONMetadataFileContents = `{ 89 "version": 1, 90 "timestamp": "2022-01-31T00:00:00Z", 91 "swarming_task_id": "58dccb0d6a3f0411", 92 "swarming_server": "https://chromium-swarm.appspot.com", 93 "task_id": "CkPp9ElAaEXyYWNHpXHU", 94 "task_name": "CodeSize-dm-Debian10-Clang-x86_64-Release", 95 "compile_task_name": "Build-Debian10-Clang-x86_64-Release", 96 "binary_name": "dm", 97 "bloaty_cipd_version": "1", 98 "bloaty_args": [ 99 "build/dm", 100 "-d", 101 "compileunits,symbols", 102 "-n", 103 "0", 104 "--tsv" 105 ], 106 "patch_issue": "12345", 107 "patch_server": "https://skia-review.googlesource.com", 108 "patch_set": "3", 109 "repo": "https://skia.googlesource.com/skia.git", 110 "revision": "693abc06538769c662ca1871d347323b133a5d3c", 111 "commit_timestamp": "2022-01-30T23:59:00Z", 112 "author": "test (test@google.com)", 113 "subject": "Fake commit subject" 114}` 115 116 test(t, inputPatch, expectedBloatyFileGCSPath, expectedJSONMetadataFileGCSPath, expectedJSONMetadataFileContents) 117} 118 119func test(t *testing.T, patch types.Patch, expectedBloatyFileGCSPath, expectedJSONMetadataFileGCSPath, expectedJSONMetadataFileContents string) { 120 const expectedBloatyFileContents = "I'm a fake Bloaty output!" 121 122 fakeNow := time.Date(2022, time.January, 31, 0, 0, 0, 0, time.UTC) 123 commitTimestamp := time.Date(2022, time.January, 30, 23, 59, 0, 0, time.UTC) 124 125 repoState := types.RepoState{ 126 Patch: patch, 127 Repo: "https://skia.googlesource.com/skia.git", 128 } 129 130 // Seed a fake Git repository. 131 gitBuilder := git_testutils.GitInit(t, context.Background()) 132 defer gitBuilder.Cleanup() 133 gitBuilder.Add(context.Background(), "README.md", "I'm a fake repository.") 134 repoState.Revision = gitBuilder.CommitMsgAt(context.Background(), "Fake commit subject", commitTimestamp) 135 136 // Mock a Gerrit client. 137 tmp, err := ioutil.TempDir("", "") 138 require.NoError(t, err) 139 defer testutils.RemoveAll(t, tmp) 140 mockGerrit := gerrit_testutils.NewGerrit(t, tmp) 141 mockGerrit.MockGetIssueProperties(&gerrit.ChangeInfo{ 142 Issue: 12345, 143 Owner: &gerrit.Person{ 144 Name: "test", 145 Email: "test@google.com", 146 }, 147 Subject: "Fake commit subject", 148 // We ignore the patchset commit hashes, their values do not matter. 149 Revisions: map[string]*gerrit.Revision{ 150 "commit hash for patchset 1": { 151 Number: 1, 152 CreatedString: commitTimestamp.Add(-2 * time.Hour).Format(time.RFC3339), 153 }, 154 "commit hash for patchset 2": { 155 Number: 2, 156 CreatedString: commitTimestamp.Add(-time.Hour).Format(time.RFC3339), 157 }, 158 "commit hash for patchset 3": { 159 Number: 3, 160 CreatedString: commitTimestamp.Format(time.RFC3339), 161 }, 162 "commit hash for patchset 4": { 163 Number: 4, 164 CreatedString: commitTimestamp.Add(time.Hour).Format(time.RFC3339), 165 }, 166 }, 167 }) 168 169 // Mock a Gitiles client. 170 urlMock := mockhttpclient.NewURLMock() 171 mockRepo := gitiles_testutils.NewMockRepo(t, gitBuilder.RepoUrl(), git.GitDir(gitBuilder.Dir()), urlMock) 172 mockRepo.MockGetCommit(context.Background(), repoState.Revision) 173 mockGitiles := gitiles.NewRepo(gitBuilder.RepoUrl(), urlMock.Client()) 174 175 // Mock "bloaty" invocations. 176 commandCollector := exec.CommandCollector{} 177 commandCollector.SetDelegateRun(func(ctx context.Context, cmd *exec.Command) error { 178 if filepath.Base(cmd.Name) == "bloaty" { 179 cmd.CombinedOutput.Write([]byte(expectedBloatyFileContents)) 180 return nil 181 } 182 // "ls" and any other commands directly executed by the task driver produce no mock outputs. 183 return nil 184 }) 185 186 mockGCSClient := test_gcsclient.NewMockClient() 187 defer mockGCSClient.AssertExpectations(t) 188 189 // Mock the GCS client call to upload the Bloaty output. 190 mockGCSClient.On( 191 "SetFileContents", 192 testutils.AnyContext, 193 expectedBloatyFileGCSPath, 194 gcs.FILE_WRITE_OPTS_TEXT, 195 []byte(expectedBloatyFileContents), 196 ).Return(nil) 197 198 // Mock the GCS client call to upload the JSON metadata file. 199 mockGCSClient.On( 200 "SetFileContents", 201 testutils.AnyContext, 202 expectedJSONMetadataFileGCSPath, 203 gcs.FILE_WRITE_OPTS_TEXT, 204 []byte(expectedJSONMetadataFileContents), 205 ).Return(nil) 206 207 // Realistic but arbitrary arguments. 208 args := runStepsArgs{ 209 repoState: repoState, 210 gerrit: mockGerrit.Gerrit, 211 gitilesRepo: mockGitiles, 212 gcsClient: mockGCSClient, 213 swarmingTaskID: "58dccb0d6a3f0411", 214 swarmingServer: "https://chromium-swarm.appspot.com", 215 taskID: "CkPp9ElAaEXyYWNHpXHU", 216 taskName: "CodeSize-dm-Debian10-Clang-x86_64-Release", 217 compileTaskName: "Build-Debian10-Clang-x86_64-Release", 218 binaryName: "dm", 219 bloatyCIPDVersion: "1", 220 } 221 222 res := td.RunTestSteps(t, false, func(ctx context.Context) error { 223 ctx = now.TimeTravelingContext(fakeNow).WithContext(ctx) 224 ctx = td.WithExecRunFn(ctx, commandCollector.Run) 225 226 return runSteps(ctx, args) 227 }) 228 229 require.Empty(t, res.Errors) 230 require.Empty(t, res.Exceptions) 231 232 // Filter out all Git commands. 233 var commands []*exec.Command 234 for _, c := range commandCollector.Commands() { 235 if filepath.Base(c.Name) != "git" { 236 commands = append(commands, c) 237 } 238 } 239 240 // We expect one "ls" command and one "bloaty" command. 241 require.Len(t, commands, 2) 242 243 // Assert that "ls build" was executed to list the contents of the directory with the binaries 244 // built by the compile task, for debugging purposes. 245 lsCmd := commands[0] 246 assert.Equal(t, "ls", lsCmd.Name) 247 assert.Equal(t, []string{"build"}, lsCmd.Args) 248 249 // Assert that Bloaty was invoked with the expected arguments. 250 bloatyCmd := commands[1] 251 assert.Equal(t, "bloaty/bloaty", bloatyCmd.Name) 252 assert.Equal(t, []string{"build/dm", "-d", "compileunits,symbols", "-n", "0", "--tsv"}, bloatyCmd.Args) 253 254 // Assert that two files were uploaded to GCS. 255 mockGCSClient.AssertNumberOfCalls(t, "SetFileContents", 2) 256} 257