blob: faf7ab0bdbb65207503cd7294ad86df7365e1438 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Evan Hernandezf388cbf2019-04-01 11:15:23 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unittests for Artifacts operations."""
6
Evan Hernandezf388cbf2019-04-01 11:15:23 -06007import os
Greg Edelstondc941072021-08-11 12:32:30 -06008import pathlib
Varun Somani04dccd72021-10-09 01:06:11 +00009from typing import Optional
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Evan Hernandezf388cbf2019-04-01 11:15:23 -060011
Alex Klein231d2da2019-07-22 16:44:45 -060012from chromite.api import api_config
Evan Hernandezf388cbf2019-04-01 11:15:23 -060013from chromite.api.controller import artifacts
Greg Edelstondc941072021-08-11 12:32:30 -060014from chromite.api.controller import controller_util
Jack Neus26b94672022-10-27 17:33:21 +000015from chromite.api.controller import image as image_controller
16from chromite.api.controller import sysroot as sysroot_controller
17from chromite.api.controller import test as test_controller
Evan Hernandezf388cbf2019-04-01 11:15:23 -060018from chromite.api.gen.chromite.api import artifacts_pb2
Jack Neus26b94672022-10-27 17:33:21 +000019from chromite.api.gen.chromite.api import sysroot_pb2
Greg Edelstondc941072021-08-11 12:32:30 -060020from chromite.api.gen.chromiumos import common_pb2
Evan Hernandezf388cbf2019-04-01 11:15:23 -060021from chromite.cbuildbot import commands
Alex Kleinb9d810b2019-07-01 12:38:02 -060022from chromite.lib import chroot_lib
Evan Hernandezf388cbf2019-04-01 11:15:23 -060023from chromite.lib import constants
24from chromite.lib import cros_build_lib
25from chromite.lib import cros_test_lib
26from chromite.lib import osutils
Alex Klein238d8862019-05-07 11:32:46 -060027from chromite.lib import sysroot_lib
Alex Klein2275d692019-04-23 16:04:12 -060028from chromite.service import artifacts as artifacts_svc
Evan Hernandezf388cbf2019-04-01 11:15:23 -060029
30
Alex Kleind91e95a2019-09-17 10:39:02 -060031class BundleRequestMixin(object):
Alex Klein1699fab2022-09-08 08:46:06 -060032 """Mixin to provide bundle request methods."""
Alex Kleind91e95a2019-09-17 10:39:02 -060033
Alex Klein1699fab2022-09-08 08:46:06 -060034 def EmptyRequest(self):
35 return artifacts_pb2.BundleRequest()
Alex Kleind91e95a2019-09-17 10:39:02 -060036
Alex Klein1699fab2022-09-08 08:46:06 -060037 def BuildTargetRequest(
38 self, build_target=None, output_dir=None, chroot=None
39 ):
40 """Get a build target format request instance."""
41 request = self.EmptyRequest()
42 if build_target:
43 request.build_target.name = build_target
44 if output_dir:
45 request.output_dir = output_dir
46 if chroot:
47 request.chroot.path = chroot
Alex Kleind91e95a2019-09-17 10:39:02 -060048
Alex Klein1699fab2022-09-08 08:46:06 -060049 return request
Alex Kleind91e95a2019-09-17 10:39:02 -060050
Alex Klein1699fab2022-09-08 08:46:06 -060051 def SysrootRequest(
Brian Norris09937012023-03-31 15:16:55 -070052 self,
53 sysroot=None,
54 build_target=None,
55 output_dir=None,
56 chroot=None,
57 chroot_out=None,
Alex Klein1699fab2022-09-08 08:46:06 -060058 ):
59 """Get a sysroot format request instance."""
60 request = self.EmptyRequest()
61 if sysroot:
62 request.sysroot.path = sysroot
63 if build_target:
64 request.sysroot.build_target.name = build_target
65 if output_dir:
66 request.output_dir = output_dir
67 if chroot:
Brian Norris09937012023-03-31 15:16:55 -070068 request.chroot.path = str(chroot)
69 if chroot_out:
70 request.chroot.out_path = str(chroot_out)
Alex Kleind91e95a2019-09-17 10:39:02 -060071
Alex Klein1699fab2022-09-08 08:46:06 -060072 return request
Alex Kleind91e95a2019-09-17 10:39:02 -060073
74
Alex Klein1699fab2022-09-08 08:46:06 -060075class BundleTestCase(
76 cros_test_lib.MockTempDirTestCase,
77 api_config.ApiConfigMixin,
78 BundleRequestMixin,
79):
80 """Basic setup for all artifacts unittests."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -060081
Alex Klein1699fab2022-09-08 08:46:06 -060082 def setUp(self):
83 self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
84 self.output_dir = os.path.join(self.tempdir, "artifacts")
85 osutils.SafeMakedirs(self.output_dir)
86 self.sysroot_path = "/build/target"
87 self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
Brian Norris09937012023-03-31 15:16:55 -070088 self.chroot_path = self.tempdir / "chroot"
89 self.chroot_out_path = self.tempdir / "out"
Alex Klein1699fab2022-09-08 08:46:06 -060090 full_sysroot_path = os.path.join(
91 self.chroot_path, self.sysroot_path.lstrip(os.sep)
92 )
93 osutils.SafeMakedirs(full_sysroot_path)
Brian Norrisea02aa12023-05-01 15:24:58 -070094 osutils.SafeMakedirs(self.chroot_out_path)
Alex Klein231d2da2019-07-22 16:44:45 -060095
Alex Klein1699fab2022-09-08 08:46:06 -060096 # All requests use same response type.
97 self.response = artifacts_pb2.BundleResponse()
Alex Klein231d2da2019-07-22 16:44:45 -060098
Alex Klein1699fab2022-09-08 08:46:06 -060099 # Build target request.
100 self.target_request = self.BuildTargetRequest(
101 build_target="target",
102 output_dir=self.output_dir,
Brian Norris09937012023-03-31 15:16:55 -0700103 chroot=str(self.chroot_path),
Alex Klein1699fab2022-09-08 08:46:06 -0600104 )
Alex Klein68c8fdf2019-09-25 15:09:11 -0600105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 # Sysroot request.
107 self.sysroot_request = self.SysrootRequest(
108 sysroot=self.sysroot_path,
109 build_target="target",
110 output_dir=self.output_dir,
111 chroot=self.chroot_path,
Brian Norris09937012023-03-31 15:16:55 -0700112 chroot_out=self.chroot_out_path,
Alex Klein1699fab2022-09-08 08:46:06 -0600113 )
Alex Klein68c8fdf2019-09-25 15:09:11 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 self.source_root = self.tempdir
116 self.PatchObject(constants, "SOURCE_ROOT", new=self.tempdir)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600117
118
Alex Kleind91e95a2019-09-17 10:39:02 -0600119class BundleImageArchivesTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600120 """BundleImageArchives tests."""
Alex Kleind91e95a2019-09-17 10:39:02 -0600121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 def testValidateOnly(self):
123 """Quick check that a validate only call does not execute any logic."""
124 patch = self.PatchObject(artifacts_svc, "ArchiveImages")
125 artifacts.BundleImageArchives(
126 self.target_request, self.response, self.validate_only_config
127 )
128 patch.assert_not_called()
Alex Kleind91e95a2019-09-17 10:39:02 -0600129
Alex Klein1699fab2022-09-08 08:46:06 -0600130 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700131 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600132 patch = self.PatchObject(artifacts_svc, "ArchiveImages")
133 artifacts.BundleImageArchives(
134 self.target_request, self.response, self.mock_call_config
135 )
136 patch.assert_not_called()
137 self.assertEqual(len(self.response.artifacts), 2)
138 self.assertEqual(
139 self.response.artifacts[0].path,
140 os.path.join(self.output_dir, "path0.tar.xz"),
141 )
142 self.assertEqual(
143 self.response.artifacts[1].path,
144 os.path.join(self.output_dir, "path1.tar.xz"),
145 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700146
Alex Klein1699fab2022-09-08 08:46:06 -0600147 def testNoBuildTarget(self):
148 """Test that no build target fails."""
149 request = self.BuildTargetRequest(output_dir=str(self.tempdir))
150 with self.assertRaises(cros_build_lib.DieSystemExit):
151 artifacts.BundleImageArchives(
152 request, self.response, self.api_config
153 )
Alex Kleind91e95a2019-09-17 10:39:02 -0600154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 def testNoOutputDir(self):
156 """Test no output dir fails."""
157 request = self.BuildTargetRequest(build_target="board")
158 with self.assertRaises(cros_build_lib.DieSystemExit):
159 artifacts.BundleImageArchives(
160 request, self.response, self.api_config
161 )
Alex Kleind91e95a2019-09-17 10:39:02 -0600162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 def testInvalidOutputDir(self):
164 """Test invalid output dir fails."""
165 request = self.BuildTargetRequest(
166 build_target="board", output_dir=os.path.join(self.tempdir, "DNE")
167 )
168 with self.assertRaises(cros_build_lib.DieSystemExit):
169 artifacts.BundleImageArchives(
170 request, self.response, self.api_config
171 )
Alex Kleind91e95a2019-09-17 10:39:02 -0600172
Alex Klein1699fab2022-09-08 08:46:06 -0600173 def testOutputHandling(self):
174 """Test the artifact output handling."""
175 expected = [os.path.join(self.output_dir, f) for f in ("a", "b", "c")]
176 self.PatchObject(artifacts_svc, "ArchiveImages", return_value=expected)
177 self.PatchObject(os.path, "exists", return_value=True)
Alex Kleind91e95a2019-09-17 10:39:02 -0600178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 artifacts.BundleImageArchives(
180 self.target_request, self.response, self.api_config
181 )
Alex Kleind91e95a2019-09-17 10:39:02 -0600182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 self.assertCountEqual(
184 expected, [a.path for a in self.response.artifacts]
185 )
Alex Kleind91e95a2019-09-17 10:39:02 -0600186
187
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600188class BundleImageZipTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600189 """Unittests for BundleImageZip."""
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 def testValidateOnly(self):
192 """Quick check that a validate only call does not execute any logic."""
193 patch = self.PatchObject(commands, "BuildImageZip")
194 artifacts.BundleImageZip(
195 self.target_request, self.response, self.validate_only_config
196 )
197 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600198
Alex Klein1699fab2022-09-08 08:46:06 -0600199 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700200 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600201 patch = self.PatchObject(commands, "BuildImageZip")
202 artifacts.BundleImageZip(
203 self.target_request, self.response, self.mock_call_config
204 )
205 patch.assert_not_called()
206 self.assertEqual(len(self.response.artifacts), 1)
207 self.assertEqual(
208 self.response.artifacts[0].path,
209 os.path.join(self.output_dir, "image.zip"),
210 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700211
Alex Klein1699fab2022-09-08 08:46:06 -0600212 def testBundleImageZip(self):
213 """BundleImageZip calls cbuildbot/commands with correct args."""
214 bundle_image_zip = self.PatchObject(
215 artifacts_svc, "BundleImageZip", return_value="image.zip"
216 )
217 self.PatchObject(os.path, "exists", return_value=True)
218 artifacts.BundleImageZip(
219 self.target_request, self.response, self.api_config
220 )
221 self.assertEqual(
222 [artifact.path for artifact in self.response.artifacts],
223 [os.path.join(self.output_dir, "image.zip")],
224 )
Alex Klein231d2da2019-07-22 16:44:45 -0600225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 latest = os.path.join(
227 self.source_root, "src/build/images/target/latest"
228 )
229 self.assertEqual(
230 bundle_image_zip.call_args_list,
231 [mock.call(self.output_dir, latest)],
232 )
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600233
Alex Klein1699fab2022-09-08 08:46:06 -0600234 def testBundleImageZipNoImageDir(self):
235 """BundleImageZip dies when image dir does not exist."""
236 self.PatchObject(os.path, "exists", return_value=False)
237 with self.assertRaises(cros_build_lib.DieSystemExit):
238 artifacts.BundleImageZip(
239 self.target_request, self.response, self.api_config
240 )
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600241
242
Alex Klein68c8fdf2019-09-25 15:09:11 -0600243class BundleAutotestFilesTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600244 """Unittests for BundleAutotestFiles."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 def testValidateOnly(self):
247 """Quick check that a validate only call does not execute any logic."""
248 patch = self.PatchObject(artifacts_svc, "BundleAutotestFiles")
249 artifacts.BundleAutotestFiles(
250 self.sysroot_request, self.response, self.validate_only_config
251 )
252 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700255 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600256 patch = self.PatchObject(artifacts_svc, "BundleAutotestFiles")
257 artifacts.BundleAutotestFiles(
258 self.sysroot_request, self.response, self.mock_call_config
259 )
260 patch.assert_not_called()
261 self.assertEqual(len(self.response.artifacts), 1)
262 self.assertEqual(
263 self.response.artifacts[0].path,
264 os.path.join(self.output_dir, "autotest-a.tar.gz"),
265 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700266
Alex Klein1699fab2022-09-08 08:46:06 -0600267 def testBundleAutotestFiles(self):
268 """BundleAutotestFiles calls service correctly."""
Alex Kleinab87ceb2023-01-24 12:00:51 -0700269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 files = {
Alex Kleinab87ceb2023-01-24 12:00:51 -0700271 artifacts_svc.ARCHIVE_CONTROL_FILES: (
272 "/tmp/artifacts/autotest-a.tar.gz"
273 ),
Alex Klein1699fab2022-09-08 08:46:06 -0600274 artifacts_svc.ARCHIVE_PACKAGES: "/tmp/artifacts/autotest-b.tar.gz",
275 }
276 patch = self.PatchObject(
277 artifacts_svc, "BundleAutotestFiles", return_value=files
278 )
Alex Klein238d8862019-05-07 11:32:46 -0600279
Alex Klein1699fab2022-09-08 08:46:06 -0600280 artifacts.BundleAutotestFiles(
281 self.sysroot_request, self.response, self.api_config
282 )
Alex Klein238d8862019-05-07 11:32:46 -0600283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 # Verify the arguments are being passed through.
285 patch.assert_called_with(mock.ANY, self.sysroot, self.output_dir)
Alex Klein238d8862019-05-07 11:32:46 -0600286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 # Verify the output proto is being populated correctly.
288 self.assertTrue(self.response.artifacts)
289 paths = [artifact.path for artifact in self.response.artifacts]
290 self.assertCountEqual(list(files.values()), paths)
Alex Klein238d8862019-05-07 11:32:46 -0600291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 def testInvalidOutputDir(self):
293 """Test invalid output directory argument."""
294 request = self.SysrootRequest(
295 chroot=self.chroot_path, sysroot=self.sysroot_path
296 )
Alex Klein238d8862019-05-07 11:32:46 -0600297
Alex Klein1699fab2022-09-08 08:46:06 -0600298 with self.assertRaises(cros_build_lib.DieSystemExit):
299 artifacts.BundleAutotestFiles(
300 request, self.response, self.api_config
301 )
Alex Klein238d8862019-05-07 11:32:46 -0600302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 def testInvalidSysroot(self):
304 """Test no sysroot directory."""
305 request = self.SysrootRequest(
306 chroot=self.chroot_path, output_dir=self.output_dir
307 )
Alex Klein238d8862019-05-07 11:32:46 -0600308
Alex Klein1699fab2022-09-08 08:46:06 -0600309 with self.assertRaises(cros_build_lib.DieSystemExit):
310 artifacts.BundleAutotestFiles(
311 request, self.response, self.api_config
312 )
Alex Klein238d8862019-05-07 11:32:46 -0600313
Alex Klein1699fab2022-09-08 08:46:06 -0600314 def testSysrootDoesNotExist(self):
315 """Test dies when no sysroot does not exist."""
316 request = self.SysrootRequest(
317 chroot=self.chroot_path,
318 sysroot="/does/not/exist",
319 output_dir=self.output_dir,
320 )
Alex Klein238d8862019-05-07 11:32:46 -0600321
Alex Klein1699fab2022-09-08 08:46:06 -0600322 artifacts.BundleAutotestFiles(request, self.response, self.api_config)
323 self.assertFalse(self.response.artifacts)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600324
325
326class BundleTastFilesTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600327 """Unittests for BundleTastFiles."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600328
Alex Klein1699fab2022-09-08 08:46:06 -0600329 def testValidateOnly(self):
330 """Quick check that a validate only call does not execute any logic."""
331 patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
332 artifacts.BundleTastFiles(
333 self.sysroot_request, self.response, self.validate_only_config
334 )
335 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600336
Alex Klein1699fab2022-09-08 08:46:06 -0600337 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700338 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600339 patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
340 artifacts.BundleTastFiles(
341 self.sysroot_request, self.response, self.mock_call_config
342 )
343 patch.assert_not_called()
344 self.assertEqual(len(self.response.artifacts), 1)
345 self.assertEqual(
346 self.response.artifacts[0].path,
347 os.path.join(self.output_dir, "tast_bundles.tar.gz"),
348 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 def testBundleTastFilesNoLogs(self):
351 """BundleTasteFiles succeeds when no tast files found."""
352 self.PatchObject(commands, "BuildTastBundleTarball", return_value=None)
353 artifacts.BundleTastFiles(
354 self.sysroot_request, self.response, self.api_config
355 )
356 self.assertFalse(self.response.artifacts)
Alex Kleinb9d810b2019-07-01 12:38:02 -0600357
Alex Klein1699fab2022-09-08 08:46:06 -0600358 def testBundleTastFiles(self):
359 """BundleTastFiles calls service correctly."""
Brian Norris09937012023-03-31 15:16:55 -0700360 chroot = chroot_lib.Chroot(
361 self.chroot_path, out_path=self.chroot_out_path
362 )
Alex Kleinb9d810b2019-07-01 12:38:02 -0600363
Alex Klein1699fab2022-09-08 08:46:06 -0600364 expected_archive = os.path.join(
365 self.output_dir, artifacts_svc.TAST_BUNDLE_NAME
366 )
367 # Patch the service being called.
368 bundle_patch = self.PatchObject(
369 artifacts_svc, "BundleTastFiles", return_value=expected_archive
370 )
Alex Kleinb9d810b2019-07-01 12:38:02 -0600371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 artifacts.BundleTastFiles(
373 self.sysroot_request, self.response, self.api_config
374 )
Alex Kleinb9d810b2019-07-01 12:38:02 -0600375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 # Make sure the artifact got recorded successfully.
377 self.assertTrue(self.response.artifacts)
378 self.assertEqual(expected_archive, self.response.artifacts[0].path)
379 # Make sure the service got called correctly.
380 bundle_patch.assert_called_once_with(
381 chroot, self.sysroot, self.output_dir
382 )
Alex Kleinb9d810b2019-07-01 12:38:02 -0600383
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600384
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600385class BundleFirmwareTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600386 """Unittests for BundleFirmware."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 def testValidateOnly(self):
389 """Quick check that a validate only call does not execute any logic."""
390 patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
391 artifacts.BundleFirmware(
392 self.sysroot_request, self.response, self.validate_only_config
393 )
394 patch.assert_not_called()
Michael Mortensen38675192019-06-28 16:52:55 +0000395
Alex Klein1699fab2022-09-08 08:46:06 -0600396 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700397 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600398 patch = self.PatchObject(artifacts_svc, "BundleTastFiles")
399 artifacts.BundleFirmware(
400 self.sysroot_request, self.response, self.mock_call_config
401 )
402 patch.assert_not_called()
403 self.assertEqual(len(self.response.artifacts), 1)
404 self.assertEqual(
405 self.response.artifacts[0].path,
406 os.path.join(self.output_dir, "firmware.tar.gz"),
407 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700408
Alex Klein1699fab2022-09-08 08:46:06 -0600409 def testBundleFirmware(self):
410 """BundleFirmware calls cbuildbot/commands with correct args."""
411 self.PatchObject(
412 artifacts_svc,
413 "BuildFirmwareArchive",
414 return_value=os.path.join(self.output_dir, "firmware.tar.gz"),
415 )
Alex Klein231d2da2019-07-22 16:44:45 -0600416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 artifacts.BundleFirmware(
418 self.sysroot_request, self.response, self.api_config
419 )
420 self.assertEqual(
421 [artifact.path for artifact in self.response.artifacts],
422 [os.path.join(self.output_dir, "firmware.tar.gz")],
423 )
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600424
Alex Klein1699fab2022-09-08 08:46:06 -0600425 def testBundleFirmwareNoLogs(self):
426 """BundleFirmware dies when no firmware found."""
427 self.PatchObject(commands, "BuildFirmwareArchive", return_value=None)
428 artifacts.BundleFirmware(
429 self.sysroot_request, self.response, self.api_config
430 )
431 self.assertEqual(len(self.response.artifacts), 0)
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600432
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600433
Yicheng Liea1181f2020-09-22 11:51:10 -0700434class BundleFpmcuUnittestsTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600435 """Unittests for BundleFpmcuUnittests."""
Yicheng Liea1181f2020-09-22 11:51:10 -0700436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 def testValidateOnly(self):
438 """Quick check that a validate only call does not execute any logic."""
439 patch = self.PatchObject(artifacts_svc, "BundleFpmcuUnittests")
440 artifacts.BundleFpmcuUnittests(
441 self.sysroot_request, self.response, self.validate_only_config
442 )
443 patch.assert_not_called()
Yicheng Liea1181f2020-09-22 11:51:10 -0700444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700446 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600447 patch = self.PatchObject(artifacts_svc, "BundleFpmcuUnittests")
448 artifacts.BundleFpmcuUnittests(
449 self.sysroot_request, self.response, self.mock_call_config
450 )
451 patch.assert_not_called()
452 self.assertEqual(len(self.response.artifacts), 1)
453 self.assertEqual(
454 self.response.artifacts[0].path,
455 os.path.join(self.output_dir, "fpmcu_unittests.tar.gz"),
456 )
Yicheng Liea1181f2020-09-22 11:51:10 -0700457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 def testBundleFpmcuUnittests(self):
459 """BundleFpmcuUnittests calls cbuildbot/commands with correct args."""
460 self.PatchObject(
461 artifacts_svc,
462 "BundleFpmcuUnittests",
463 return_value=os.path.join(
464 self.output_dir, "fpmcu_unittests.tar.gz"
465 ),
466 )
467 artifacts.BundleFpmcuUnittests(
468 self.sysroot_request, self.response, self.api_config
469 )
470 self.assertEqual(
471 [artifact.path for artifact in self.response.artifacts],
472 [os.path.join(self.output_dir, "fpmcu_unittests.tar.gz")],
473 )
Yicheng Liea1181f2020-09-22 11:51:10 -0700474
Alex Klein1699fab2022-09-08 08:46:06 -0600475 def testBundleFpmcuUnittestsNoLogs(self):
476 """BundleFpmcuUnittests does not die when no fpmcu unittests found."""
477 self.PatchObject(
478 artifacts_svc, "BundleFpmcuUnittests", return_value=None
479 )
480 artifacts.BundleFpmcuUnittests(
481 self.sysroot_request, self.response, self.api_config
482 )
483 self.assertFalse(self.response.artifacts)
Yicheng Liea1181f2020-09-22 11:51:10 -0700484
485
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600486class BundleEbuildLogsTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600487 """Unittests for BundleEbuildLogs."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 def testValidateOnly(self):
490 """Quick check that a validate only call does not execute any logic."""
491 patch = self.PatchObject(commands, "BuildEbuildLogsTarball")
492 artifacts.BundleEbuildLogs(
493 self.sysroot_request, self.response, self.validate_only_config
494 )
495 patch.assert_not_called()
Michael Mortensen3f382cb2019-07-29 13:21:49 -0600496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700498 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600499 patch = self.PatchObject(commands, "BuildEbuildLogsTarball")
500 artifacts.BundleEbuildLogs(
501 self.sysroot_request, self.response, self.mock_call_config
502 )
503 patch.assert_not_called()
504 self.assertEqual(len(self.response.artifacts), 1)
505 self.assertEqual(
506 self.response.artifacts[0].path,
507 os.path.join(self.output_dir, "ebuild-logs.tar.gz"),
508 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 def testBundleEbuildLogs(self):
511 """BundleEbuildLogs calls cbuildbot/commands with correct args."""
512 bundle_ebuild_logs_tarball = self.PatchObject(
513 artifacts_svc,
514 "BundleEBuildLogsTarball",
515 return_value="ebuild-logs.tar.gz",
516 )
517 artifacts.BundleEbuildLogs(
518 self.sysroot_request, self.response, self.api_config
519 )
520 self.assertEqual(
521 [artifact.path for artifact in self.response.artifacts],
522 [os.path.join(self.output_dir, "ebuild-logs.tar.gz")],
523 )
524 self.assertEqual(
525 bundle_ebuild_logs_tarball.call_args_list,
526 [mock.call(mock.ANY, self.sysroot, self.output_dir)],
527 )
Michael Mortensen3f382cb2019-07-29 13:21:49 -0600528
Alex Klein1699fab2022-09-08 08:46:06 -0600529 def testBundleEbuildLogsNoLogs(self):
530 """BundleEbuildLogs dies when no logs found."""
531 self.PatchObject(commands, "BuildEbuildLogsTarball", return_value=None)
532 artifacts.BundleEbuildLogs(
533 self.sysroot_request, self.response, self.api_config
534 )
Alex Klein036833d2022-06-01 13:05:01 -0600535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 self.assertFalse(self.response.artifacts)
Evan Hernandez9a5d3122019-04-09 10:51:23 -0600537
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600538
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600539class BundleChromeOSConfigTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600540 """Unittests for BundleChromeOSConfig"""
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 def testValidateOnly(self):
543 """Quick check that a validate only call does not execute any logic."""
544 patch = self.PatchObject(artifacts_svc, "BundleChromeOSConfig")
545 artifacts.BundleChromeOSConfig(
546 self.sysroot_request, self.response, self.validate_only_config
547 )
548 patch.assert_not_called()
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700551 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600552 patch = self.PatchObject(artifacts_svc, "BundleChromeOSConfig")
553 artifacts.BundleChromeOSConfig(
554 self.sysroot_request, self.response, self.mock_call_config
555 )
556 patch.assert_not_called()
557 self.assertEqual(len(self.response.artifacts), 1)
558 self.assertEqual(
559 self.response.artifacts[0].path,
560 os.path.join(self.output_dir, "config.yaml"),
561 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700562
Alex Klein1699fab2022-09-08 08:46:06 -0600563 def testBundleChromeOSConfigSuccess(self):
564 """Test standard success case."""
565 bundle_chromeos_config = self.PatchObject(
566 artifacts_svc, "BundleChromeOSConfig", return_value="config.yaml"
567 )
568 artifacts.BundleChromeOSConfig(
569 self.sysroot_request, self.response, self.api_config
570 )
571 self.assertEqual(
572 [artifact.path for artifact in self.response.artifacts],
573 [os.path.join(self.output_dir, "config.yaml")],
574 )
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 self.assertEqual(
577 bundle_chromeos_config.call_args_list,
578 [mock.call(mock.ANY, self.sysroot, self.output_dir)],
579 )
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600580
Alex Klein1699fab2022-09-08 08:46:06 -0600581 def testBundleChromeOSConfigNoConfigFound(self):
582 """Empty results when the config payload isn't found."""
583 self.PatchObject(
584 artifacts_svc, "BundleChromeOSConfig", return_value=None
585 )
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 artifacts.BundleChromeOSConfig(
588 self.sysroot_request, self.response, self.api_config
589 )
590 self.assertFalse(self.response.artifacts)
Andrew Lamb67bd68f2019-08-15 09:09:15 -0600591
592
Alex Klein1699fab2022-09-08 08:46:06 -0600593class BundleTestUpdatePayloadsTest(
594 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
595):
596 """Unittests for BundleTestUpdatePayloads."""
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600597
Alex Klein1699fab2022-09-08 08:46:06 -0600598 def setUp(self):
599 self.source_root = os.path.join(self.tempdir, "cros")
600 osutils.SafeMakedirs(self.source_root)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600601
Alex Klein1699fab2022-09-08 08:46:06 -0600602 self.archive_root = os.path.join(self.tempdir, "output")
603 osutils.SafeMakedirs(self.archive_root)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600604
Alex Klein1699fab2022-09-08 08:46:06 -0600605 self.target = "target"
606 self.image_root = os.path.join(
607 self.source_root, "src/build/images/target/latest"
608 )
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600609
Alex Klein1699fab2022-09-08 08:46:06 -0600610 self.input_proto = artifacts_pb2.BundleRequest()
611 self.input_proto.build_target.name = self.target
612 self.input_proto.output_dir = self.archive_root
613 self.output_proto = artifacts_pb2.BundleResponse()
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600614
Alex Klein1699fab2022-09-08 08:46:06 -0600615 self.PatchObject(constants, "SOURCE_ROOT", new=self.source_root)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600616
Alex Klein1699fab2022-09-08 08:46:06 -0600617 def MockPayloads(image_path, archive_dir):
618 osutils.WriteFile(
619 os.path.join(archive_dir, "payload1.bin"), image_path
620 )
621 osutils.WriteFile(
622 os.path.join(archive_dir, "payload2.bin"), image_path
623 )
624 return [
625 os.path.join(archive_dir, "payload1.bin"),
626 os.path.join(archive_dir, "payload2.bin"),
627 ]
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600628
Alex Klein1699fab2022-09-08 08:46:06 -0600629 self.bundle_patch = self.PatchObject(
630 artifacts_svc, "BundleTestUpdatePayloads", side_effect=MockPayloads
631 )
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600632
Alex Klein1699fab2022-09-08 08:46:06 -0600633 def testValidateOnly(self):
634 """Quick check that a validate only call does not execute any logic."""
635 patch = self.PatchObject(artifacts_svc, "BundleTestUpdatePayloads")
636 artifacts.BundleTestUpdatePayloads(
637 self.input_proto, self.output_proto, self.validate_only_config
638 )
639 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600640
Alex Klein1699fab2022-09-08 08:46:06 -0600641 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700642 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600643 patch = self.PatchObject(artifacts_svc, "BundleTestUpdatePayloads")
644 artifacts.BundleTestUpdatePayloads(
645 self.input_proto, self.output_proto, self.mock_call_config
646 )
647 patch.assert_not_called()
648 self.assertEqual(len(self.output_proto.artifacts), 3)
649 self.assertEqual(
650 self.output_proto.artifacts[0].path,
651 os.path.join(self.archive_root, "payload1.bin"),
652 )
653 self.assertEqual(
654 self.output_proto.artifacts[1].path,
655 os.path.join(self.archive_root, "payload1.json"),
656 )
657 self.assertEqual(
658 self.output_proto.artifacts[2].path,
659 os.path.join(self.archive_root, "payload1.log"),
660 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700661
Alex Klein1699fab2022-09-08 08:46:06 -0600662 def testBundleTestUpdatePayloads(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700663 """BundleTestUpdatePayloads calls cbuildbot/commands correctly."""
Alex Klein1699fab2022-09-08 08:46:06 -0600664 image_path = os.path.join(self.image_root, constants.BASE_IMAGE_BIN)
665 osutils.WriteFile(image_path, "image!", makedirs=True)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600666
Alex Klein1699fab2022-09-08 08:46:06 -0600667 artifacts.BundleTestUpdatePayloads(
668 self.input_proto, self.output_proto, self.api_config
669 )
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600670
Alex Klein1699fab2022-09-08 08:46:06 -0600671 actual = [
672 os.path.relpath(artifact.path, self.archive_root)
673 for artifact in self.output_proto.artifacts
674 ]
675 expected = ["payload1.bin", "payload2.bin"]
676 self.assertCountEqual(actual, expected)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600677
Alex Klein1699fab2022-09-08 08:46:06 -0600678 actual = [
679 os.path.relpath(path, self.archive_root)
680 for path in osutils.DirectoryIterator(self.archive_root)
681 ]
682 self.assertCountEqual(actual, expected)
Evan Hernandezf388cbf2019-04-01 11:15:23 -0600683
Alex Klein1699fab2022-09-08 08:46:06 -0600684 def testBundleTestUpdatePayloadsNoImageDir(self):
685 """BundleTestUpdatePayloads dies if no image dir is found."""
686 # Intentionally do not write image directory.
687 artifacts.BundleTestUpdatePayloads(
688 self.input_proto, self.output_proto, self.api_config
689 )
690 self.assertFalse(self.output_proto.artifacts)
Evan Hernandez9f125ac2019-04-08 17:18:47 -0600691
Alex Klein1699fab2022-09-08 08:46:06 -0600692 def testBundleTestUpdatePayloadsNoImage(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700693 """BundleTestUpdatePayloads dies if no usable image found for target."""
Alex Klein1699fab2022-09-08 08:46:06 -0600694 # Intentionally do not write image, but create the directory.
695 osutils.SafeMakedirs(self.image_root)
696 with self.assertRaises(cros_build_lib.DieSystemExit):
697 artifacts.BundleTestUpdatePayloads(
698 self.input_proto, self.output_proto, self.api_config
699 )
Alex Klein6504eca2019-04-18 15:37:56 -0600700
701
Alex Klein1699fab2022-09-08 08:46:06 -0600702class BundleSimpleChromeArtifactsTest(
703 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
704):
705 """BundleSimpleChromeArtifacts tests."""
Alex Klein2275d692019-04-23 16:04:12 -0600706
Alex Klein1699fab2022-09-08 08:46:06 -0600707 def setUp(self):
708 self.chroot_dir = os.path.join(self.tempdir, "chroot_dir")
709 self.sysroot_path = "/sysroot"
710 self.sysroot_dir = os.path.join(self.chroot_dir, "sysroot")
711 osutils.SafeMakedirs(self.sysroot_dir)
712 self.output_dir = os.path.join(self.tempdir, "output_dir")
713 osutils.SafeMakedirs(self.output_dir)
Alex Klein2275d692019-04-23 16:04:12 -0600714
Alex Klein1699fab2022-09-08 08:46:06 -0600715 self.does_not_exist = os.path.join(self.tempdir, "does_not_exist")
Alex Klein2275d692019-04-23 16:04:12 -0600716
Alex Klein1699fab2022-09-08 08:46:06 -0600717 self.response = artifacts_pb2.BundleResponse()
Alex Klein231d2da2019-07-22 16:44:45 -0600718
Alex Klein1699fab2022-09-08 08:46:06 -0600719 def _GetRequest(
720 self,
721 chroot: Optional[str] = None,
722 sysroot: Optional[str] = None,
723 build_target: Optional[str] = None,
724 output_dir: Optional[str] = None,
725 ) -> artifacts_pb2.BundleRequest:
726 """Helper to create a request message instance.
Alex Klein2275d692019-04-23 16:04:12 -0600727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600729 chroot: The chroot path.
730 sysroot: The sysroot path.
731 build_target: The build target name.
732 output_dir: The output directory.
Alex Klein1699fab2022-09-08 08:46:06 -0600733 """
734 return artifacts_pb2.BundleRequest(
735 sysroot={"path": sysroot, "build_target": {"name": build_target}},
736 chroot={"path": chroot},
737 output_dir=output_dir,
738 )
Alex Klein2275d692019-04-23 16:04:12 -0600739
Alex Klein1699fab2022-09-08 08:46:06 -0600740 def testValidateOnly(self):
741 """Quick check that a validate only call does not execute any logic."""
742 patch = self.PatchObject(artifacts_svc, "BundleSimpleChromeArtifacts")
743 request = self._GetRequest(
744 chroot=self.chroot_dir,
745 sysroot=self.sysroot_path,
746 build_target="board",
747 output_dir=self.output_dir,
748 )
749 artifacts.BundleSimpleChromeArtifacts(
750 request, self.response, self.validate_only_config
751 )
752 patch.assert_not_called()
Alex Klein2275d692019-04-23 16:04:12 -0600753
Alex Klein1699fab2022-09-08 08:46:06 -0600754 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700755 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600756 patch = self.PatchObject(artifacts_svc, "BundleSimpleChromeArtifacts")
757 request = self._GetRequest(
758 chroot=self.chroot_dir,
759 sysroot=self.sysroot_path,
760 build_target="board",
761 output_dir=self.output_dir,
762 )
763 artifacts.BundleSimpleChromeArtifacts(
764 request, self.response, self.mock_call_config
765 )
766 patch.assert_not_called()
767 self.assertEqual(len(self.response.artifacts), 1)
768 self.assertEqual(
769 self.response.artifacts[0].path,
770 os.path.join(self.output_dir, "simple_chrome.txt"),
771 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 def testNoBuildTarget(self):
774 """Test no build target fails."""
775 request = self._GetRequest(
776 chroot=self.chroot_dir,
777 sysroot=self.sysroot_path,
778 output_dir=self.output_dir,
779 )
780 response = self.response
781 with self.assertRaises(cros_build_lib.DieSystemExit):
782 artifacts.BundleSimpleChromeArtifacts(
783 request, response, self.api_config
784 )
Alex Klein2275d692019-04-23 16:04:12 -0600785
Alex Klein1699fab2022-09-08 08:46:06 -0600786 def testNoSysroot(self):
787 """Test no sysroot fails."""
788 request = self._GetRequest(
789 build_target="board", output_dir=self.output_dir
790 )
791 response = self.response
792 with self.assertRaises(cros_build_lib.DieSystemExit):
793 artifacts.BundleSimpleChromeArtifacts(
794 request, response, self.api_config
795 )
Alex Klein2275d692019-04-23 16:04:12 -0600796
Alex Klein1699fab2022-09-08 08:46:06 -0600797 def testSysrootDoesNotExist(self):
798 """Test no sysroot fails."""
799 request = self._GetRequest(
800 build_target="board",
801 output_dir=self.output_dir,
802 sysroot=self.does_not_exist,
803 )
804 response = self.response
Alex Kleinb6847e22022-11-07 10:44:48 -0700805 artifacts.BundleSimpleChromeArtifacts(
806 request, response, self.api_config
807 )
808 self.assertFalse(self.response.artifacts)
Alex Klein2275d692019-04-23 16:04:12 -0600809
Alex Klein1699fab2022-09-08 08:46:06 -0600810 def testNoOutputDir(self):
811 """Test no output dir fails."""
812 request = self._GetRequest(
813 chroot=self.chroot_dir,
814 sysroot=self.sysroot_path,
815 build_target="board",
816 )
817 response = self.response
818 with self.assertRaises(cros_build_lib.DieSystemExit):
819 artifacts.BundleSimpleChromeArtifacts(
820 request, response, self.api_config
821 )
Alex Klein2275d692019-04-23 16:04:12 -0600822
Alex Klein1699fab2022-09-08 08:46:06 -0600823 def testOutputDirDoesNotExist(self):
824 """Test no output dir fails."""
825 request = self._GetRequest(
826 chroot=self.chroot_dir,
827 sysroot=self.sysroot_path,
828 build_target="board",
829 output_dir=self.does_not_exist,
830 )
831 response = self.response
832 with self.assertRaises(cros_build_lib.DieSystemExit):
833 artifacts.BundleSimpleChromeArtifacts(
834 request, response, self.api_config
835 )
Alex Klein2275d692019-04-23 16:04:12 -0600836
Alex Klein1699fab2022-09-08 08:46:06 -0600837 def testOutputHandling(self):
838 """Test response output."""
839 files = ["file1", "file2", "file3"]
840 expected_files = [os.path.join(self.output_dir, f) for f in files]
841 self.PatchObject(
842 artifacts_svc,
843 "BundleSimpleChromeArtifacts",
844 return_value=expected_files,
845 )
846 request = self._GetRequest(
847 chroot=self.chroot_dir,
848 sysroot=self.sysroot_path,
849 build_target="board",
850 output_dir=self.output_dir,
851 )
852 response = self.response
Alex Klein2275d692019-04-23 16:04:12 -0600853
Alex Klein1699fab2022-09-08 08:46:06 -0600854 artifacts.BundleSimpleChromeArtifacts(
855 request, response, self.api_config
856 )
Alex Klein2275d692019-04-23 16:04:12 -0600857
Alex Klein1699fab2022-09-08 08:46:06 -0600858 self.assertTrue(response.artifacts)
859 self.assertCountEqual(
860 expected_files, [a.path for a in response.artifacts]
861 )
Alex Klein2275d692019-04-23 16:04:12 -0600862
863
Alex Klein1699fab2022-09-08 08:46:06 -0600864class BundleVmFilesTest(
865 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
866):
867 """BuildVmFiles tests."""
Alex Klein6504eca2019-04-18 15:37:56 -0600868
Alex Klein1699fab2022-09-08 08:46:06 -0600869 def setUp(self):
870 self.output_dir = os.path.join(self.tempdir, "output")
871 osutils.SafeMakedirs(self.output_dir)
Alex Klein231d2da2019-07-22 16:44:45 -0600872
Alex Klein1699fab2022-09-08 08:46:06 -0600873 self.response = artifacts_pb2.BundleResponse()
Alex Klein231d2da2019-07-22 16:44:45 -0600874
Alex Klein1699fab2022-09-08 08:46:06 -0600875 def _GetInput(
876 self,
877 chroot: Optional[str] = None,
878 sysroot: Optional[str] = None,
879 test_results_dir: Optional[str] = None,
880 output_dir: Optional[str] = None,
881 ) -> artifacts_pb2.BundleVmFilesRequest:
882 """Helper to build out an input message instance.
Alex Klein6504eca2019-04-18 15:37:56 -0600883
Alex Klein1699fab2022-09-08 08:46:06 -0600884 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600885 chroot: The chroot path.
886 sysroot: The sysroot path relative to the chroot.
Alex Kleinab87ceb2023-01-24 12:00:51 -0700887 test_results_dir: The test results directory relative to the
888 sysroot.
Alex Klein611dddd2022-10-11 17:02:01 -0600889 output_dir: The directory where the results tarball should be saved.
Alex Klein1699fab2022-09-08 08:46:06 -0600890 """
891 return artifacts_pb2.BundleVmFilesRequest(
892 chroot={"path": chroot},
893 sysroot={"path": sysroot},
894 test_results_dir=test_results_dir,
895 output_dir=output_dir,
896 )
Alex Klein6504eca2019-04-18 15:37:56 -0600897
Alex Klein1699fab2022-09-08 08:46:06 -0600898 def testValidateOnly(self):
899 """Quick check that a validate only call does not execute any logic."""
900 patch = self.PatchObject(artifacts_svc, "BundleVmFiles")
901 in_proto = self._GetInput(
902 chroot="/chroot/dir",
903 sysroot="/build/board",
904 test_results_dir="/test/results",
905 output_dir=self.output_dir,
906 )
907 artifacts.BundleVmFiles(
908 in_proto, self.response, self.validate_only_config
909 )
910 patch.assert_not_called()
Alex Klein6504eca2019-04-18 15:37:56 -0600911
Alex Klein1699fab2022-09-08 08:46:06 -0600912 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700913 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600914 patch = self.PatchObject(artifacts_svc, "BundleVmFiles")
915 in_proto = self._GetInput(
916 chroot="/chroot/dir",
917 sysroot="/build/board",
918 test_results_dir="/test/results",
919 output_dir=self.output_dir,
920 )
921 artifacts.BundleVmFiles(in_proto, self.response, self.mock_call_config)
922 patch.assert_not_called()
923 self.assertEqual(len(self.response.artifacts), 1)
924 self.assertEqual(
925 self.response.artifacts[0].path,
926 os.path.join(self.output_dir, "f1.tar"),
927 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -0700928
Alex Klein1699fab2022-09-08 08:46:06 -0600929 def testChrootMissing(self):
930 """Test error handling for missing chroot."""
931 in_proto = self._GetInput(
932 sysroot="/build/board",
933 test_results_dir="/test/results",
934 output_dir=self.output_dir,
935 )
Alex Klein6504eca2019-04-18 15:37:56 -0600936
Alex Klein1699fab2022-09-08 08:46:06 -0600937 with self.assertRaises(cros_build_lib.DieSystemExit):
938 artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
Alex Klein6504eca2019-04-18 15:37:56 -0600939
Alex Klein1699fab2022-09-08 08:46:06 -0600940 def testTestResultsDirMissing(self):
941 """Test error handling for missing test results directory."""
942 in_proto = self._GetInput(
943 chroot="/chroot/dir",
944 sysroot="/build/board",
945 output_dir=self.output_dir,
946 )
Alex Klein6504eca2019-04-18 15:37:56 -0600947
Alex Klein1699fab2022-09-08 08:46:06 -0600948 with self.assertRaises(cros_build_lib.DieSystemExit):
949 artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
Alex Klein6504eca2019-04-18 15:37:56 -0600950
Alex Klein1699fab2022-09-08 08:46:06 -0600951 def testOutputDirMissing(self):
952 """Test error handling for missing output directory."""
953 in_proto = self._GetInput(
954 chroot="/chroot/dir",
955 sysroot="/build/board",
956 test_results_dir="/test/results",
957 )
Alex Klein6504eca2019-04-18 15:37:56 -0600958
Alex Klein1699fab2022-09-08 08:46:06 -0600959 with self.assertRaises(cros_build_lib.DieSystemExit):
960 artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600961
Alex Klein1699fab2022-09-08 08:46:06 -0600962 def testOutputDirDoesNotExist(self):
963 """Test error handling for output directory that does not exist."""
964 in_proto = self._GetInput(
965 chroot="/chroot/dir",
966 sysroot="/build/board",
967 output_dir=os.path.join(self.tempdir, "dne"),
968 test_results_dir="/test/results",
969 )
Alex Klein231d2da2019-07-22 16:44:45 -0600970
Alex Klein1699fab2022-09-08 08:46:06 -0600971 with self.assertRaises(cros_build_lib.DieSystemExit):
972 artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
Alex Klein6504eca2019-04-18 15:37:56 -0600973
Alex Klein1699fab2022-09-08 08:46:06 -0600974 def testValidCall(self):
975 """Test image dir building."""
976 in_proto = self._GetInput(
977 chroot="/chroot/dir",
978 sysroot="/build/board",
979 test_results_dir="/test/results",
980 output_dir=self.output_dir,
981 )
Alex Klein231d2da2019-07-22 16:44:45 -0600982
Alex Klein1699fab2022-09-08 08:46:06 -0600983 expected_files = ["/tmp/output/f1.tar", "/tmp/output/f2.tar"]
984 patch = self.PatchObject(
985 artifacts_svc, "BundleVmFiles", return_value=expected_files
986 )
Alex Klein6504eca2019-04-18 15:37:56 -0600987
Alex Klein1699fab2022-09-08 08:46:06 -0600988 artifacts.BundleVmFiles(in_proto, self.response, self.api_config)
Alex Klein6504eca2019-04-18 15:37:56 -0600989
Alex Klein1699fab2022-09-08 08:46:06 -0600990 patch.assert_called_with(mock.ANY, "/test/results", self.output_dir)
Alex Klein6504eca2019-04-18 15:37:56 -0600991
Alex Kleinab87ceb2023-01-24 12:00:51 -0700992 # Make sure we have artifacts, and that every artifact is an expected
993 # file.
Alex Klein1699fab2022-09-08 08:46:06 -0600994 self.assertTrue(self.response.artifacts)
995 for artifact in self.response.artifacts:
996 self.assertIn(artifact.path, expected_files)
997 expected_files.remove(artifact.path)
Alex Klein6504eca2019-04-18 15:37:56 -0600998
Alex Klein1699fab2022-09-08 08:46:06 -0600999 # Make sure we've seen all of the expected files.
1000 self.assertFalse(expected_files)
Alex Kleinb9d810b2019-07-01 12:38:02 -06001001
Tiancong Wang50b80a92019-08-01 14:46:15 -07001002
Alex Klein036833d2022-06-01 13:05:01 -06001003class ExportCpeReportTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -06001004 """ExportCpeReport tests."""
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001005
Alex Klein1699fab2022-09-08 08:46:06 -06001006 def testValidateOnly(self):
1007 """Quick check validate only calls don't execute."""
1008 patch = self.PatchObject(artifacts_svc, "GenerateCpeReport")
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001009
Alex Klein1699fab2022-09-08 08:46:06 -06001010 artifacts.ExportCpeReport(
1011 self.sysroot_request, self.response, self.validate_only_config
1012 )
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001013
Alex Klein1699fab2022-09-08 08:46:06 -06001014 patch.assert_not_called()
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001015
Alex Klein1699fab2022-09-08 08:46:06 -06001016 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -07001017 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -06001018 patch = self.PatchObject(artifacts_svc, "GenerateCpeReport")
Michael Mortensen2d6a2402019-11-26 13:40:40 -07001019
Alex Klein1699fab2022-09-08 08:46:06 -06001020 artifacts.ExportCpeReport(
1021 self.sysroot_request, self.response, self.mock_call_config
1022 )
Michael Mortensen2d6a2402019-11-26 13:40:40 -07001023
Alex Klein1699fab2022-09-08 08:46:06 -06001024 patch.assert_not_called()
1025 self.assertEqual(len(self.response.artifacts), 2)
1026 self.assertEqual(
1027 self.response.artifacts[0].path,
1028 os.path.join(self.output_dir, "cpe_report.txt"),
1029 )
1030 self.assertEqual(
1031 self.response.artifacts[1].path,
1032 os.path.join(self.output_dir, "cpe_warnings.txt"),
1033 )
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001034
Alex Klein1699fab2022-09-08 08:46:06 -06001035 def testSuccess(self):
1036 """Test success case."""
1037 expected = artifacts_svc.CpeResult(
1038 report="/output/report.json", warnings="/output/warnings.json"
1039 )
1040 self.PatchObject(
1041 artifacts_svc, "GenerateCpeReport", return_value=expected
1042 )
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001043
Alex Klein1699fab2022-09-08 08:46:06 -06001044 artifacts.ExportCpeReport(
1045 self.sysroot_request, self.response, self.api_config
1046 )
Alex Klein0b1cbfc2019-08-14 10:09:58 -06001047
Alex Klein1699fab2022-09-08 08:46:06 -06001048 for artifact in self.response.artifacts:
1049 self.assertIn(artifact.path, [expected.report, expected.warnings])
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001050
1051
1052class BundleGceTarballTest(BundleTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -06001053 """Unittests for BundleGceTarball."""
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001054
Alex Klein1699fab2022-09-08 08:46:06 -06001055 def testValidateOnly(self):
1056 """Check that a validate only call does not execute any logic."""
1057 patch = self.PatchObject(artifacts_svc, "BundleGceTarball")
1058 artifacts.BundleGceTarball(
1059 self.target_request, self.response, self.validate_only_config
1060 )
1061 patch.assert_not_called()
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001062
Alex Klein1699fab2022-09-08 08:46:06 -06001063 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -07001064 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -06001065 patch = self.PatchObject(artifacts_svc, "BundleGceTarball")
1066 artifacts.BundleGceTarball(
1067 self.target_request, self.response, self.mock_call_config
1068 )
1069 patch.assert_not_called()
1070 self.assertEqual(len(self.response.artifacts), 1)
1071 self.assertEqual(
1072 self.response.artifacts[0].path,
1073 os.path.join(self.output_dir, constants.TEST_IMAGE_GCE_TAR),
1074 )
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001075
Alex Klein1699fab2022-09-08 08:46:06 -06001076 def testBundleGceTarball(self):
1077 """BundleGceTarball calls cbuildbot/commands with correct args."""
1078 bundle_gce_tarball = self.PatchObject(
1079 artifacts_svc,
1080 "BundleGceTarball",
1081 return_value=os.path.join(
1082 self.output_dir, constants.TEST_IMAGE_GCE_TAR
1083 ),
1084 )
1085 self.PatchObject(os.path, "exists", return_value=True)
1086 artifacts.BundleGceTarball(
1087 self.target_request, self.response, self.api_config
1088 )
1089 self.assertEqual(
1090 [artifact.path for artifact in self.response.artifacts],
1091 [os.path.join(self.output_dir, constants.TEST_IMAGE_GCE_TAR)],
1092 )
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001093
Alex Klein1699fab2022-09-08 08:46:06 -06001094 latest = os.path.join(
1095 self.source_root, "src/build/images/target/latest"
1096 )
1097 self.assertEqual(
1098 bundle_gce_tarball.call_args_list,
1099 [mock.call(self.output_dir, latest)],
1100 )
Shao-Chuan Leea44dddc2020-10-30 17:16:55 +09001101
Alex Klein1699fab2022-09-08 08:46:06 -06001102 def testBundleGceTarballNoImageDir(self):
1103 """BundleGceTarball dies when image dir does not exist."""
1104 self.PatchObject(os.path, "exists", return_value=False)
1105 with self.assertRaises(cros_build_lib.DieSystemExit):
1106 artifacts.BundleGceTarball(
1107 self.target_request, self.response, self.api_config
1108 )
Greg Edelstondc941072021-08-11 12:32:30 -06001109
Greg Edelstondc941072021-08-11 12:32:30 -06001110
Alex Klein1699fab2022-09-08 08:46:06 -06001111class FetchMetadataTestCase(
1112 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
1113):
1114 """Unittests for FetchMetadata."""
Greg Edelstondc941072021-08-11 12:32:30 -06001115
Alex Klein1699fab2022-09-08 08:46:06 -06001116 sysroot_path = "/build/coral"
1117 chroot_name = "chroot"
Greg Edelstondc941072021-08-11 12:32:30 -06001118
Alex Klein1699fab2022-09-08 08:46:06 -06001119 def setUp(self):
1120 self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
1121 self.chroot_path = os.path.join(self.tempdir, "chroot")
1122 pathlib.Path(self.chroot_path).touch()
1123 self.expected_filepaths = [
1124 os.path.join(self.chroot_path, fp)
1125 for fp in (
1126 "build/coral/usr/local/build/autotest/autotest_metadata.pb",
1127 "build/coral/usr/share/tast/metadata/local/cros.pb",
1128 "build/coral/build/share/tast/metadata/local/crosint.pb",
1129 "usr/share/tast/metadata/remote/cros.pb",
1130 )
1131 ]
1132 self.PatchObject(cros_build_lib, "AssertOutsideChroot")
Greg Edelstondc941072021-08-11 12:32:30 -06001133
Alex Klein1699fab2022-09-08 08:46:06 -06001134 def createFetchMetadataRequest(
1135 self, use_sysroot_path=True, use_chroot=True
1136 ):
1137 """Construct a FetchMetadataRequest for use in test cases."""
1138 request = artifacts_pb2.FetchMetadataRequest()
1139 if use_sysroot_path:
1140 request.sysroot.path = self.sysroot_path
1141 if use_chroot:
1142 request.chroot.path = self.chroot_path
1143 return request
Greg Edelstondc941072021-08-11 12:32:30 -06001144
Alex Klein1699fab2022-09-08 08:46:06 -06001145 def testValidateOnly(self):
1146 """Check that a validate only call does not execute any logic."""
1147 patch = self.PatchObject(controller_util, "ParseSysroot")
1148 request = self.createFetchMetadataRequest()
1149 response = artifacts_pb2.FetchMetadataResponse()
1150 artifacts.FetchMetadata(request, response, self.validate_only_config)
1151 patch.assert_not_called()
Greg Edelstondc941072021-08-11 12:32:30 -06001152
Alex Klein1699fab2022-09-08 08:46:06 -06001153 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -07001154 """Test a mock call does not execute logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -06001155 patch = self.PatchObject(controller_util, "ParseSysroot")
1156 request = self.createFetchMetadataRequest()
1157 response = artifacts_pb2.FetchMetadataResponse()
1158 artifacts.FetchMetadata(request, response, self.mock_call_config)
1159 patch.assert_not_called()
1160 self.assertGreater(len(response.filepaths), 0)
Greg Edelstondc941072021-08-11 12:32:30 -06001161
Alex Klein1699fab2022-09-08 08:46:06 -06001162 def testNoSysrootPath(self):
1163 """Check that a request with no sysroot.path results in failure."""
1164 request = self.createFetchMetadataRequest(use_sysroot_path=False)
1165 response = artifacts_pb2.FetchMetadataResponse()
1166 with self.assertRaises(cros_build_lib.DieSystemExit):
1167 artifacts.FetchMetadata(request, response, self.api_config)
Greg Edelstondc941072021-08-11 12:32:30 -06001168
Alex Klein1699fab2022-09-08 08:46:06 -06001169 def testNoChroot(self):
1170 """Check that a request with no chroot results in failure."""
1171 request = self.createFetchMetadataRequest(use_chroot=False)
1172 response = artifacts_pb2.FetchMetadataResponse()
1173 with self.assertRaises(cros_build_lib.DieSystemExit):
1174 artifacts.FetchMetadata(request, response, self.api_config)
1175
1176 def testSuccess(self):
1177 """Check that a well-formed request yields the expected results."""
1178 request = self.createFetchMetadataRequest(use_chroot=True)
1179 response = artifacts_pb2.FetchMetadataResponse()
1180 artifacts.FetchMetadata(request, response, self.api_config)
1181 actual_filepaths = [fp.path.path for fp in response.filepaths]
1182 self.assertEqual(
1183 sorted(actual_filepaths), sorted(self.expected_filepaths)
1184 )
1185 self.assertTrue(
1186 all(
1187 fp.path.location == common_pb2.Path.OUTSIDE
1188 for fp in response.filepaths
1189 )
1190 )
Jack Neus26b94672022-10-27 17:33:21 +00001191
1192
1193class GetTest(cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin):
1194 """Get function tests."""
1195
1196 def setUp(self):
1197 self.sysroot_path = "/build/target"
1198 self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
1199
1200 def _InputProto(self):
1201 """Helper to build an input proto instance."""
Alex Kleinab87ceb2023-01-24 12:00:51 -07001202 # pylint: disable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001203 return artifacts_pb2.GetRequest(
1204 sysroot=sysroot_pb2.Sysroot(path=self.sysroot_path),
1205 artifact_info=common_pb2.ArtifactsByService(
1206 sysroot=common_pb2.ArtifactsByService.Sysroot(
1207 output_artifacts=[
1208 common_pb2.ArtifactsByService.Sysroot.ArtifactInfo(
1209 artifact_types=[
1210 common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT
1211 ]
1212 )
1213 ],
1214 ),
1215 image=common_pb2.ArtifactsByService.Image(
1216 output_artifacts=[
1217 common_pb2.ArtifactsByService.Image.ArtifactInfo(
1218 artifact_types=[
1219 common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS
1220 ]
1221 )
1222 ],
1223 ),
1224 test=common_pb2.ArtifactsByService.Test(
1225 output_artifacts=[
1226 common_pb2.ArtifactsByService.Test.ArtifactInfo(
1227 artifact_types=[
1228 common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL
1229 ]
1230 )
1231 ],
1232 ),
1233 ),
1234 result_path=common_pb2.ResultPath(
1235 path=common_pb2.Path(path=str(self.tempdir))
1236 ),
1237 )
Alex Kleinab87ceb2023-01-24 12:00:51 -07001238 # pylint: enable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001239
1240 def _OutputProto(self):
1241 """Helper to build an output proto instance."""
1242 return artifacts_pb2.GetResponse()
1243
1244 def testSuccess(self):
1245 """Test Get."""
Alex Kleinab87ceb2023-01-24 12:00:51 -07001246 # pylint: disable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001247 image_mock = self.PatchObject(
1248 image_controller,
1249 "GetArtifacts",
1250 return_value=[
1251 {
1252 "paths": ["/foo/bar/license_credits.html"],
1253 "type": common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS,
1254 }
1255 ],
1256 )
1257 sysroot_mock = self.PatchObject(
1258 sysroot_controller,
1259 "GetArtifacts",
1260 return_value=[
1261 {
1262 "type": common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT,
1263 "failed": True,
1264 "failure_reason": "Bad data!",
1265 }
1266 ],
1267 )
1268 test_mock = self.PatchObject(
1269 test_controller,
1270 "GetArtifacts",
1271 return_value=[
1272 {
1273 "paths": ["/foo/bar/hwqual.tar.xz"],
1274 "type": common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL,
1275 }
1276 ],
1277 )
Alex Kleinab87ceb2023-01-24 12:00:51 -07001278 # pylint: enable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001279
1280 in_proto = self._InputProto()
1281 out_proto = self._OutputProto()
1282 artifacts.Get(
1283 in_proto,
1284 out_proto,
1285 self.api_config,
1286 )
1287
1288 image_mock.assert_called_once()
1289 sysroot_mock.assert_called_once()
1290 test_mock.assert_called_once()
1291
Alex Kleinab87ceb2023-01-24 12:00:51 -07001292 # pylint: disable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001293 expected = common_pb2.UploadedArtifactsByService(
1294 sysroot=common_pb2.UploadedArtifactsByService.Sysroot(
1295 artifacts=[
1296 common_pb2.UploadedArtifactsByService.Sysroot.ArtifactPaths(
1297 artifact_type=common_pb2.ArtifactsByService.Sysroot.ArtifactType.FUZZER_SYSROOT,
1298 failed=True,
1299 failure_reason="Bad data!",
1300 )
1301 ]
1302 ),
1303 image=common_pb2.UploadedArtifactsByService.Image(
1304 artifacts=[
1305 common_pb2.UploadedArtifactsByService.Image.ArtifactPaths(
1306 artifact_type=common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS,
1307 paths=[
1308 common_pb2.Path(
1309 path="/foo/bar/license_credits.html",
1310 location=common_pb2.Path.OUTSIDE,
1311 )
1312 ],
1313 )
1314 ]
1315 ),
1316 test=common_pb2.UploadedArtifactsByService.Test(
1317 artifacts=[
1318 common_pb2.UploadedArtifactsByService.Test.ArtifactPaths(
1319 artifact_type=common_pb2.ArtifactsByService.Test.ArtifactType.HWQUAL,
1320 paths=[
1321 common_pb2.Path(
1322 path="/foo/bar/hwqual.tar.xz",
1323 location=common_pb2.Path.OUTSIDE,
1324 )
1325 ],
1326 )
1327 ]
1328 ),
1329 )
Alex Kleinab87ceb2023-01-24 12:00:51 -07001330 # pylint: enable=line-too-long
Jack Neus26b94672022-10-27 17:33:21 +00001331 self.assertEqual(out_proto.artifacts, expected)