• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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