blob: 2a9bc8aed77b05c59e604b76f8896d1e6b820e62 [file] [log] [blame]
Alex Kleina2e42c42019-04-17 16:13:19 -06001# Copyright 2019 The Chromium OS 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
5"""The test controller tests."""
6
Alex Klein9f915782020-02-14 23:15:09 +00007import contextlib
Lizzy Presland4feb2372022-01-20 05:16:30 +00008import datetime
Mike Frysingeref94e4c2020-02-10 23:59:54 -05009import os
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Mike Frysingeref94e4c2020-02-10 23:59:54 -050011
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040012from chromite.third_party.google.protobuf import json_format
13
Alex Klein231d2da2019-07-22 16:44:45 -060014from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060015from chromite.api import controller
Lizzy Presland4feb2372022-01-20 05:16:30 +000016from chromite.api.controller import controller_util
Alex Kleina2e42c42019-04-17 16:13:19 -060017from chromite.api.controller import test as test_controller
18from chromite.api.gen.chromite.api import test_pb2
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040019from chromite.api.gen.chromiumos import common_pb2
Sean McAllister3834fef2021-10-08 15:45:18 -060020from chromite.api.gen.chromiumos.build.api import container_metadata_pb2
Jack Neusc9707c32021-07-23 21:48:54 +000021from chromite.lib import build_target_lib
Evan Hernandeze1e05d32019-07-19 12:32:18 -060022from chromite.lib import chroot_lib
Alex Kleina2e42c42019-04-17 16:13:19 -060023from chromite.lib import cros_build_lib
24from chromite.lib import cros_test_lib
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060025from chromite.lib import image_lib
Alex Kleina2e42c42019-04-17 16:13:19 -060026from chromite.lib import osutils
David Wellingc1433c22021-06-25 16:29:48 +000027from chromite.lib import sysroot_lib
Alex Klein18a60af2020-06-11 12:08:47 -060028from chromite.lib.parser import package_info
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060029from chromite.scripts import cros_set_lsb_release
30from chromite.service import test as test_service
Mike Frysingere652ba12019-09-08 00:57:43 -040031from chromite.utils import key_value_store
Alex Kleina2e42c42019-04-17 16:13:19 -060032
33
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -070034class DebugInfoTestTest(cros_test_lib.MockTempDirTestCase,
35 api_config.ApiConfigMixin):
36 """Tests for the DebugInfoTest function."""
37
38 def setUp(self):
39 self.board = 'board'
40 self.chroot_path = os.path.join(self.tempdir, 'chroot')
41 self.sysroot_path = '/build/board'
42 self.full_sysroot_path = os.path.join(self.chroot_path,
43 self.sysroot_path.lstrip(os.sep))
44 osutils.SafeMakedirs(self.full_sysroot_path)
45
46 def _GetInput(self, sysroot_path=None, build_target=None):
47 """Helper to build an input message instance."""
48 proto = test_pb2.DebugInfoTestRequest()
49 if sysroot_path:
50 proto.sysroot.path = sysroot_path
51 if build_target:
52 proto.sysroot.build_target.name = build_target
53 return proto
54
55 def _GetOutput(self):
56 """Helper to get an empty output message instance."""
57 return test_pb2.DebugInfoTestResponse()
58
59 def testValidateOnly(self):
60 """Sanity check that a validate only call does not execute any logic."""
61 patch = self.PatchObject(test_service, 'DebugInfoTest')
62 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
63 test_controller.DebugInfoTest(input_msg, self._GetOutput(),
64 self.validate_only_config)
65 patch.assert_not_called()
66
Michael Mortensen85d38402019-12-12 09:50:29 -070067 def testMockError(self):
68 """Test mock error call does not execute any logic, returns error."""
69 patch = self.PatchObject(test_service, 'DebugInfoTest')
70
71 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
72 rc = test_controller.DebugInfoTest(input_msg, self._GetOutput(),
73 self.mock_error_config)
74 patch.assert_not_called()
75 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
76
77 def testMockCall(self):
78 """Test mock call does not execute any logic, returns success."""
79 patch = self.PatchObject(test_service, 'DebugInfoTest')
80
81 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
82 rc = test_controller.DebugInfoTest(input_msg, self._GetOutput(),
83 self.mock_call_config)
84 patch.assert_not_called()
85 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
86
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -070087 def testNoBuildTargetNoSysrootFails(self):
88 """Test missing build target name and sysroot path fails."""
89 input_msg = self._GetInput()
90 output_msg = self._GetOutput()
91 with self.assertRaises(cros_build_lib.DieSystemExit):
92 test_controller.DebugInfoTest(input_msg, output_msg, self.api_config)
93
94 def testDebugInfoTest(self):
95 """Call DebugInfoTest with valid sysroot_path."""
96 request = self._GetInput(sysroot_path=self.full_sysroot_path)
97
98 test_controller.DebugInfoTest(request, self._GetOutput(), self.api_config)
99
100
Alex Klein231d2da2019-07-22 16:44:45 -0600101class BuildTargetUnitTestTest(cros_test_lib.MockTempDirTestCase,
102 api_config.ApiConfigMixin):
Alex Kleina2e42c42019-04-17 16:13:19 -0600103 """Tests for the UnitTest function."""
104
Lizzy Presland4feb2372022-01-20 05:16:30 +0000105 def setUp(self):
106 # Set up portage log directory.
107 self.sysroot = os.path.join(self.tempdir, 'build', 'board')
108 osutils.SafeMakedirs(self.sysroot)
109 self.target_sysroot = sysroot_lib.Sysroot(self.sysroot)
110 self.portage_dir = os.path.join(self.tempdir, 'portage_logdir')
111 self.PatchObject(
112 sysroot_lib.Sysroot, 'portage_logdir', new=self.portage_dir)
113 osutils.SafeMakedirs(self.portage_dir)
114
Navil Perezc0b29a82020-07-07 14:17:48 +0000115 def _GetInput(self,
116 board=None,
117 result_path=None,
118 chroot_path=None,
119 cache_dir=None,
120 empty_sysroot=None,
121 packages=None,
Alex Kleinb64e5f82020-09-23 10:55:31 -0600122 blocklist=None):
Alex Kleina2e42c42019-04-17 16:13:19 -0600123 """Helper to build an input message instance."""
Navil Perezc0b29a82020-07-07 14:17:48 +0000124 formatted_packages = []
125 for pkg in packages or []:
126 formatted_packages.append({
127 'category': pkg.category,
128 'package_name': pkg.package
129 })
Alex Kleinb64e5f82020-09-23 10:55:31 -0600130 formatted_blocklist = []
131 for pkg in blocklist or []:
132 formatted_blocklist.append({'category': pkg.category,
Alex Kleinf2674462019-05-16 16:47:24 -0600133 'package_name': pkg.package})
134
Alex Kleina2e42c42019-04-17 16:13:19 -0600135 return test_pb2.BuildTargetUnitTestRequest(
136 build_target={'name': board}, result_path=result_path,
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600137 chroot={'path': chroot_path, 'cache_dir': cache_dir},
Alex Kleinf2674462019-05-16 16:47:24 -0600138 flags={'empty_sysroot': empty_sysroot},
Alex Klein64ac34c2020-09-23 10:21:33 -0600139 packages=formatted_packages,
Alex Klein157caf42021-07-01 14:36:43 -0600140 package_blocklist=formatted_blocklist,
Alex Kleina2e42c42019-04-17 16:13:19 -0600141 )
142
143 def _GetOutput(self):
144 """Helper to get an empty output message instance."""
145 return test_pb2.BuildTargetUnitTestResponse()
146
Lizzy Presland4feb2372022-01-20 05:16:30 +0000147 def _CreatePortageLogFile(self, log_path, pkg_info, timestamp):
148 """Creates a log file for testing for individual packages built by Portage.
149
150 Args:
151 log_path (pathlike): the PORTAGE_LOGDIR path
152 pkg_info (PackageInfo): name components for log file.
153 timestamp (datetime): timestamp used to name the file.
154 """
155 path = os.path.join(log_path,
156 f'{pkg_info.category}:{pkg_info.pvr}:' \
157 f'{timestamp.strftime("%Y%m%d-%H%M%S")}.log')
158 osutils.WriteFile(path,
159 f'Test log file for package {pkg_info.category}/'
160 f'{pkg_info.package} written to {path}')
161 return path
162
Alex Klein231d2da2019-07-22 16:44:45 -0600163 def testValidateOnly(self):
164 """Sanity check that a validate only call does not execute any logic."""
165 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
166
167 input_msg = self._GetInput(board='board', result_path=self.tempdir)
168 test_controller.BuildTargetUnitTest(input_msg, self._GetOutput(),
169 self.validate_only_config)
170 patch.assert_not_called()
171
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700172 def testMockCall(self):
173 """Test that a mock call does not execute logic, returns mocked value."""
174 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
175
176 input_msg = self._GetInput(board='board', result_path=self.tempdir)
177 response = self._GetOutput()
178 test_controller.BuildTargetUnitTest(input_msg, response,
179 self.mock_call_config)
180 patch.assert_not_called()
181 self.assertEqual(response.tarball_path,
182 os.path.join(input_msg.result_path, 'unit_tests.tar'))
183
184 def testMockError(self):
Michael Mortensen85d38402019-12-12 09:50:29 -0700185 """Test that a mock error does not execute logic, returns error."""
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700186 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
187
188 input_msg = self._GetInput(board='board', result_path=self.tempdir)
189 response = self._GetOutput()
190 rc = test_controller.BuildTargetUnitTest(input_msg, response,
191 self.mock_error_config)
192 patch.assert_not_called()
193 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
194 self.assertTrue(response.failed_packages)
195 self.assertEqual(response.failed_packages[0].category, 'foo')
196 self.assertEqual(response.failed_packages[0].package_name, 'bar')
197 self.assertEqual(response.failed_packages[1].category, 'cat')
198 self.assertEqual(response.failed_packages[1].package_name, 'pkg')
199
Alex Klein64ac34c2020-09-23 10:21:33 -0600200 def testInvalidPackageFails(self):
201 """Test missing result path fails."""
202 # Missing result_path.
203 pkg = package_info.PackageInfo(package='bar')
204 input_msg = self._GetInput(board='board', result_path=self.tempdir,
205 packages=[pkg])
206 output_msg = self._GetOutput()
207 with self.assertRaises(cros_build_lib.DieSystemExit):
208 test_controller.BuildTargetUnitTest(input_msg, output_msg,
209 self.api_config)
210
Alex Kleina2e42c42019-04-17 16:13:19 -0600211 def testPackageBuildFailure(self):
212 """Test handling of raised BuildPackageFailure."""
213 tempdir = osutils.TempDir(base_dir=self.tempdir)
214 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
215
Lizzy Presland4feb2372022-01-20 05:16:30 +0000216 pkgs = ['cat/pkg-1.0-r1', 'foo/bar-2.0-r1']
217 cpvrs = [package_info.parse(pkg) for pkg in pkgs]
Alex Kleina2e42c42019-04-17 16:13:19 -0600218 expected = [('cat', 'pkg'), ('foo', 'bar')]
Lizzy Presland4feb2372022-01-20 05:16:30 +0000219 new_logs = {}
220 for i, pkg in enumerate(pkgs):
221 self._CreatePortageLogFile(self.portage_dir, cpvrs[i],
222 datetime.datetime(2021, 6, 9, 13, 37, 0))
223 new_logs[pkg] = self._CreatePortageLogFile(self.portage_dir, cpvrs[i],
224 datetime.datetime(2021, 6, 9,
225 16, 20, 0))
Alex Kleina2e42c42019-04-17 16:13:19 -0600226
Alex Klein38c7d9e2019-05-08 09:31:19 -0600227 result = test_service.BuildTargetUnitTestResult(1, None)
Alex Kleinea0c89e2021-09-09 15:17:35 -0600228 result.failed_pkgs = [package_info.parse(p) for p in pkgs]
Alex Klein38c7d9e2019-05-08 09:31:19 -0600229 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
Alex Kleina2e42c42019-04-17 16:13:19 -0600230
231 input_msg = self._GetInput(board='board', result_path=self.tempdir)
232 output_msg = self._GetOutput()
233
Alex Klein231d2da2019-07-22 16:44:45 -0600234 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg,
235 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600236
Alex Klein8cb365a2019-05-15 16:24:53 -0600237 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600238 self.assertTrue(output_msg.failed_packages)
Lizzy Presland4feb2372022-01-20 05:16:30 +0000239 self.assertTrue(output_msg.failed_package_data)
240 # TODO(b/206514844): remove when field is deleted
Alex Kleina2e42c42019-04-17 16:13:19 -0600241 failed = []
242 for pi in output_msg.failed_packages:
243 failed.append((pi.category, pi.package_name))
Mike Frysinger678735c2019-09-28 18:23:28 -0400244 self.assertCountEqual(expected, failed)
Alex Kleina2e42c42019-04-17 16:13:19 -0600245
Lizzy Presland4feb2372022-01-20 05:16:30 +0000246 failed_with_logs = []
247 for data in output_msg.failed_package_data:
248 failed_with_logs.append((data.name.category, data.name.package_name))
249 package = controller_util.deserialize_package_info(data.name)
250 self.assertEqual(data.log_path.path, new_logs[package.cpvr])
251 self.assertCountEqual(expected, failed_with_logs)
252
253
Alex Kleina2e42c42019-04-17 16:13:19 -0600254 def testOtherBuildScriptFailure(self):
255 """Test build script failure due to non-package emerge error."""
256 tempdir = osutils.TempDir(base_dir=self.tempdir)
257 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
258
Alex Klein38c7d9e2019-05-08 09:31:19 -0600259 result = test_service.BuildTargetUnitTestResult(1, None)
260 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
Alex Kleina2e42c42019-04-17 16:13:19 -0600261
Alex Kleinf2674462019-05-16 16:47:24 -0600262 pkgs = ['foo/bar', 'cat/pkg']
Alex Kleinb64e5f82020-09-23 10:55:31 -0600263 blocklist = [package_info.SplitCPV(p, strict=False) for p in pkgs]
Alex Klein2e91e522022-01-14 09:22:03 -0700264 input_msg = self._GetInput(board='board', empty_sysroot=True,
265 blocklist=blocklist)
Alex Kleina2e42c42019-04-17 16:13:19 -0600266 output_msg = self._GetOutput()
267
Alex Klein231d2da2019-07-22 16:44:45 -0600268 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg,
269 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600270
Alex Klein8cb365a2019-05-15 16:24:53 -0600271 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600272 self.assertFalse(output_msg.failed_packages)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600273
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700274 def testBuildTargetUnitTest(self):
275 """Test BuildTargetUnitTest successful call."""
Navil Perezc0b29a82020-07-07 14:17:48 +0000276 pkgs = ['foo/bar', 'cat/pkg']
Alex Klein18a60af2020-06-11 12:08:47 -0600277 packages = [package_info.SplitCPV(p, strict=False) for p in pkgs]
Alex Klein2e91e522022-01-14 09:22:03 -0700278 input_msg = self._GetInput(board='board', packages=packages)
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700279
280 result = test_service.BuildTargetUnitTestResult(0, None)
281 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
282
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700283 response = self._GetOutput()
284 test_controller.BuildTargetUnitTest(input_msg, response,
285 self.api_config)
Alex Klein2e91e522022-01-14 09:22:03 -0700286 self.assertFalse(response.failed_packages)
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700287
Evan Hernandez4e388a52019-05-01 12:16:33 -0600288
Sean McAllister17eed8d2021-09-21 10:41:16 -0600289class DockerConstraintsTest(cros_test_lib.MockTestCase):
290 """Tests for Docker argument constraints."""
291
292 def assertValid(self, output):
293 return output is None
294
295 def assertInvalid(self, output):
296 return not self.assertValid(output)
297
298 def testValidDockerTag(self):
299 """Check logic for validating docker tag format."""
300 # pylint: disable=protected-access
301
302 invalid_tags = [
303 '.invalid-tag',
304 '-invalid-tag',
305 'invalid-tag;',
306 'invalid'*100,
307 ]
308
309 for tag in invalid_tags:
310 self.assertInvalid(test_controller._ValidDockerTag(tag))
311
312 valid_tags = [
313 'valid-tag',
314 'valid-tag-',
315 'valid.tag.',
316 ]
317
318 for tag in valid_tags:
319 self.assertValid(test_controller._ValidDockerTag(tag))
320
321
322 def testValidDockerLabelKey(self):
323 """Check logic for validating docker label key format."""
324 # pylint: disable=protected-access
325
326 invalid_keys = [
327 'Invalid-keY',
328 'Invalid-key',
329 'invalid-keY',
330 'iNVALID-KEy',
331 'invalid_key',
332 'invalid-key;',
333 ]
334
335 for key in invalid_keys:
336 self.assertInvalid(test_controller._ValidDockerLabelKey(key))
337
338 valid_keys = [
339 'chromeos.valid-key',
340 'chromeos.valid-key-2',
341 ]
342
343 for key in valid_keys:
344 self.assertValid(test_controller._ValidDockerLabelKey(key))
345
346
Sean McAllister3834fef2021-10-08 15:45:18 -0600347class BuildTestServiceContainers(cros_test_lib.RunCommandTempDirTestCase,
David Wellingc1433c22021-06-25 16:29:48 +0000348 api_config.ApiConfigMixin):
C Shapiro91af1ce2021-06-17 12:42:09 -0500349 """Tests for the BuildTestServiceContainers function."""
350
351 def setUp(self):
352 self.request = test_pb2.BuildTestServiceContainersRequest(
353 chroot={'path': '/path/to/chroot'},
354 build_target={'name': 'build_target'},
David Wellingc1433c22021-06-25 16:29:48 +0000355 version='R93-14033.0.0',
C Shapiro91af1ce2021-06-17 12:42:09 -0500356 )
357
C Shapiro91af1ce2021-06-17 12:42:09 -0500358 def testSuccess(self):
359 """Check passing case with mocked cros_build_lib.run."""
Sean McAllister3834fef2021-10-08 15:45:18 -0600360
361 def ContainerMetadata():
362 """Return mocked ContainerImageInfo proto"""
363 metadata = container_metadata_pb2.ContainerImageInfo()
364 metadata.repository.hostname = 'gcr.io'
365 metadata.repository.project = 'chromeos-bot'
366 metadata.name = 'random-container-name'
367 metadata.digest = (
368 '09b730f8b6a862f9c2705cb3acf3554563325f5fca5c784bf5c98beb2e56f6db')
369 metadata.tags[:] = [
370 'staging-cq-amd64-generic.R96-1.2.3',
371 '8834106026340379089',
372 ]
373 return metadata
374
375 def WriteContainerMetadata(path):
376 """Write json formatted metadata to the given file."""
377 osutils.WriteFile(
378 path,
379 json_format.MessageToJson(ContainerMetadata()),
380 )
381
382 # Write out mocked container metadata to a temporary file.
383 output_path = os.path.join(self.tempdir, 'metadata.jsonpb')
384 self.rc.SetDefaultCmdResult(
385 returncode=0,
386 side_effect=lambda *_, **__: WriteContainerMetadata(output_path)
387 )
388
389 # Patch TempDir so that we always use this test's directory.
390 self.PatchObject(osutils.TempDir, '__enter__', return_value=self.tempdir)
C Shapiro91af1ce2021-06-17 12:42:09 -0500391
392 response = test_pb2.BuildTestServiceContainersResponse()
393 test_controller.BuildTestServiceContainers(
394 self.request,
395 response,
396 self.api_config)
Sean McAllister3834fef2021-10-08 15:45:18 -0600397
398 self.assertTrue(self.rc.called)
C Shapiro91af1ce2021-06-17 12:42:09 -0500399 for result in response.results:
400 self.assertEqual(result.WhichOneof('result'), 'success')
Sean McAllister3834fef2021-10-08 15:45:18 -0600401 self.assertEqual(result.success.image_info, ContainerMetadata())
C Shapiro91af1ce2021-06-17 12:42:09 -0500402
C Shapiro91af1ce2021-06-17 12:42:09 -0500403 def testFailure(self):
404 """Check failure case with mocked cros_build_lib.run."""
405 patch = self.PatchObject(
406 cros_build_lib, 'run',
407 return_value=cros_build_lib.CommandResult(returncode=1))
408
409 response = test_pb2.BuildTestServiceContainersResponse()
410 test_controller.BuildTestServiceContainers(
411 self.request,
412 response,
413 self.api_config)
414 patch.assert_called()
415 for result in response.results:
Andres Calderon Jaramillod74c5ea2022-05-03 18:16:45 +0000416 self.assertEqual(result.WhichOneof('result'), 'failure')
C Shapiro91af1ce2021-06-17 12:42:09 -0500417
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -0700418class ChromiteUnitTestTest(cros_test_lib.MockTestCase,
419 api_config.ApiConfigMixin):
420 """Tests for the ChromiteInfoTest function."""
421
422 def setUp(self):
423 self.board = 'board'
424 self.chroot_path = '/path/to/chroot'
425
426 def _GetInput(self, chroot_path=None):
427 """Helper to build an input message instance."""
428 proto = test_pb2.ChromiteUnitTestRequest(
429 chroot={'path': chroot_path},
430 )
431 return proto
432
433 def _GetOutput(self):
434 """Helper to get an empty output message instance."""
435 return test_pb2.ChromiteUnitTestResponse()
436
437 def testValidateOnly(self):
438 """Sanity check that a validate only call does not execute any logic."""
439 patch = self.PatchObject(cros_build_lib, 'run')
440
441 input_msg = self._GetInput(chroot_path=self.chroot_path)
442 test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
443 self.validate_only_config)
444 patch.assert_not_called()
445
Michael Mortensen7a860eb2019-12-03 20:25:15 -0700446 def testMockError(self):
447 """Test mock error call does not execute any logic, returns error."""
448 patch = self.PatchObject(cros_build_lib, 'run')
449
450 input_msg = self._GetInput(chroot_path=self.chroot_path)
451 rc = test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
452 self.mock_error_config)
453 patch.assert_not_called()
454 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
455
456 def testMockCall(self):
457 """Test mock call does not execute any logic, returns success."""
458 patch = self.PatchObject(cros_build_lib, 'run')
459
460 input_msg = self._GetInput(chroot_path=self.chroot_path)
461 rc = test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
462 self.mock_call_config)
463 patch.assert_not_called()
464 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
465
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -0700466 def testChromiteUnitTest(self):
467 """Call ChromiteUnitTest with mocked cros_build_lib.run."""
468 request = self._GetInput(chroot_path=self.chroot_path)
469 patch = self.PatchObject(
470 cros_build_lib, 'run',
471 return_value=cros_build_lib.CommandResult(returncode=0))
472
473 test_controller.ChromiteUnitTest(request, self._GetOutput(),
474 self.api_config)
475 patch.assert_called_once()
476
477
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600478class CrosSigningTestTest(cros_test_lib.RunCommandTestCase,
479 api_config.ApiConfigMixin):
480 """CrosSigningTest tests."""
481
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700482 def setUp(self):
483 self.chroot_path = '/path/to/chroot'
484
485 def _GetInput(self, chroot_path=None):
486 """Helper to build an input message instance."""
487 proto = test_pb2.CrosSigningTestRequest(
488 chroot={'path': chroot_path},
489 )
490 return proto
491
492 def _GetOutput(self):
493 """Helper to get an empty output message instance."""
494 return test_pb2.CrosSigningTestResponse()
495
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600496 def testValidateOnly(self):
497 """Sanity check that a validate only call does not execute any logic."""
498 test_controller.CrosSigningTest(None, None, self.validate_only_config)
499 self.assertFalse(self.rc.call_count)
500
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700501 def testMockCall(self):
502 """Test mock call does not execute any logic, returns success."""
503 rc = test_controller.CrosSigningTest(None, None, self.mock_call_config)
504 self.assertFalse(self.rc.call_count)
505 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
506
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700507 def testCrosSigningTest(self):
508 """Call CrosSigningTest with mocked cros_build_lib.run."""
509 request = self._GetInput(chroot_path=self.chroot_path)
510 patch = self.PatchObject(
511 cros_build_lib, 'run',
512 return_value=cros_build_lib.CommandResult(returncode=0))
513
514 test_controller.CrosSigningTest(request, self._GetOutput(),
515 self.api_config)
516 patch.assert_called_once()
517
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600518
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600519class SimpleChromeWorkflowTestTest(cros_test_lib.MockTestCase,
520 api_config.ApiConfigMixin):
521 """Test the SimpleChromeWorkflowTest endpoint."""
522
523 @staticmethod
524 def _Output():
525 return test_pb2.SimpleChromeWorkflowTestResponse()
526
David Wellingc1433c22021-06-25 16:29:48 +0000527 def _Input(self,
528 sysroot_path=None,
529 build_target=None,
530 chrome_root=None,
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600531 goma_config=None):
532 proto = test_pb2.SimpleChromeWorkflowTestRequest()
533 if sysroot_path:
534 proto.sysroot.path = sysroot_path
535 if build_target:
536 proto.sysroot.build_target.name = build_target
537 if chrome_root:
538 proto.chrome_root = chrome_root
539 if goma_config:
540 proto.goma_config = goma_config
541 return proto
542
543 def setUp(self):
544 self.chrome_path = 'path/to/chrome'
545 self.sysroot_dir = 'build/board'
546 self.build_target = 'amd64'
547 self.mock_simple_chrome_workflow_test = self.PatchObject(
548 test_service, 'SimpleChromeWorkflowTest')
549
550 def testMissingBuildTarget(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700551 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600552 input_proto = self._Input(build_target=None, sysroot_path='/sysroot/dir',
553 chrome_root='/chrome/path')
554 with self.assertRaises(cros_build_lib.DieSystemExit):
555 test_controller.SimpleChromeWorkflowTest(input_proto, None,
556 self.api_config)
557
558 def testMissingSysrootPath(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700559 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600560 input_proto = self._Input(build_target='board', sysroot_path=None,
561 chrome_root='/chrome/path')
562 with self.assertRaises(cros_build_lib.DieSystemExit):
563 test_controller.SimpleChromeWorkflowTest(input_proto, None,
564 self.api_config)
565
566 def testMissingChromeRoot(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700567 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600568 input_proto = self._Input(build_target='board', sysroot_path='/sysroot/dir',
569 chrome_root=None)
570 with self.assertRaises(cros_build_lib.DieSystemExit):
571 test_controller.SimpleChromeWorkflowTest(input_proto, None,
572 self.api_config)
573
574 def testSimpleChromeWorkflowTest(self):
575 """Call SimpleChromeWorkflowTest with valid args and temp dir."""
576 request = self._Input(sysroot_path='sysroot_path', build_target='board',
577 chrome_root='/path/to/chrome')
578 response = self._Output()
579
580 test_controller.SimpleChromeWorkflowTest(request, response, self.api_config)
581 self.mock_simple_chrome_workflow_test.assert_called()
582
583 def testValidateOnly(self):
584 request = self._Input(sysroot_path='sysroot_path', build_target='board',
585 chrome_root='/path/to/chrome')
586 test_controller.SimpleChromeWorkflowTest(request, self._Output(),
587 self.validate_only_config)
588 self.mock_simple_chrome_workflow_test.assert_not_called()
589
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700590 def testMockCall(self):
591 """Test mock call does not execute any logic, returns success."""
592 patch = self.mock_simple_chrome_workflow_test = self.PatchObject(
593 test_service, 'SimpleChromeWorkflowTest')
594
595 request = self._Input(sysroot_path='sysroot_path', build_target='board',
596 chrome_root='/path/to/chrome')
597 rc = test_controller.SimpleChromeWorkflowTest(request, self._Output(),
598 self.mock_call_config)
599 patch.assert_not_called()
600 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
601
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600602
Alex Klein231d2da2019-07-22 16:44:45 -0600603class VmTestTest(cros_test_lib.RunCommandTestCase, api_config.ApiConfigMixin):
Evan Hernandez4e388a52019-05-01 12:16:33 -0600604 """Test the VmTest endpoint."""
605
606 def _GetInput(self, **kwargs):
607 values = dict(
608 build_target=common_pb2.BuildTarget(name='target'),
Alex Klein311b8022019-06-05 16:00:07 -0600609 vm_path=common_pb2.Path(path='/path/to/image.bin',
610 location=common_pb2.Path.INSIDE),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600611 test_harness=test_pb2.VmTestRequest.TAST,
612 vm_tests=[test_pb2.VmTestRequest.VmTest(pattern='suite')],
613 ssh_options=test_pb2.VmTestRequest.SshOptions(
Alex Klein231d2da2019-07-22 16:44:45 -0600614 port=1234, private_key_path={'path': '/path/to/id_rsa',
Alex Kleinaa705412019-06-04 15:00:30 -0600615 'location': common_pb2.Path.INSIDE}),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600616 )
617 values.update(kwargs)
618 return test_pb2.VmTestRequest(**values)
619
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700620 def _Output(self):
621 return test_pb2.VmTestResponse()
622
Alex Klein231d2da2019-07-22 16:44:45 -0600623 def testValidateOnly(self):
624 """Sanity check that a validate only call does not execute any logic."""
625 test_controller.VmTest(self._GetInput(), None, self.validate_only_config)
626 self.assertEqual(0, self.rc.call_count)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600627
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700628 def testMockCall(self):
629 """Test mock call does not execute any logic."""
630 patch = self.PatchObject(cros_build_lib, 'run')
631
632 request = self._GetInput()
633 response = self._Output()
634 # VmTest does not return a value, checking mocked value is flagged by lint.
635 test_controller.VmTest(request, response, self.mock_call_config)
636 patch.assert_not_called()
637
Evan Hernandez4e388a52019-05-01 12:16:33 -0600638 def testTastAllOptions(self):
639 """Test VmTest for Tast with all options set."""
Alex Klein231d2da2019-07-22 16:44:45 -0600640 test_controller.VmTest(self._GetInput(), None, self.api_config)
641 self.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700642 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600643 '--board', 'target',
644 '--image-path', '/path/to/image.bin',
645 '--tast', 'suite',
646 '--ssh-port', '1234',
647 '--private-key', '/path/to/id_rsa',
648 ])
649
650 def testAutotestAllOptions(self):
651 """Test VmTest for Autotest with all options set."""
652 input_proto = self._GetInput(test_harness=test_pb2.VmTestRequest.AUTOTEST)
Alex Klein231d2da2019-07-22 16:44:45 -0600653 test_controller.VmTest(input_proto, None, self.api_config)
654 self.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700655 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600656 '--board', 'target',
657 '--image-path', '/path/to/image.bin',
658 '--autotest', 'suite',
659 '--ssh-port', '1234',
660 '--private-key', '/path/to/id_rsa',
Greg Edelstondcb0e912020-08-31 11:09:40 -0600661 '--test_that-args=--allow-chrome-crashes',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600662 ])
663
664 def testMissingBuildTarget(self):
665 """Test VmTest dies when build_target not set."""
666 input_proto = self._GetInput(build_target=None)
667 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600668 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600669
670 def testMissingVmImage(self):
671 """Test VmTest dies when vm_image not set."""
Alex Klein311b8022019-06-05 16:00:07 -0600672 input_proto = self._GetInput(vm_path=None)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600673 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600674 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600675
676 def testMissingTestHarness(self):
677 """Test VmTest dies when test_harness not specified."""
678 input_proto = self._GetInput(
679 test_harness=test_pb2.VmTestRequest.UNSPECIFIED)
680 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600681 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600682
683 def testMissingVmTests(self):
684 """Test VmTest dies when vm_tests not set."""
685 input_proto = self._GetInput(vm_tests=[])
686 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600687 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600688
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700689 def testVmTest(self):
690 """Call VmTest with valid args and temp dir."""
691 request = self._GetInput()
692 response = self._Output()
693 patch = self.PatchObject(
694 cros_build_lib, 'run',
695 return_value=cros_build_lib.CommandResult(returncode=0))
696
697 test_controller.VmTest(request, response, self.api_config)
698 patch.assert_called()
699
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600700
Alex Klein231d2da2019-07-22 16:44:45 -0600701class MoblabVmTestTest(cros_test_lib.MockTestCase, api_config.ApiConfigMixin):
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600702 """Test the MoblabVmTest endpoint."""
703
704 @staticmethod
705 def _Payload(path):
706 return test_pb2.MoblabVmTestRequest.Payload(
707 path=common_pb2.Path(path=path))
708
709 @staticmethod
710 def _Output():
711 return test_pb2.MoblabVmTestResponse()
712
713 def _Input(self):
714 return test_pb2.MoblabVmTestRequest(
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600715 chroot=common_pb2.Chroot(path=self.chroot_dir),
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600716 image_payload=self._Payload(self.image_payload_dir),
717 cache_payloads=[self._Payload(self.autotest_payload_dir)])
718
719 def setUp(self):
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600720 self.chroot_dir = '/chroot'
721 self.chroot_tmp_dir = '/chroot/tmp'
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600722 self.image_payload_dir = '/payloads/image'
723 self.autotest_payload_dir = '/payloads/autotest'
724 self.builder = 'moblab-generic-vm/R12-3.4.5-67.890'
725 self.image_cache_dir = '/mnt/moblab/cache'
726 self.image_mount_dir = '/mnt/image'
727
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600728 self.PatchObject(chroot_lib.Chroot, 'tempdir', osutils.TempDir)
Evan Hernandez655e8042019-06-13 12:50:44 -0600729
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600730 self.mock_create_moblab_vms = self.PatchObject(
731 test_service, 'CreateMoblabVm')
732 self.mock_prepare_moblab_vm_image_cache = self.PatchObject(
733 test_service, 'PrepareMoblabVmImageCache',
734 return_value=self.image_cache_dir)
735 self.mock_run_moblab_vm_tests = self.PatchObject(
736 test_service, 'RunMoblabVmTest')
737 self.mock_validate_moblab_vm_tests = self.PatchObject(
738 test_service, 'ValidateMoblabVmTest')
739
740 @contextlib.contextmanager
Alex Klein38c7d9e2019-05-08 09:31:19 -0600741 def MockLoopbackPartitions(*_args, **_kwargs):
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600742 mount = mock.MagicMock()
Evan Hernandez40ee7452019-06-13 12:51:43 -0600743 mount.Mount.return_value = [self.image_mount_dir]
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600744 yield mount
Alex Klein231d2da2019-07-22 16:44:45 -0600745
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600746 self.PatchObject(image_lib, 'LoopbackPartitions', MockLoopbackPartitions)
747
Alex Klein231d2da2019-07-22 16:44:45 -0600748 def testValidateOnly(self):
749 """Sanity check that a validate only call does not execute any logic."""
750 test_controller.MoblabVmTest(self._Input(), self._Output(),
751 self.validate_only_config)
752 self.mock_create_moblab_vms.assert_not_called()
753
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700754 def testMockCall(self):
755 """Test mock call does not execute any logic."""
756 patch = self.PatchObject(key_value_store, 'LoadFile')
757
758 # MoblabVmTest does not return a value, checking mocked value is flagged by
759 # lint.
760 test_controller.MoblabVmTest(self._Input(), self._Output(),
761 self.mock_call_config)
762 patch.assert_not_called()
763
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600764 def testImageContainsBuilder(self):
765 """MoblabVmTest calls service with correct args."""
766 request = self._Input()
767 response = self._Output()
768
769 self.PatchObject(
Mike Frysingere652ba12019-09-08 00:57:43 -0400770 key_value_store, 'LoadFile',
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600771 return_value={cros_set_lsb_release.LSB_KEY_BUILDER_PATH: self.builder})
772
Alex Klein231d2da2019-07-22 16:44:45 -0600773 test_controller.MoblabVmTest(request, response, self.api_config)
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600774
775 self.assertEqual(
776 self.mock_create_moblab_vms.call_args_list,
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600777 [mock.call(mock.ANY, self.chroot_dir, self.image_payload_dir)])
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600778 self.assertEqual(
779 self.mock_prepare_moblab_vm_image_cache.call_args_list,
780 [mock.call(mock.ANY, self.builder, [self.autotest_payload_dir])])
781 self.assertEqual(
782 self.mock_run_moblab_vm_tests.call_args_list,
Evan Hernandez655e8042019-06-13 12:50:44 -0600783 [mock.call(mock.ANY, mock.ANY, self.builder, self.image_cache_dir,
784 mock.ANY)])
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600785 self.assertEqual(
786 self.mock_validate_moblab_vm_tests.call_args_list,
787 [mock.call(mock.ANY)])
788
789 def testImageMissingBuilder(self):
790 """MoblabVmTest dies when builder path not found in lsb-release."""
791 request = self._Input()
792 response = self._Output()
793
Mike Frysingere652ba12019-09-08 00:57:43 -0400794 self.PatchObject(key_value_store, 'LoadFile', return_value={})
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600795
796 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600797 test_controller.MoblabVmTest(request, response, self.api_config)
David Wellingc1433c22021-06-25 16:29:48 +0000798
799
800class GetArtifactsTest(cros_test_lib.MockTempDirTestCase):
801 """Test GetArtifacts."""
802
803 CODE_COVERAGE_LLVM_ARTIFACT_TYPE = (
804 common_pb2.ArtifactsByService.Test.ArtifactType.CODE_COVERAGE_LLVM_JSON
805 )
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600806 UNIT_TEST_ARTIFACT_TYPE = (
807 common_pb2.ArtifactsByService.Test.ArtifactType.UNIT_TESTS
808 )
David Wellingc1433c22021-06-25 16:29:48 +0000809
810 def setUp(self):
811 """Set up the class for tests."""
812 chroot_dir = os.path.join(self.tempdir, 'chroot')
813 osutils.SafeMakedirs(chroot_dir)
814 osutils.SafeMakedirs(os.path.join(chroot_dir, 'tmp'))
815 self.chroot = chroot_lib.Chroot(chroot_dir)
816
817 sysroot_path = os.path.join(chroot_dir, 'build', 'board')
818 osutils.SafeMakedirs(sysroot_path)
819 self.sysroot = sysroot_lib.Sysroot(sysroot_path)
820
Jack Neusc9707c32021-07-23 21:48:54 +0000821 self.build_target = build_target_lib.BuildTarget('board')
822
David Wellingc1433c22021-06-25 16:29:48 +0000823 def testReturnsEmptyListWhenNoOutputArtifactsProvided(self):
824 """Test empty list is returned when there are no output_artifacts."""
825 result = test_controller.GetArtifacts(
826 common_pb2.ArtifactsByService.Test(output_artifacts=[]),
Jack Neusc9707c32021-07-23 21:48:54 +0000827 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000828
829 self.assertEqual(len(result), 0)
830
831 def testShouldCallBundleCodeCoverageLlvmJsonForEachValidArtifact(self):
832 """Test BundleCodeCoverageLlvmJson is called on each valid artifact."""
Sean McAllister17eed8d2021-09-21 10:41:16 -0600833 BundleCodeCoverageLlvmJson_mock = (
834 self.PatchObject(
835 test_service,
836 'BundleCodeCoverageLlvmJson',
837 return_value='test'))
David Wellingc1433c22021-06-25 16:29:48 +0000838
839 test_controller.GetArtifacts(
840 common_pb2.ArtifactsByService.Test(output_artifacts=[
841 # Valid
842 common_pb2.ArtifactsByService.Test.ArtifactInfo(
843 artifact_types=[
844 self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE
845 ]
846 ),
847
848 # Invalid
849 common_pb2.ArtifactsByService.Test.ArtifactInfo(
850 artifact_types=[
851 common_pb2.ArtifactsByService.Test.ArtifactType.UNIT_TESTS
852 ]
853 ),
854 ]),
Jack Neusc9707c32021-07-23 21:48:54 +0000855 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000856
857 BundleCodeCoverageLlvmJson_mock.assert_called_once()
858
859 def testShouldReturnValidResult(self):
860 """Test result contains paths and code_coverage_llvm_json type."""
861 self.PatchObject(test_service, 'BundleCodeCoverageLlvmJson',
Sean McAllister17eed8d2021-09-21 10:41:16 -0600862 return_value='test')
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600863 self.PatchObject(test_service, 'BuildTargetUnitTestTarball',
Sean McAllister17eed8d2021-09-21 10:41:16 -0600864 return_value='unit_tests.tar')
David Wellingc1433c22021-06-25 16:29:48 +0000865
866 result = test_controller.GetArtifacts(
867 common_pb2.ArtifactsByService.Test(output_artifacts=[
868 # Valid
869 common_pb2.ArtifactsByService.Test.ArtifactInfo(
870 artifact_types=[
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600871 self.UNIT_TEST_ARTIFACT_TYPE
872 ]
873 ),
874 common_pb2.ArtifactsByService.Test.ArtifactInfo(
875 artifact_types=[
David Wellingc1433c22021-06-25 16:29:48 +0000876 self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE
877 ]
878 ),
879 ]),
Jack Neusc9707c32021-07-23 21:48:54 +0000880 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000881
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600882 self.assertEqual(result[0]['paths'], ['unit_tests.tar'])
883 self.assertEqual(result[0]['type'], self.UNIT_TEST_ARTIFACT_TYPE)
884 self.assertEqual(result[1]['paths'], ['test'])
885 self.assertEqual(result[1]['type'], self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE)