blob: 0f2f118426c9afb887381a9fef5787a555b66d9a [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
Mike Frysingeref94e4c2020-02-10 23:59:54 -05008import os
Mike Frysinger166fea02021-02-12 05:30:33 -05009from unittest import mock
Mike Frysingeref94e4c2020-02-10 23:59:54 -050010
Alex Klein231d2da2019-07-22 16:44:45 -060011from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060012from chromite.api import controller
Alex Kleina2e42c42019-04-17 16:13:19 -060013from chromite.api.controller import test as test_controller
Evan Hernandez4e388a52019-05-01 12:16:33 -060014from chromite.api.gen.chromiumos import common_pb2
Alex Kleina2e42c42019-04-17 16:13:19 -060015from chromite.api.gen.chromite.api import test_pb2
Sean McAllister3834fef2021-10-08 15:45:18 -060016from chromite.api.gen.chromiumos.build.api import container_metadata_pb2
Jack Neusc9707c32021-07-23 21:48:54 +000017from chromite.lib import build_target_lib
Evan Hernandeze1e05d32019-07-19 12:32:18 -060018from chromite.lib import chroot_lib
Alex Kleina2e42c42019-04-17 16:13:19 -060019from chromite.lib import cros_build_lib
20from chromite.lib import cros_test_lib
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060021from chromite.lib import image_lib
Alex Kleina2e42c42019-04-17 16:13:19 -060022from chromite.lib import osutils
David Wellingc1433c22021-06-25 16:29:48 +000023from chromite.lib import sysroot_lib
Alex Klein18a60af2020-06-11 12:08:47 -060024from chromite.lib.parser import package_info
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -060025from chromite.scripts import cros_set_lsb_release
26from chromite.service import test as test_service
Andrew Lamb763e3be2021-07-27 17:22:02 -060027from chromite.third_party.google.protobuf import json_format
Mike Frysingere652ba12019-09-08 00:57:43 -040028from chromite.utils import key_value_store
Alex Kleina2e42c42019-04-17 16:13:19 -060029
30
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -070031class DebugInfoTestTest(cros_test_lib.MockTempDirTestCase,
32 api_config.ApiConfigMixin):
33 """Tests for the DebugInfoTest function."""
34
35 def setUp(self):
36 self.board = 'board'
37 self.chroot_path = os.path.join(self.tempdir, 'chroot')
38 self.sysroot_path = '/build/board'
39 self.full_sysroot_path = os.path.join(self.chroot_path,
40 self.sysroot_path.lstrip(os.sep))
41 osutils.SafeMakedirs(self.full_sysroot_path)
42
43 def _GetInput(self, sysroot_path=None, build_target=None):
44 """Helper to build an input message instance."""
45 proto = test_pb2.DebugInfoTestRequest()
46 if sysroot_path:
47 proto.sysroot.path = sysroot_path
48 if build_target:
49 proto.sysroot.build_target.name = build_target
50 return proto
51
52 def _GetOutput(self):
53 """Helper to get an empty output message instance."""
54 return test_pb2.DebugInfoTestResponse()
55
56 def testValidateOnly(self):
57 """Sanity check that a validate only call does not execute any logic."""
58 patch = self.PatchObject(test_service, 'DebugInfoTest')
59 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
60 test_controller.DebugInfoTest(input_msg, self._GetOutput(),
61 self.validate_only_config)
62 patch.assert_not_called()
63
Michael Mortensen85d38402019-12-12 09:50:29 -070064 def testMockError(self):
65 """Test mock error call does not execute any logic, returns error."""
66 patch = self.PatchObject(test_service, 'DebugInfoTest')
67
68 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
69 rc = test_controller.DebugInfoTest(input_msg, self._GetOutput(),
70 self.mock_error_config)
71 patch.assert_not_called()
72 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
73
74 def testMockCall(self):
75 """Test mock call does not execute any logic, returns success."""
76 patch = self.PatchObject(test_service, 'DebugInfoTest')
77
78 input_msg = self._GetInput(sysroot_path=self.full_sysroot_path)
79 rc = test_controller.DebugInfoTest(input_msg, self._GetOutput(),
80 self.mock_call_config)
81 patch.assert_not_called()
82 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
83
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -070084 def testNoBuildTargetNoSysrootFails(self):
85 """Test missing build target name and sysroot path fails."""
86 input_msg = self._GetInput()
87 output_msg = self._GetOutput()
88 with self.assertRaises(cros_build_lib.DieSystemExit):
89 test_controller.DebugInfoTest(input_msg, output_msg, self.api_config)
90
91 def testDebugInfoTest(self):
92 """Call DebugInfoTest with valid sysroot_path."""
93 request = self._GetInput(sysroot_path=self.full_sysroot_path)
94
95 test_controller.DebugInfoTest(request, self._GetOutput(), self.api_config)
96
97
Alex Klein231d2da2019-07-22 16:44:45 -060098class BuildTargetUnitTestTest(cros_test_lib.MockTempDirTestCase,
99 api_config.ApiConfigMixin):
Alex Kleina2e42c42019-04-17 16:13:19 -0600100 """Tests for the UnitTest function."""
101
Navil Perezc0b29a82020-07-07 14:17:48 +0000102 def _GetInput(self,
103 board=None,
104 result_path=None,
105 chroot_path=None,
106 cache_dir=None,
107 empty_sysroot=None,
108 packages=None,
Alex Kleinb64e5f82020-09-23 10:55:31 -0600109 blocklist=None):
Alex Kleina2e42c42019-04-17 16:13:19 -0600110 """Helper to build an input message instance."""
Navil Perezc0b29a82020-07-07 14:17:48 +0000111 formatted_packages = []
112 for pkg in packages or []:
113 formatted_packages.append({
114 'category': pkg.category,
115 'package_name': pkg.package
116 })
Alex Kleinb64e5f82020-09-23 10:55:31 -0600117 formatted_blocklist = []
118 for pkg in blocklist or []:
119 formatted_blocklist.append({'category': pkg.category,
Alex Kleinf2674462019-05-16 16:47:24 -0600120 'package_name': pkg.package})
121
Alex Kleina2e42c42019-04-17 16:13:19 -0600122 return test_pb2.BuildTargetUnitTestRequest(
123 build_target={'name': board}, result_path=result_path,
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600124 chroot={'path': chroot_path, 'cache_dir': cache_dir},
Alex Kleinf2674462019-05-16 16:47:24 -0600125 flags={'empty_sysroot': empty_sysroot},
Alex Klein64ac34c2020-09-23 10:21:33 -0600126 packages=formatted_packages,
Alex Klein157caf42021-07-01 14:36:43 -0600127 package_blocklist=formatted_blocklist,
Alex Kleina2e42c42019-04-17 16:13:19 -0600128 )
129
130 def _GetOutput(self):
131 """Helper to get an empty output message instance."""
132 return test_pb2.BuildTargetUnitTestResponse()
133
Alex Klein231d2da2019-07-22 16:44:45 -0600134 def testValidateOnly(self):
135 """Sanity check that a validate only call does not execute any logic."""
136 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
137
138 input_msg = self._GetInput(board='board', result_path=self.tempdir)
139 test_controller.BuildTargetUnitTest(input_msg, self._GetOutput(),
140 self.validate_only_config)
141 patch.assert_not_called()
142
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700143 def testMockCall(self):
144 """Test that a mock call does not execute logic, returns mocked value."""
145 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
146
147 input_msg = self._GetInput(board='board', result_path=self.tempdir)
148 response = self._GetOutput()
149 test_controller.BuildTargetUnitTest(input_msg, response,
150 self.mock_call_config)
151 patch.assert_not_called()
152 self.assertEqual(response.tarball_path,
153 os.path.join(input_msg.result_path, 'unit_tests.tar'))
154
155 def testMockError(self):
Michael Mortensen85d38402019-12-12 09:50:29 -0700156 """Test that a mock error does not execute logic, returns error."""
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700157 patch = self.PatchObject(test_service, 'BuildTargetUnitTest')
158
159 input_msg = self._GetInput(board='board', result_path=self.tempdir)
160 response = self._GetOutput()
161 rc = test_controller.BuildTargetUnitTest(input_msg, response,
162 self.mock_error_config)
163 patch.assert_not_called()
164 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
165 self.assertTrue(response.failed_packages)
166 self.assertEqual(response.failed_packages[0].category, 'foo')
167 self.assertEqual(response.failed_packages[0].package_name, 'bar')
168 self.assertEqual(response.failed_packages[1].category, 'cat')
169 self.assertEqual(response.failed_packages[1].package_name, 'pkg')
170
Alex Kleina2e42c42019-04-17 16:13:19 -0600171 def testNoArgumentFails(self):
172 """Test no arguments fails."""
173 input_msg = self._GetInput()
174 output_msg = self._GetOutput()
175 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600176 test_controller.BuildTargetUnitTest(input_msg, output_msg,
177 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600178
Alex Kleina2e42c42019-04-17 16:13:19 -0600179 def testNoResultPathFails(self):
180 """Test missing result path fails."""
181 # Missing result_path.
182 input_msg = self._GetInput(board='board')
183 output_msg = self._GetOutput()
184 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600185 test_controller.BuildTargetUnitTest(input_msg, output_msg,
186 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600187
Alex Klein64ac34c2020-09-23 10:21:33 -0600188 def testInvalidPackageFails(self):
189 """Test missing result path fails."""
190 # Missing result_path.
191 pkg = package_info.PackageInfo(package='bar')
192 input_msg = self._GetInput(board='board', result_path=self.tempdir,
193 packages=[pkg])
194 output_msg = self._GetOutput()
195 with self.assertRaises(cros_build_lib.DieSystemExit):
196 test_controller.BuildTargetUnitTest(input_msg, output_msg,
197 self.api_config)
198
Alex Kleina2e42c42019-04-17 16:13:19 -0600199 def testPackageBuildFailure(self):
200 """Test handling of raised BuildPackageFailure."""
201 tempdir = osutils.TempDir(base_dir=self.tempdir)
202 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
203
204 pkgs = ['cat/pkg', 'foo/bar']
205 expected = [('cat', 'pkg'), ('foo', 'bar')]
Alex Kleina2e42c42019-04-17 16:13:19 -0600206
Alex Klein38c7d9e2019-05-08 09:31:19 -0600207 result = test_service.BuildTargetUnitTestResult(1, None)
Alex Kleinea0c89e2021-09-09 15:17:35 -0600208 result.failed_pkgs = [package_info.parse(p) for p in pkgs]
Alex Klein38c7d9e2019-05-08 09:31:19 -0600209 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
Alex Kleina2e42c42019-04-17 16:13:19 -0600210
211 input_msg = self._GetInput(board='board', result_path=self.tempdir)
212 output_msg = self._GetOutput()
213
Alex Klein231d2da2019-07-22 16:44:45 -0600214 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg,
215 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600216
Alex Klein8cb365a2019-05-15 16:24:53 -0600217 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600218 self.assertTrue(output_msg.failed_packages)
219 failed = []
220 for pi in output_msg.failed_packages:
221 failed.append((pi.category, pi.package_name))
Mike Frysinger678735c2019-09-28 18:23:28 -0400222 self.assertCountEqual(expected, failed)
Alex Kleina2e42c42019-04-17 16:13:19 -0600223
224 def testOtherBuildScriptFailure(self):
225 """Test build script failure due to non-package emerge error."""
226 tempdir = osutils.TempDir(base_dir=self.tempdir)
227 self.PatchObject(osutils, 'TempDir', return_value=tempdir)
228
Alex Klein38c7d9e2019-05-08 09:31:19 -0600229 result = test_service.BuildTargetUnitTestResult(1, None)
230 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
Alex Kleina2e42c42019-04-17 16:13:19 -0600231
Alex Kleinf2674462019-05-16 16:47:24 -0600232 pkgs = ['foo/bar', 'cat/pkg']
Alex Kleinb64e5f82020-09-23 10:55:31 -0600233 blocklist = [package_info.SplitCPV(p, strict=False) for p in pkgs]
Alex Kleinfa6ebdc2019-05-10 10:57:31 -0600234 input_msg = self._GetInput(board='board', result_path=self.tempdir,
Alex Kleinb64e5f82020-09-23 10:55:31 -0600235 empty_sysroot=True, blocklist=blocklist)
Alex Kleina2e42c42019-04-17 16:13:19 -0600236 output_msg = self._GetOutput()
237
Alex Klein231d2da2019-07-22 16:44:45 -0600238 rc = test_controller.BuildTargetUnitTest(input_msg, output_msg,
239 self.api_config)
Alex Kleina2e42c42019-04-17 16:13:19 -0600240
Alex Klein8cb365a2019-05-15 16:24:53 -0600241 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Alex Kleina2e42c42019-04-17 16:13:19 -0600242 self.assertFalse(output_msg.failed_packages)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600243
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700244 def testBuildTargetUnitTest(self):
245 """Test BuildTargetUnitTest successful call."""
Navil Perezc0b29a82020-07-07 14:17:48 +0000246 pkgs = ['foo/bar', 'cat/pkg']
Alex Klein18a60af2020-06-11 12:08:47 -0600247 packages = [package_info.SplitCPV(p, strict=False) for p in pkgs]
Navil Perezc0b29a82020-07-07 14:17:48 +0000248 input_msg = self._GetInput(
249 board='board', result_path=self.tempdir, packages=packages)
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700250
251 result = test_service.BuildTargetUnitTestResult(0, None)
252 self.PatchObject(test_service, 'BuildTargetUnitTest', return_value=result)
253
254 tarball_result = os.path.join(input_msg.result_path, 'unit_tests.tar')
255 self.PatchObject(test_service, 'BuildTargetUnitTestTarball',
256 return_value=tarball_result)
257
258 response = self._GetOutput()
259 test_controller.BuildTargetUnitTest(input_msg, response,
260 self.api_config)
261 self.assertEqual(response.tarball_path,
262 os.path.join(input_msg.result_path, 'unit_tests.tar'))
263
Evan Hernandez4e388a52019-05-01 12:16:33 -0600264
Sean McAllister17eed8d2021-09-21 10:41:16 -0600265class DockerConstraintsTest(cros_test_lib.MockTestCase):
266 """Tests for Docker argument constraints."""
267
268 def assertValid(self, output):
269 return output is None
270
271 def assertInvalid(self, output):
272 return not self.assertValid(output)
273
274 def testValidDockerTag(self):
275 """Check logic for validating docker tag format."""
276 # pylint: disable=protected-access
277
278 invalid_tags = [
279 '.invalid-tag',
280 '-invalid-tag',
281 'invalid-tag;',
282 'invalid'*100,
283 ]
284
285 for tag in invalid_tags:
286 self.assertInvalid(test_controller._ValidDockerTag(tag))
287
288 valid_tags = [
289 'valid-tag',
290 'valid-tag-',
291 'valid.tag.',
292 ]
293
294 for tag in valid_tags:
295 self.assertValid(test_controller._ValidDockerTag(tag))
296
297
298 def testValidDockerLabelKey(self):
299 """Check logic for validating docker label key format."""
300 # pylint: disable=protected-access
301
302 invalid_keys = [
303 'Invalid-keY',
304 'Invalid-key',
305 'invalid-keY',
306 'iNVALID-KEy',
307 'invalid_key',
308 'invalid-key;',
309 ]
310
311 for key in invalid_keys:
312 self.assertInvalid(test_controller._ValidDockerLabelKey(key))
313
314 valid_keys = [
315 'chromeos.valid-key',
316 'chromeos.valid-key-2',
317 ]
318
319 for key in valid_keys:
320 self.assertValid(test_controller._ValidDockerLabelKey(key))
321
322
Sean McAllister3834fef2021-10-08 15:45:18 -0600323class BuildTestServiceContainers(cros_test_lib.RunCommandTempDirTestCase,
David Wellingc1433c22021-06-25 16:29:48 +0000324 api_config.ApiConfigMixin):
C Shapiro91af1ce2021-06-17 12:42:09 -0500325 """Tests for the BuildTestServiceContainers function."""
326
327 def setUp(self):
328 self.request = test_pb2.BuildTestServiceContainersRequest(
329 chroot={'path': '/path/to/chroot'},
330 build_target={'name': 'build_target'},
David Wellingc1433c22021-06-25 16:29:48 +0000331 version='R93-14033.0.0',
C Shapiro91af1ce2021-06-17 12:42:09 -0500332 )
333
C Shapiro91af1ce2021-06-17 12:42:09 -0500334 def testSuccess(self):
335 """Check passing case with mocked cros_build_lib.run."""
Sean McAllister3834fef2021-10-08 15:45:18 -0600336
337 def ContainerMetadata():
338 """Return mocked ContainerImageInfo proto"""
339 metadata = container_metadata_pb2.ContainerImageInfo()
340 metadata.repository.hostname = 'gcr.io'
341 metadata.repository.project = 'chromeos-bot'
342 metadata.name = 'random-container-name'
343 metadata.digest = (
344 '09b730f8b6a862f9c2705cb3acf3554563325f5fca5c784bf5c98beb2e56f6db')
345 metadata.tags[:] = [
346 'staging-cq-amd64-generic.R96-1.2.3',
347 '8834106026340379089',
348 ]
349 return metadata
350
351 def WriteContainerMetadata(path):
352 """Write json formatted metadata to the given file."""
353 osutils.WriteFile(
354 path,
355 json_format.MessageToJson(ContainerMetadata()),
356 )
357
358 # Write out mocked container metadata to a temporary file.
359 output_path = os.path.join(self.tempdir, 'metadata.jsonpb')
360 self.rc.SetDefaultCmdResult(
361 returncode=0,
362 side_effect=lambda *_, **__: WriteContainerMetadata(output_path)
363 )
364
365 # Patch TempDir so that we always use this test's directory.
366 self.PatchObject(osutils.TempDir, '__enter__', return_value=self.tempdir)
C Shapiro91af1ce2021-06-17 12:42:09 -0500367
368 response = test_pb2.BuildTestServiceContainersResponse()
369 test_controller.BuildTestServiceContainers(
370 self.request,
371 response,
372 self.api_config)
Sean McAllister3834fef2021-10-08 15:45:18 -0600373
374 self.assertTrue(self.rc.called)
C Shapiro91af1ce2021-06-17 12:42:09 -0500375 for result in response.results:
376 self.assertEqual(result.WhichOneof('result'), 'success')
Sean McAllister3834fef2021-10-08 15:45:18 -0600377 self.assertEqual(result.success.image_info, ContainerMetadata())
C Shapiro91af1ce2021-06-17 12:42:09 -0500378
C Shapiro91af1ce2021-06-17 12:42:09 -0500379 def testFailure(self):
380 """Check failure case with mocked cros_build_lib.run."""
381 patch = self.PatchObject(
382 cros_build_lib, 'run',
383 return_value=cros_build_lib.CommandResult(returncode=1))
384
385 response = test_pb2.BuildTestServiceContainersResponse()
386 test_controller.BuildTestServiceContainers(
387 self.request,
388 response,
389 self.api_config)
390 patch.assert_called()
391 for result in response.results:
392 self.assertEqual(result.WhichOneof('result'), 'failure')
393
394
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -0700395class ChromiteUnitTestTest(cros_test_lib.MockTestCase,
396 api_config.ApiConfigMixin):
397 """Tests for the ChromiteInfoTest function."""
398
399 def setUp(self):
400 self.board = 'board'
401 self.chroot_path = '/path/to/chroot'
402
403 def _GetInput(self, chroot_path=None):
404 """Helper to build an input message instance."""
405 proto = test_pb2.ChromiteUnitTestRequest(
406 chroot={'path': chroot_path},
407 )
408 return proto
409
410 def _GetOutput(self):
411 """Helper to get an empty output message instance."""
412 return test_pb2.ChromiteUnitTestResponse()
413
414 def testValidateOnly(self):
415 """Sanity check that a validate only call does not execute any logic."""
416 patch = self.PatchObject(cros_build_lib, 'run')
417
418 input_msg = self._GetInput(chroot_path=self.chroot_path)
419 test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
420 self.validate_only_config)
421 patch.assert_not_called()
422
Michael Mortensen7a860eb2019-12-03 20:25:15 -0700423 def testMockError(self):
424 """Test mock error call does not execute any logic, returns error."""
425 patch = self.PatchObject(cros_build_lib, 'run')
426
427 input_msg = self._GetInput(chroot_path=self.chroot_path)
428 rc = test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
429 self.mock_error_config)
430 patch.assert_not_called()
431 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
432
433 def testMockCall(self):
434 """Test mock call does not execute any logic, returns success."""
435 patch = self.PatchObject(cros_build_lib, 'run')
436
437 input_msg = self._GetInput(chroot_path=self.chroot_path)
438 rc = test_controller.ChromiteUnitTest(input_msg, self._GetOutput(),
439 self.mock_call_config)
440 patch.assert_not_called()
441 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
442
Michael Mortensen8ca4d3b2019-11-27 09:35:22 -0700443 def testChromiteUnitTest(self):
444 """Call ChromiteUnitTest with mocked cros_build_lib.run."""
445 request = self._GetInput(chroot_path=self.chroot_path)
446 patch = self.PatchObject(
447 cros_build_lib, 'run',
448 return_value=cros_build_lib.CommandResult(returncode=0))
449
450 test_controller.ChromiteUnitTest(request, self._GetOutput(),
451 self.api_config)
452 patch.assert_called_once()
453
454
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600455class CrosSigningTestTest(cros_test_lib.RunCommandTestCase,
456 api_config.ApiConfigMixin):
457 """CrosSigningTest tests."""
458
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700459 def setUp(self):
460 self.chroot_path = '/path/to/chroot'
461
462 def _GetInput(self, chroot_path=None):
463 """Helper to build an input message instance."""
464 proto = test_pb2.CrosSigningTestRequest(
465 chroot={'path': chroot_path},
466 )
467 return proto
468
469 def _GetOutput(self):
470 """Helper to get an empty output message instance."""
471 return test_pb2.CrosSigningTestResponse()
472
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600473 def testValidateOnly(self):
474 """Sanity check that a validate only call does not execute any logic."""
475 test_controller.CrosSigningTest(None, None, self.validate_only_config)
476 self.assertFalse(self.rc.call_count)
477
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700478 def testMockCall(self):
479 """Test mock call does not execute any logic, returns success."""
480 rc = test_controller.CrosSigningTest(None, None, self.mock_call_config)
481 self.assertFalse(self.rc.call_count)
482 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
483
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700484 def testCrosSigningTest(self):
485 """Call CrosSigningTest with mocked cros_build_lib.run."""
486 request = self._GetInput(chroot_path=self.chroot_path)
487 patch = self.PatchObject(
488 cros_build_lib, 'run',
489 return_value=cros_build_lib.CommandResult(returncode=0))
490
491 test_controller.CrosSigningTest(request, self._GetOutput(),
492 self.api_config)
493 patch.assert_called_once()
494
Alex Klein4bc8f4f2019-08-16 14:53:30 -0600495
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600496class SimpleChromeWorkflowTestTest(cros_test_lib.MockTestCase,
497 api_config.ApiConfigMixin):
498 """Test the SimpleChromeWorkflowTest endpoint."""
499
500 @staticmethod
501 def _Output():
502 return test_pb2.SimpleChromeWorkflowTestResponse()
503
David Wellingc1433c22021-06-25 16:29:48 +0000504 def _Input(self,
505 sysroot_path=None,
506 build_target=None,
507 chrome_root=None,
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600508 goma_config=None):
509 proto = test_pb2.SimpleChromeWorkflowTestRequest()
510 if sysroot_path:
511 proto.sysroot.path = sysroot_path
512 if build_target:
513 proto.sysroot.build_target.name = build_target
514 if chrome_root:
515 proto.chrome_root = chrome_root
516 if goma_config:
517 proto.goma_config = goma_config
518 return proto
519
520 def setUp(self):
521 self.chrome_path = 'path/to/chrome'
522 self.sysroot_dir = 'build/board'
523 self.build_target = 'amd64'
524 self.mock_simple_chrome_workflow_test = self.PatchObject(
525 test_service, 'SimpleChromeWorkflowTest')
526
527 def testMissingBuildTarget(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700528 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600529 input_proto = self._Input(build_target=None, sysroot_path='/sysroot/dir',
530 chrome_root='/chrome/path')
531 with self.assertRaises(cros_build_lib.DieSystemExit):
532 test_controller.SimpleChromeWorkflowTest(input_proto, None,
533 self.api_config)
534
535 def testMissingSysrootPath(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700536 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600537 input_proto = self._Input(build_target='board', sysroot_path=None,
538 chrome_root='/chrome/path')
539 with self.assertRaises(cros_build_lib.DieSystemExit):
540 test_controller.SimpleChromeWorkflowTest(input_proto, None,
541 self.api_config)
542
543 def testMissingChromeRoot(self):
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700544 """Test SimpleChromeWorkflowTest dies when build_target not set."""
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600545 input_proto = self._Input(build_target='board', sysroot_path='/sysroot/dir',
546 chrome_root=None)
547 with self.assertRaises(cros_build_lib.DieSystemExit):
548 test_controller.SimpleChromeWorkflowTest(input_proto, None,
549 self.api_config)
550
551 def testSimpleChromeWorkflowTest(self):
552 """Call SimpleChromeWorkflowTest with valid args and temp dir."""
553 request = self._Input(sysroot_path='sysroot_path', build_target='board',
554 chrome_root='/path/to/chrome')
555 response = self._Output()
556
557 test_controller.SimpleChromeWorkflowTest(request, response, self.api_config)
558 self.mock_simple_chrome_workflow_test.assert_called()
559
560 def testValidateOnly(self):
561 request = self._Input(sysroot_path='sysroot_path', build_target='board',
562 chrome_root='/path/to/chrome')
563 test_controller.SimpleChromeWorkflowTest(request, self._Output(),
564 self.validate_only_config)
565 self.mock_simple_chrome_workflow_test.assert_not_called()
566
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700567 def testMockCall(self):
568 """Test mock call does not execute any logic, returns success."""
569 patch = self.mock_simple_chrome_workflow_test = self.PatchObject(
570 test_service, 'SimpleChromeWorkflowTest')
571
572 request = self._Input(sysroot_path='sysroot_path', build_target='board',
573 chrome_root='/path/to/chrome')
574 rc = test_controller.SimpleChromeWorkflowTest(request, self._Output(),
575 self.mock_call_config)
576 patch.assert_not_called()
577 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
578
Michael Mortensenc28d6f12019-10-03 13:34:51 -0600579
Alex Klein231d2da2019-07-22 16:44:45 -0600580class VmTestTest(cros_test_lib.RunCommandTestCase, api_config.ApiConfigMixin):
Evan Hernandez4e388a52019-05-01 12:16:33 -0600581 """Test the VmTest endpoint."""
582
583 def _GetInput(self, **kwargs):
584 values = dict(
585 build_target=common_pb2.BuildTarget(name='target'),
Alex Klein311b8022019-06-05 16:00:07 -0600586 vm_path=common_pb2.Path(path='/path/to/image.bin',
587 location=common_pb2.Path.INSIDE),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600588 test_harness=test_pb2.VmTestRequest.TAST,
589 vm_tests=[test_pb2.VmTestRequest.VmTest(pattern='suite')],
590 ssh_options=test_pb2.VmTestRequest.SshOptions(
Alex Klein231d2da2019-07-22 16:44:45 -0600591 port=1234, private_key_path={'path': '/path/to/id_rsa',
Alex Kleinaa705412019-06-04 15:00:30 -0600592 'location': common_pb2.Path.INSIDE}),
Evan Hernandez4e388a52019-05-01 12:16:33 -0600593 )
594 values.update(kwargs)
595 return test_pb2.VmTestRequest(**values)
596
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700597 def _Output(self):
598 return test_pb2.VmTestResponse()
599
Alex Klein231d2da2019-07-22 16:44:45 -0600600 def testValidateOnly(self):
601 """Sanity check that a validate only call does not execute any logic."""
602 test_controller.VmTest(self._GetInput(), None, self.validate_only_config)
603 self.assertEqual(0, self.rc.call_count)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600604
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700605 def testMockCall(self):
606 """Test mock call does not execute any logic."""
607 patch = self.PatchObject(cros_build_lib, 'run')
608
609 request = self._GetInput()
610 response = self._Output()
611 # VmTest does not return a value, checking mocked value is flagged by lint.
612 test_controller.VmTest(request, response, self.mock_call_config)
613 patch.assert_not_called()
614
Evan Hernandez4e388a52019-05-01 12:16:33 -0600615 def testTastAllOptions(self):
616 """Test VmTest for Tast with all options set."""
Alex Klein231d2da2019-07-22 16:44:45 -0600617 test_controller.VmTest(self._GetInput(), None, self.api_config)
618 self.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700619 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600620 '--board', 'target',
621 '--image-path', '/path/to/image.bin',
622 '--tast', 'suite',
623 '--ssh-port', '1234',
624 '--private-key', '/path/to/id_rsa',
625 ])
626
627 def testAutotestAllOptions(self):
628 """Test VmTest for Autotest with all options set."""
629 input_proto = self._GetInput(test_harness=test_pb2.VmTestRequest.AUTOTEST)
Alex Klein231d2da2019-07-22 16:44:45 -0600630 test_controller.VmTest(input_proto, None, self.api_config)
631 self.assertCommandContains([
Achuith Bhandarkara9e9c3d2019-05-22 13:56:11 -0700632 'cros_run_test', '--debug', '--no-display', '--copy-on-write',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600633 '--board', 'target',
634 '--image-path', '/path/to/image.bin',
635 '--autotest', 'suite',
636 '--ssh-port', '1234',
637 '--private-key', '/path/to/id_rsa',
Greg Edelstondcb0e912020-08-31 11:09:40 -0600638 '--test_that-args=--allow-chrome-crashes',
Evan Hernandez4e388a52019-05-01 12:16:33 -0600639 ])
640
641 def testMissingBuildTarget(self):
642 """Test VmTest dies when build_target not set."""
643 input_proto = self._GetInput(build_target=None)
644 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600645 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600646
647 def testMissingVmImage(self):
648 """Test VmTest dies when vm_image not set."""
Alex Klein311b8022019-06-05 16:00:07 -0600649 input_proto = self._GetInput(vm_path=None)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600650 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600651 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600652
653 def testMissingTestHarness(self):
654 """Test VmTest dies when test_harness not specified."""
655 input_proto = self._GetInput(
656 test_harness=test_pb2.VmTestRequest.UNSPECIFIED)
657 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600658 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandez4e388a52019-05-01 12:16:33 -0600659
660 def testMissingVmTests(self):
661 """Test VmTest dies when vm_tests not set."""
662 input_proto = self._GetInput(vm_tests=[])
663 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600664 test_controller.VmTest(input_proto, None, self.api_config)
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600665
Michael Mortensen82cd62d2019-12-01 14:58:54 -0700666 def testVmTest(self):
667 """Call VmTest with valid args and temp dir."""
668 request = self._GetInput()
669 response = self._Output()
670 patch = self.PatchObject(
671 cros_build_lib, 'run',
672 return_value=cros_build_lib.CommandResult(returncode=0))
673
674 test_controller.VmTest(request, response, self.api_config)
675 patch.assert_called()
676
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600677
Alex Klein231d2da2019-07-22 16:44:45 -0600678class MoblabVmTestTest(cros_test_lib.MockTestCase, api_config.ApiConfigMixin):
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600679 """Test the MoblabVmTest endpoint."""
680
681 @staticmethod
682 def _Payload(path):
683 return test_pb2.MoblabVmTestRequest.Payload(
684 path=common_pb2.Path(path=path))
685
686 @staticmethod
687 def _Output():
688 return test_pb2.MoblabVmTestResponse()
689
690 def _Input(self):
691 return test_pb2.MoblabVmTestRequest(
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600692 chroot=common_pb2.Chroot(path=self.chroot_dir),
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600693 image_payload=self._Payload(self.image_payload_dir),
694 cache_payloads=[self._Payload(self.autotest_payload_dir)])
695
696 def setUp(self):
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600697 self.chroot_dir = '/chroot'
698 self.chroot_tmp_dir = '/chroot/tmp'
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600699 self.image_payload_dir = '/payloads/image'
700 self.autotest_payload_dir = '/payloads/autotest'
701 self.builder = 'moblab-generic-vm/R12-3.4.5-67.890'
702 self.image_cache_dir = '/mnt/moblab/cache'
703 self.image_mount_dir = '/mnt/image'
704
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600705 self.PatchObject(chroot_lib.Chroot, 'tempdir', osutils.TempDir)
Evan Hernandez655e8042019-06-13 12:50:44 -0600706
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600707 self.mock_create_moblab_vms = self.PatchObject(
708 test_service, 'CreateMoblabVm')
709 self.mock_prepare_moblab_vm_image_cache = self.PatchObject(
710 test_service, 'PrepareMoblabVmImageCache',
711 return_value=self.image_cache_dir)
712 self.mock_run_moblab_vm_tests = self.PatchObject(
713 test_service, 'RunMoblabVmTest')
714 self.mock_validate_moblab_vm_tests = self.PatchObject(
715 test_service, 'ValidateMoblabVmTest')
716
717 @contextlib.contextmanager
Alex Klein38c7d9e2019-05-08 09:31:19 -0600718 def MockLoopbackPartitions(*_args, **_kwargs):
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600719 mount = mock.MagicMock()
Evan Hernandez40ee7452019-06-13 12:51:43 -0600720 mount.Mount.return_value = [self.image_mount_dir]
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600721 yield mount
Alex Klein231d2da2019-07-22 16:44:45 -0600722
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600723 self.PatchObject(image_lib, 'LoopbackPartitions', MockLoopbackPartitions)
724
Alex Klein231d2da2019-07-22 16:44:45 -0600725 def testValidateOnly(self):
726 """Sanity check that a validate only call does not execute any logic."""
727 test_controller.MoblabVmTest(self._Input(), self._Output(),
728 self.validate_only_config)
729 self.mock_create_moblab_vms.assert_not_called()
730
Michael Mortensen7a7646d2019-12-12 15:36:14 -0700731 def testMockCall(self):
732 """Test mock call does not execute any logic."""
733 patch = self.PatchObject(key_value_store, 'LoadFile')
734
735 # MoblabVmTest does not return a value, checking mocked value is flagged by
736 # lint.
737 test_controller.MoblabVmTest(self._Input(), self._Output(),
738 self.mock_call_config)
739 patch.assert_not_called()
740
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600741 def testImageContainsBuilder(self):
742 """MoblabVmTest calls service with correct args."""
743 request = self._Input()
744 response = self._Output()
745
746 self.PatchObject(
Mike Frysingere652ba12019-09-08 00:57:43 -0400747 key_value_store, 'LoadFile',
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600748 return_value={cros_set_lsb_release.LSB_KEY_BUILDER_PATH: self.builder})
749
Alex Klein231d2da2019-07-22 16:44:45 -0600750 test_controller.MoblabVmTest(request, response, self.api_config)
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600751
752 self.assertEqual(
753 self.mock_create_moblab_vms.call_args_list,
Evan Hernandeze1e05d32019-07-19 12:32:18 -0600754 [mock.call(mock.ANY, self.chroot_dir, self.image_payload_dir)])
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600755 self.assertEqual(
756 self.mock_prepare_moblab_vm_image_cache.call_args_list,
757 [mock.call(mock.ANY, self.builder, [self.autotest_payload_dir])])
758 self.assertEqual(
759 self.mock_run_moblab_vm_tests.call_args_list,
Evan Hernandez655e8042019-06-13 12:50:44 -0600760 [mock.call(mock.ANY, mock.ANY, self.builder, self.image_cache_dir,
761 mock.ANY)])
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600762 self.assertEqual(
763 self.mock_validate_moblab_vm_tests.call_args_list,
764 [mock.call(mock.ANY)])
765
766 def testImageMissingBuilder(self):
767 """MoblabVmTest dies when builder path not found in lsb-release."""
768 request = self._Input()
769 response = self._Output()
770
Mike Frysingere652ba12019-09-08 00:57:43 -0400771 self.PatchObject(key_value_store, 'LoadFile', return_value={})
Evan Hernandezdc3f0bb2019-06-06 12:46:52 -0600772
773 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600774 test_controller.MoblabVmTest(request, response, self.api_config)
David Wellingc1433c22021-06-25 16:29:48 +0000775
776
777class GetArtifactsTest(cros_test_lib.MockTempDirTestCase):
778 """Test GetArtifacts."""
779
780 CODE_COVERAGE_LLVM_ARTIFACT_TYPE = (
781 common_pb2.ArtifactsByService.Test.ArtifactType.CODE_COVERAGE_LLVM_JSON
782 )
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600783 UNIT_TEST_ARTIFACT_TYPE = (
784 common_pb2.ArtifactsByService.Test.ArtifactType.UNIT_TESTS
785 )
David Wellingc1433c22021-06-25 16:29:48 +0000786
787 def setUp(self):
788 """Set up the class for tests."""
789 chroot_dir = os.path.join(self.tempdir, 'chroot')
790 osutils.SafeMakedirs(chroot_dir)
791 osutils.SafeMakedirs(os.path.join(chroot_dir, 'tmp'))
792 self.chroot = chroot_lib.Chroot(chroot_dir)
793
794 sysroot_path = os.path.join(chroot_dir, 'build', 'board')
795 osutils.SafeMakedirs(sysroot_path)
796 self.sysroot = sysroot_lib.Sysroot(sysroot_path)
797
Jack Neusc9707c32021-07-23 21:48:54 +0000798 self.build_target = build_target_lib.BuildTarget('board')
799
David Wellingc1433c22021-06-25 16:29:48 +0000800 def testReturnsEmptyListWhenNoOutputArtifactsProvided(self):
801 """Test empty list is returned when there are no output_artifacts."""
802 result = test_controller.GetArtifacts(
803 common_pb2.ArtifactsByService.Test(output_artifacts=[]),
Jack Neusc9707c32021-07-23 21:48:54 +0000804 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000805
806 self.assertEqual(len(result), 0)
807
808 def testShouldCallBundleCodeCoverageLlvmJsonForEachValidArtifact(self):
809 """Test BundleCodeCoverageLlvmJson is called on each valid artifact."""
Sean McAllister17eed8d2021-09-21 10:41:16 -0600810 BundleCodeCoverageLlvmJson_mock = (
811 self.PatchObject(
812 test_service,
813 'BundleCodeCoverageLlvmJson',
814 return_value='test'))
David Wellingc1433c22021-06-25 16:29:48 +0000815
816 test_controller.GetArtifacts(
817 common_pb2.ArtifactsByService.Test(output_artifacts=[
818 # Valid
819 common_pb2.ArtifactsByService.Test.ArtifactInfo(
820 artifact_types=[
821 self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE
822 ]
823 ),
824
825 # Invalid
826 common_pb2.ArtifactsByService.Test.ArtifactInfo(
827 artifact_types=[
828 common_pb2.ArtifactsByService.Test.ArtifactType.UNIT_TESTS
829 ]
830 ),
831 ]),
Jack Neusc9707c32021-07-23 21:48:54 +0000832 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000833
834 BundleCodeCoverageLlvmJson_mock.assert_called_once()
835
836 def testShouldReturnValidResult(self):
837 """Test result contains paths and code_coverage_llvm_json type."""
838 self.PatchObject(test_service, 'BundleCodeCoverageLlvmJson',
Sean McAllister17eed8d2021-09-21 10:41:16 -0600839 return_value='test')
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600840 self.PatchObject(test_service, 'BuildTargetUnitTestTarball',
Sean McAllister17eed8d2021-09-21 10:41:16 -0600841 return_value='unit_tests.tar')
David Wellingc1433c22021-06-25 16:29:48 +0000842
843 result = test_controller.GetArtifacts(
844 common_pb2.ArtifactsByService.Test(output_artifacts=[
845 # Valid
846 common_pb2.ArtifactsByService.Test.ArtifactInfo(
847 artifact_types=[
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600848 self.UNIT_TEST_ARTIFACT_TYPE
849 ]
850 ),
851 common_pb2.ArtifactsByService.Test.ArtifactInfo(
852 artifact_types=[
David Wellingc1433c22021-06-25 16:29:48 +0000853 self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE
854 ]
855 ),
856 ]),
Jack Neusc9707c32021-07-23 21:48:54 +0000857 self.chroot, self.sysroot, self.build_target, self.tempdir)
David Wellingc1433c22021-06-25 16:29:48 +0000858
George Engelbrecht764b1cd2021-06-18 17:01:07 -0600859 self.assertEqual(result[0]['paths'], ['unit_tests.tar'])
860 self.assertEqual(result[0]['type'], self.UNIT_TEST_ARTIFACT_TYPE)
861 self.assertEqual(result[1]['paths'], ['test'])
862 self.assertEqual(result[1]['type'], self.CODE_COVERAGE_LLVM_ARTIFACT_TYPE)