blob: 4641c0895fcb0acdbe479230682beac06c18143d [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Alex Klein2966e302019-01-17 13:29:38 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Image service tests."""
6
Alex Klein2966e302019-01-17 13:29:38 -07007import os
Joseph Sussman34c01be2022-06-03 14:04:41 +00008from pathlib import Path
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +00009from typing import List, Optional
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Alex Klein2966e302019-01-17 13:29:38 -070011
Alex Klein231d2da2019-07-22 16:44:45 -060012from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060013from chromite.api import controller
14from chromite.api.controller import image as image_controller
Alex Klein7107bdd2019-03-14 17:14:31 -060015from chromite.api.gen.chromite.api import image_pb2
Jack Neus761e1842020-12-01 18:20:11 +000016from chromite.api.gen.chromite.api import sysroot_pb2
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040017from chromite.api.gen.chromiumos import common_pb2
Josiah Hounyode113e52022-11-30 06:30:33 +000018from chromite.lib import build_target_lib
19from chromite.lib import chroot_lib
Alex Klein56355682019-02-07 10:36:54 -070020from chromite.lib import constants
Alex Klein4f0eb432019-05-02 13:56:04 -060021from chromite.lib import cros_build_lib
Alex Klein2966e302019-01-17 13:29:38 -070022from chromite.lib import cros_test_lib
Michael Mortensenc83c9952019-08-05 12:15:12 -060023from chromite.lib import image_lib
Alex Klein2966e302019-01-17 13:29:38 -070024from chromite.lib import osutils
Josiah Hounyode113e52022-11-30 06:30:33 +000025from chromite.lib import sysroot_lib
Jack Neus761e1842020-12-01 18:20:11 +000026from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070027from chromite.service import image as image_service
Alex Klein2966e302019-01-17 13:29:38 -070028
29
Alex Klein231d2da2019-07-22 16:44:45 -060030class CreateTest(cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -060031 """Create image tests."""
Alex Klein56355682019-02-07 10:36:54 -070032
Alex Klein1699fab2022-09-08 08:46:06 -060033 def setUp(self):
34 self.response = image_pb2.CreateImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -060035
Alex Klein1699fab2022-09-08 08:46:06 -060036 def _GetRequest(
37 self,
38 board=None,
39 types=None,
40 version=None,
41 builder_path=None,
42 disable_rootfs_verification=False,
43 base_is_recovery=False,
44 ):
45 """Helper to build a request instance."""
46 return image_pb2.CreateImageRequest(
47 build_target={"name": board},
48 image_types=types,
49 disable_rootfs_verification=disable_rootfs_verification,
50 version=version,
51 builder_path=builder_path,
52 base_is_recovery=base_is_recovery,
53 )
Alex Klein21b95022019-05-09 14:14:46 -060054
Alex Klein1699fab2022-09-08 08:46:06 -060055 def testValidateOnly(self):
56 """Sanity check that a validate only call does not execute any logic."""
57 patch = self.PatchObject(image_service, "Build")
Alex Klein21b95022019-05-09 14:14:46 -060058
Alex Klein1699fab2022-09-08 08:46:06 -060059 request = self._GetRequest(board="board")
60 image_controller.Create(
61 request, self.response, self.validate_only_config
62 )
63 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -060064
Alex Klein1699fab2022-09-08 08:46:06 -060065 def testMockCall(self):
66 """Test that mock call does not execute any logic, returns mocked value."""
67 patch = self.PatchObject(image_service, "Build")
Michael Mortensen10146cf2019-11-19 19:59:22 -070068
Alex Klein1699fab2022-09-08 08:46:06 -060069 request = self._GetRequest(board="board")
70 image_controller.Create(request, self.response, self.mock_call_config)
71 patch.assert_not_called()
72 self.assertEqual(self.response.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -070073
Alex Klein1699fab2022-09-08 08:46:06 -060074 def testMockError(self):
75 """Test that mock call does not execute any logic, returns error."""
76 patch = self.PatchObject(image_service, "Build")
Michael Mortensen85d38402019-12-12 09:50:29 -070077
Alex Klein1699fab2022-09-08 08:46:06 -060078 request = self._GetRequest(board="board")
79 rc = image_controller.Create(
80 request, self.response, self.mock_error_config
81 )
82 patch.assert_not_called()
83 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -070084
Alex Klein1699fab2022-09-08 08:46:06 -060085 def testNoBoard(self):
86 """Test no board given fails."""
87 request = self._GetRequest()
Alex Klein56355682019-02-07 10:36:54 -070088
Alex Klein1699fab2022-09-08 08:46:06 -060089 # No board should cause it to fail.
90 with self.assertRaises(cros_build_lib.DieSystemExit):
91 image_controller.Create(request, self.response, self.api_config)
Alex Klein56355682019-02-07 10:36:54 -070092
Alex Klein1699fab2022-09-08 08:46:06 -060093 def testNoTypeSpecified(self):
94 """Test the image type default."""
95 request = self._GetRequest(board="board")
Alex Klein21b95022019-05-09 14:14:46 -060096
Alex Klein1699fab2022-09-08 08:46:06 -060097 # Failed result to avoid the success handling logic.
98 result = image_service.BuildResult([constants.IMAGE_TYPE_BASE])
99 result.return_code = 1
100 build_patch = self.PatchObject(
101 image_service, "Build", return_value=result
102 )
Alex Klein56355682019-02-07 10:36:54 -0700103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 image_controller.Create(request, self.response, self.api_config)
105 build_patch.assert_any_call(
106 "board", [constants.IMAGE_TYPE_BASE], config=mock.ANY
107 )
Alex Klein56355682019-02-07 10:36:54 -0700108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 def testSingleTypeSpecified(self):
110 """Test it's properly using a specified type."""
111 request = self._GetRequest(
112 board="board", types=[common_pb2.IMAGE_TYPE_DEV]
113 )
Alex Klein21b95022019-05-09 14:14:46 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 # Failed result to avoid the success handling logic.
116 result = image_service.BuildResult([constants.IMAGE_TYPE_DEV])
117 result.return_code = 1
118 build_patch = self.PatchObject(
119 image_service, "Build", return_value=result
120 )
Alex Klein21b95022019-05-09 14:14:46 -0600121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 image_controller.Create(request, self.response, self.api_config)
123 build_patch.assert_any_call(
124 "board", [constants.IMAGE_TYPE_DEV], config=mock.ANY
125 )
Alex Klein56355682019-02-07 10:36:54 -0700126
Alex Klein1699fab2022-09-08 08:46:06 -0600127 def testMultipleAndImpliedTypes(self):
128 """Test multiple types and implied type handling."""
129 # The TEST_VM type should force it to build the test image.
130 types = [common_pb2.IMAGE_TYPE_BASE, common_pb2.IMAGE_TYPE_TEST_VM]
131 expected_images = [constants.IMAGE_TYPE_BASE, constants.IMAGE_TYPE_TEST]
Alex Klein21b95022019-05-09 14:14:46 -0600132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 request = self._GetRequest(board="board", types=types)
Alex Klein21b95022019-05-09 14:14:46 -0600134
Alex Klein1699fab2022-09-08 08:46:06 -0600135 # Failed result to avoid the success handling logic.
136 result = image_service.BuildResult(expected_images)
137 result.return_code = 1
138 build_patch = self.PatchObject(
139 image_service, "Build", return_value=result
140 )
Alex Klein21b95022019-05-09 14:14:46 -0600141
Alex Klein1699fab2022-09-08 08:46:06 -0600142 image_controller.Create(request, self.response, self.api_config)
143 build_patch.assert_any_call("board", expected_images, config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 def testRecoveryImpliedTypes(self):
146 """Test implied type handling of recovery images."""
147 # The TEST_VM type should force it to build the test image.
148 types = [common_pb2.IMAGE_TYPE_RECOVERY]
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 request = self._GetRequest(board="board", types=types)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 # Failed result to avoid the success handling logic.
153 result = image_service.BuildResult([])
154 result.return_code = 1
155 build_patch = self.PatchObject(
156 image_service, "Build", return_value=result
157 )
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700158
Alex Klein1699fab2022-09-08 08:46:06 -0600159 image_controller.Create(request, self.response, self.api_config)
160 build_patch.assert_any_call(
161 "board", [constants.IMAGE_TYPE_BASE], config=mock.ANY
162 )
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 def testFailedPackageHandling(self):
165 """Test failed packages are populated correctly."""
166 result = image_service.BuildResult([])
167 result.return_code = 1
168 result.failed_packages = ["foo/bar", "cat/pkg"]
169 expected_packages = [("foo", "bar"), ("cat", "pkg")]
170 self.PatchObject(image_service, "Build", return_value=result)
Alex Klein1bcd9882019-03-19 13:25:24 -0600171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 input_proto = self._GetRequest(board="board")
Alex Klein1bcd9882019-03-19 13:25:24 -0600173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 rc = image_controller.Create(
175 input_proto, self.response, self.api_config
176 )
Alex Klein231d2da2019-07-22 16:44:45 -0600177
Alex Klein1699fab2022-09-08 08:46:06 -0600178 self.assertEqual(
179 controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc
180 )
181 for package in self.response.failed_packages:
182 self.assertIn(
183 (package.category, package.package_name), expected_packages
184 )
Alex Klein1bcd9882019-03-19 13:25:24 -0600185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 def testNoPackagesFailureHandling(self):
187 """Test failed packages are populated correctly."""
188 result = image_service.BuildResult([])
189 result.return_code = 1
190 self.PatchObject(image_service, "Build", return_value=result)
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700191
Alex Klein1699fab2022-09-08 08:46:06 -0600192 input_proto = image_pb2.CreateImageRequest()
193 input_proto.build_target.name = "board"
Alex Klein2557b4f2019-07-11 14:34:00 -0600194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 rc = image_controller.Create(
196 input_proto, self.response, self.api_config
197 )
198 self.assertTrue(rc)
199 self.assertNotEqual(
200 controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc
201 )
202 self.assertFalse(self.response.failed_packages)
Alex Klein2557b4f2019-07-11 14:34:00 -0600203
Jack Neus70490b52022-09-12 14:41:45 +0000204 def testFactory(self):
205 """Test it's properly building factory."""
206 request = self._GetRequest(
207 board="board",
208 types=[
209 common_pb2.IMAGE_TYPE_FACTORY,
210 common_pb2.IMAGE_TYPE_NETBOOT,
211 ],
212 )
213 factory_path = self.tempdir / "factory-shim"
214 factory_path.touch()
215 result = image_service.BuildResult([constants.IMAGE_TYPE_FACTORY_SHIM])
216 result.add_image(constants.IMAGE_TYPE_FACTORY_SHIM, factory_path)
217 result.return_code = 0
218 build_patch = self.PatchObject(
219 image_service, "Build", return_value=result
220 )
221 netboot_patch = self.PatchObject(image_service, "create_netboot_kernel")
222
223 image_controller.Create(request, self.response, self.api_config)
224 build_patch.assert_any_call(
225 "board", [constants.IMAGE_TYPE_FACTORY_SHIM], config=mock.ANY
226 )
Madeleine Hardtf0a92fc2022-09-19 18:41:12 +0000227 netboot_patch.assert_any_call("board", os.path.dirname(factory_path))
Jack Neus70490b52022-09-12 14:41:45 +0000228
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000229
Josiah Hounyode113e52022-11-30 06:30:33 +0000230class GetArtifactsTest(
231 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
232):
233 """GetArtifacts function tests."""
234
235 _artifact_funcs = {
236 common_pb2.ArtifactsByService.Image.ArtifactType.DLC_IMAGE: image_service.copy_dlc_image,
237 common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS: image_service.copy_license_credits,
238 common_pb2.ArtifactsByService.Image.ArtifactType.FACTORY_IMAGE: image_service.create_factory_image_zip,
239 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES: image_service.create_stripped_packages_tar,
Jack Neus91846002022-12-01 23:49:22 +0000240 common_pb2.ArtifactsByService.Image.ArtifactType.IMAGE_SCRIPTS: image_service.create_image_scripts_archive,
Josiah Hounyode113e52022-11-30 06:30:33 +0000241 }
242
243 def setUp(self):
244 self._mocks = {}
245 for artifact, func in self._artifact_funcs.items():
246 self._mocks[artifact] = self.PatchObject(
247 image_service, func.__name__
248 )
249 self.chroot = chroot_lib.Chroot(
250 path=self.tempdir, chrome_root=self.tempdir
251 )
252 board = "chell"
253 sysroot_path = "/build/%s" % board
254 self.sysroot_class = sysroot_lib.Sysroot(sysroot_path)
255 self.build_target = build_target_lib.BuildTarget(board)
256
257 def _InputProto(
258 self,
259 artifact_types=_artifact_funcs.keys(),
260 ):
261 """Helper to build an input proto instance."""
262 return common_pb2.ArtifactsByService.Image(
263 output_artifacts=[
264 common_pb2.ArtifactsByService.Image.ArtifactInfo(
265 artifact_types=artifact_types
266 )
267 ]
268 )
269
270 def testNoArtifacts(self):
271 """Test GetArtifacts with no artifact types."""
272 in_proto = self._InputProto(artifact_types=[])
273 image_controller.GetArtifacts(
274 in_proto, self.chroot, self.sysroot_class, self.build_target, ""
275 )
276
277 for _, patch in self._mocks.items():
278 patch.assert_not_called()
279
280 def testArtifactsSuccess(self):
281 """Test GetArtifacts with all artifact types."""
282 image_controller.GetArtifacts(
283 self._InputProto(),
284 self.chroot,
285 self.sysroot_class,
286 self.build_target,
287 "",
288 )
289
290 for _, patch in self._mocks.items():
291 patch.assert_called_once()
292
293 def testArtifactsException(self):
294 """Test GetArtifacts with all artifact types when one type throws an exception."""
295
296 self._mocks[
297 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES
298 ].side_effect = Exception("foo bar")
299 generated = image_controller.GetArtifacts(
300 self._InputProto(),
301 self.chroot,
302 self.sysroot_class,
303 self.build_target,
304 "",
305 )
306
307 for _, patch in self._mocks.items():
308 patch.assert_called_once()
309
310 found_artifact = False
311 for data in generated:
312 artifact_type = (
313 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
314 data["type"]
315 )
316 )
317 if artifact_type == "STRIPPED_PACKAGES":
318 found_artifact = True
319 self.assertTrue(data["failed"])
320 self.assertEqual(data["failure_reason"], "foo bar")
321 self.assertTrue(found_artifact)
322
323
Alex Klein1699fab2022-09-08 08:46:06 -0600324class RecoveryImageTest(
325 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
326):
327 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000328
Alex Klein1699fab2022-09-08 08:46:06 -0600329 def setUp(self):
330 self.response = image_pb2.CreateImageResult()
331 self.types = [
332 common_pb2.IMAGE_TYPE_BASE,
333 common_pb2.IMAGE_TYPE_RECOVERY,
334 ]
335 self.build_result = self._CreateMockBuildResult(
336 [common_pb2.IMAGE_TYPE_BASE]
337 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000338
Alex Klein1699fab2022-09-08 08:46:06 -0600339 self.PatchObject(
340 image_service,
341 "Build",
342 side_effect=[
343 self.build_result,
344 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
345 ],
346 )
347 self.copy_image_mock = self.PatchObject(
348 image_service,
349 "CopyBaseToRecovery",
350 side_effect=[
351 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
352 ],
353 )
354 self.recov_image_mock = self.PatchObject(
355 image_service,
356 "BuildRecoveryImage",
357 side_effect=[
358 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
359 ],
360 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000361
Alex Klein1699fab2022-09-08 08:46:06 -0600362 def _GetRequest(
363 self,
364 board=None,
365 types=None,
366 version=None,
367 builder_path=None,
368 disable_rootfs_verification=False,
369 base_is_recovery=False,
370 ):
371 """Helper to build a request instance."""
372 return image_pb2.CreateImageRequest(
373 build_target={"name": board},
374 image_types=types,
375 disable_rootfs_verification=disable_rootfs_verification,
376 version=version,
377 builder_path=builder_path,
378 base_is_recovery=base_is_recovery,
379 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def _CreateMockBuildResult(
382 self, image_types: List[int]
383 ) -> Optional[image_service.BuildResult]:
384 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000385
Alex Klein1699fab2022-09-08 08:46:06 -0600386 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600387 image_types: A list of image types for which the mock BuildResult has to
388 be creates.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600391 A list of mock BuildResult.
Alex Klein1699fab2022-09-08 08:46:06 -0600392 """
393 image_types_names = [
394 image_controller.SUPPORTED_IMAGE_TYPES[x]
395 for x in image_types
396 if image_controller.SUPPORTED_IMAGE_TYPES[x]
397 in constants.IMAGE_TYPE_TO_NAME
398 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 if not image_types_names:
401 if (
402 common_pb2.IMAGE_TYPE_FACTORY in image_types
403 and len(image_types) == 1
404 ):
405 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
406 else:
407 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000408
Alex Klein1699fab2022-09-08 08:46:06 -0600409 _build_result = image_service.BuildResult(image_types_names)
410 _build_result.return_code = 0
411 for image_type in image_types_names:
412 test_image = (
413 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
414 )
415 test_image.touch()
416 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000417
Alex Klein1699fab2022-09-08 08:46:06 -0600418 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000419
Alex Klein1699fab2022-09-08 08:46:06 -0600420 def testBaseIsRecoveryTrue(self):
421 """Test that cp is called."""
422 input_proto = self._GetRequest(
423 board="board", types=self.types, base_is_recovery=True
424 )
425 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000426
Alex Klein1699fab2022-09-08 08:46:06 -0600427 self.copy_image_mock.assert_called_with(
428 board="board",
429 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
430 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000431
Alex Klein1699fab2022-09-08 08:46:06 -0600432 def testBaseIsRecoveryFalse(self):
433 """Test that mod_image_for_recovery.sh is called."""
434 input_proto = self._GetRequest(
435 board="board", types=self.types, base_is_recovery=False
436 )
437 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 self.recov_image_mock.assert_called_with(
440 board="board",
441 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
442 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000443
Alex Klein2557b4f2019-07-11 14:34:00 -0600444
Alex Klein1699fab2022-09-08 08:46:06 -0600445class ImageSignerTestTest(
446 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
447):
448 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600449
Alex Klein1699fab2022-09-08 08:46:06 -0600450 def setUp(self):
451 self.image_path = os.path.join(self.tempdir, "image.bin")
452 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600453
Alex Klein1699fab2022-09-08 08:46:06 -0600454 osutils.SafeMakedirs(self.result_directory)
455 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 def testValidateOnly(self):
458 """Sanity check that validate-only calls don't execute any logic."""
459 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
460 input_proto = image_pb2.TestImageRequest()
461 input_proto.image.path = self.image_path
462 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600463
Alex Klein1699fab2022-09-08 08:46:06 -0600464 image_controller.SignerTest(
465 input_proto, output_proto, self.validate_only_config
466 )
Alex Klein231d2da2019-07-22 16:44:45 -0600467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600469
Alex Klein1699fab2022-09-08 08:46:06 -0600470 def testMockCall(self):
471 """Test that mock call does not execute any logic, returns mocked value."""
472 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
473 input_proto = image_pb2.TestImageRequest()
474 input_proto.image.path = self.image_path
475 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700476
Alex Klein1699fab2022-09-08 08:46:06 -0600477 image_controller.SignerTest(
478 input_proto, output_proto, self.mock_call_config
479 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 patch.assert_not_called()
482 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700483
Alex Klein1699fab2022-09-08 08:46:06 -0600484 def testMockError(self):
485 """Test that mock call does not execute any logic, returns error."""
486 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
487 input_proto = image_pb2.TestImageRequest()
488 input_proto.image.path = self.image_path
489 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700490
Alex Klein1699fab2022-09-08 08:46:06 -0600491 rc = image_controller.SignerTest(
492 input_proto, output_proto, self.mock_error_config
493 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 patch.assert_not_called()
496 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 def testSignerTestNoImage(self):
499 """Test function argument validation."""
500 input_proto = image_pb2.TestImageRequest()
501 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 # Nothing provided.
504 with self.assertRaises(cros_build_lib.DieSystemExit):
505 image_controller.SignerTest(
506 input_proto, output_proto, self.api_config
507 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 def testSignerTestSuccess(self):
510 """Test successful call handling."""
511 self.PatchObject(image_lib, "SecurityTest", return_value=True)
512 input_proto = image_pb2.TestImageRequest()
513 input_proto.image.path = self.image_path
514 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600515
Alex Klein1699fab2022-09-08 08:46:06 -0600516 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600517
Alex Klein1699fab2022-09-08 08:46:06 -0600518 def testSignerTestFailure(self):
519 """Test function output tests."""
520 input_proto = image_pb2.TestImageRequest()
521 input_proto.image.path = self.image_path
522 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600523
Alex Klein1699fab2022-09-08 08:46:06 -0600524 self.PatchObject(image_lib, "SecurityTest", return_value=False)
525 image_controller.SignerTest(input_proto, output_proto, self.api_config)
526 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600527
Michael Mortensenc83c9952019-08-05 12:15:12 -0600528
Alex Klein1699fab2022-09-08 08:46:06 -0600529class ImageTestTest(
530 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
531):
532 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700533
Alex Klein1699fab2022-09-08 08:46:06 -0600534 def setUp(self):
535 self.image_path = os.path.join(self.tempdir, "image.bin")
536 self.board = "board"
537 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700538
Alex Klein1699fab2022-09-08 08:46:06 -0600539 osutils.SafeMakedirs(self.result_directory)
540 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 def testValidateOnly(self):
543 """Sanity check that a validate only call does not execute any logic."""
544 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600545
Alex Klein1699fab2022-09-08 08:46:06 -0600546 input_proto = image_pb2.TestImageRequest()
547 input_proto.image.path = self.image_path
548 input_proto.build_target.name = self.board
549 input_proto.result.directory = self.result_directory
550 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600551
Alex Klein1699fab2022-09-08 08:46:06 -0600552 image_controller.Test(
553 input_proto, output_proto, self.validate_only_config
554 )
555 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600556
Alex Klein1699fab2022-09-08 08:46:06 -0600557 def testMockCall(self):
558 """Test that mock call does not execute any logic, returns mocked value."""
559 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700560
Alex Klein1699fab2022-09-08 08:46:06 -0600561 input_proto = image_pb2.TestImageRequest()
562 input_proto.image.path = self.image_path
563 input_proto.build_target.name = self.board
564 input_proto.result.directory = self.result_directory
565 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700566
Alex Klein1699fab2022-09-08 08:46:06 -0600567 image_controller.Test(input_proto, output_proto, self.mock_call_config)
568 patch.assert_not_called()
569 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700570
Alex Klein1699fab2022-09-08 08:46:06 -0600571 def testMockError(self):
572 """Test that mock call does not execute any logic, returns error."""
573 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700574
Alex Klein1699fab2022-09-08 08:46:06 -0600575 input_proto = image_pb2.TestImageRequest()
576 input_proto.image.path = self.image_path
577 input_proto.build_target.name = self.board
578 input_proto.result.directory = self.result_directory
579 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700580
Alex Klein1699fab2022-09-08 08:46:06 -0600581 rc = image_controller.Test(
582 input_proto, output_proto, self.mock_error_config
583 )
584 patch.assert_not_called()
585 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 def testTestArgumentValidation(self):
588 """Test function argument validation tests."""
589 self.PatchObject(image_service, "Test", return_value=True)
590 input_proto = image_pb2.TestImageRequest()
591 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700592
Alex Klein1699fab2022-09-08 08:46:06 -0600593 # Nothing provided.
594 with self.assertRaises(cros_build_lib.DieSystemExit):
595 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700596
Alex Klein1699fab2022-09-08 08:46:06 -0600597 # Just one argument.
598 input_proto.build_target.name = self.board
599 with self.assertRaises(cros_build_lib.DieSystemExit):
600 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700601
Alex Klein1699fab2022-09-08 08:46:06 -0600602 # Two arguments provided.
603 input_proto.result.directory = self.result_directory
604 with self.assertRaises(cros_build_lib.DieSystemExit):
605 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700606
Alex Klein1699fab2022-09-08 08:46:06 -0600607 # Invalid image path.
608 input_proto.image.path = "/invalid/image/path"
609 with self.assertRaises(cros_build_lib.DieSystemExit):
610 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 # All valid arguments.
613 input_proto.image.path = self.image_path
614 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700615
Alex Klein1699fab2022-09-08 08:46:06 -0600616 def testTestOutputHandling(self):
617 """Test function output tests."""
618 input_proto = image_pb2.TestImageRequest()
619 input_proto.image.path = self.image_path
620 input_proto.build_target.name = self.board
621 input_proto.result.directory = self.result_directory
622 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700623
Alex Klein1699fab2022-09-08 08:46:06 -0600624 self.PatchObject(image_service, "Test", return_value=True)
625 image_controller.Test(input_proto, output_proto, self.api_config)
626 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700627
Alex Klein1699fab2022-09-08 08:46:06 -0600628 self.PatchObject(image_service, "Test", return_value=False)
629 image_controller.Test(input_proto, output_proto, self.api_config)
630 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000631
632
Greg Edelston6902e3d2022-01-27 12:19:38 -0700633class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600634 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000635
Alex Klein1699fab2022-09-08 08:46:06 -0600636 def _GetRequest(
637 self,
638 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
639 build_target_name="atlas",
640 profile="foo",
641 sign_types=None,
642 dryrun=True,
643 channels=None,
644 ):
645 return image_pb2.PushImageRequest(
646 gs_image_dir=gs_image_dir,
647 sysroot=sysroot_pb2.Sysroot(
648 build_target=common_pb2.BuildTarget(name=build_target_name)
649 ),
650 profile=common_pb2.Profile(name=profile),
651 sign_types=sign_types,
652 dryrun=dryrun,
653 channels=channels,
654 )
Jack Neus761e1842020-12-01 18:20:11 +0000655
Alex Klein1699fab2022-09-08 08:46:06 -0600656 def _GetResponse(self):
657 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000658
Alex Klein1699fab2022-09-08 08:46:06 -0600659 @mock.patch.object(pushimage, "PushImage", return_value={})
660 def testValidateOnly(self, MockPushImage):
661 """Check that a validate only call does not execute any logic."""
662 req = self._GetRequest(
663 sign_types=[
664 common_pb2.IMAGE_TYPE_RECOVERY,
665 common_pb2.IMAGE_TYPE_FACTORY,
666 common_pb2.IMAGE_TYPE_FIRMWARE,
667 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
668 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
669 common_pb2.IMAGE_TYPE_BASE,
670 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
671 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
672 ]
673 )
674 rc = image_controller.PushImage(
675 req, self.NewResponse(), self.validate_only_config
676 )
677 MockPushImage.assert_not_called()
678 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000679
Alex Klein1699fab2022-09-08 08:46:06 -0600680 @mock.patch.object(pushimage, "PushImage", return_value={})
681 def testValidateOnlyInvalid(self, MockPushImage):
682 """Check that validate call rejects invalid sign types."""
683 # Pass unsupported image type.
684 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
685 rc = image_controller.PushImage(
686 req, self._GetResponse(), self.validate_only_config
687 )
688 MockPushImage.assert_not_called()
689 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000690
Alex Klein1699fab2022-09-08 08:46:06 -0600691 @mock.patch.object(pushimage, "PushImage", return_value={})
692 def testMockCall(self, MockPushImage):
693 """Test that mock call does not execute any logic, returns mocked value."""
694 rc = image_controller.PushImage(
695 self._GetRequest(), self._GetResponse(), self.mock_call_config
696 )
697 MockPushImage.assert_not_called()
698 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000699
Alex Klein1699fab2022-09-08 08:46:06 -0600700 @mock.patch.object(pushimage, "PushImage", return_value={})
701 def testMockError(self, MockPushImage):
702 """Test that mock call does not execute any logic, returns error."""
703 rc = image_controller.PushImage(
704 self._GetRequest(), self._GetResponse(), self.mock_error_config
705 )
706 MockPushImage.assert_not_called()
707 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000708
Alex Klein1699fab2022-09-08 08:46:06 -0600709 @mock.patch.object(pushimage, "PushImage", return_value={})
710 def testNoBuildTarget(self, _):
711 """Test no build target given fails."""
712 request = self._GetRequest(build_target_name="")
713 with self.assertRaises(cros_build_lib.DieSystemExit):
714 image_controller.PushImage(
715 request, self._GetResponse(), self.api_config
716 )
Jack Neus761e1842020-12-01 18:20:11 +0000717
Alex Klein1699fab2022-09-08 08:46:06 -0600718 @mock.patch.object(pushimage, "PushImage", return_value={})
719 def testNoGsImageDir(self, _):
720 """Test no image dir given fails."""
721 request = self._GetRequest(gs_image_dir="")
722 with self.assertRaises(cros_build_lib.DieSystemExit):
723 image_controller.PushImage(
724 request, self._GetResponse(), self.api_config
725 )
Jack Neus761e1842020-12-01 18:20:11 +0000726
Alex Klein1699fab2022-09-08 08:46:06 -0600727 @mock.patch.object(pushimage, "PushImage", return_value={})
728 def testCallCorrect(self, MockPushImage):
729 """Check that a call is called with the correct parameters."""
730 request = self._GetRequest(
731 dryrun=False,
732 profile="",
733 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
734 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
735 )
736 request.dest_bucket = "gs://foo"
737 image_controller.PushImage(
738 request, self._GetResponse(), self.api_config
739 )
740 MockPushImage.assert_called_with(
741 request.gs_image_dir,
742 request.sysroot.build_target.name,
743 dry_run=request.dryrun,
744 sign_types=["recovery"],
745 dest_bucket=request.dest_bucket,
746 force_channels=["dev", "canary"],
747 )
Jack Neus761e1842020-12-01 18:20:11 +0000748
Alex Klein1699fab2022-09-08 08:46:06 -0600749 @mock.patch.object(
750 pushimage,
751 "PushImage",
752 return_value={
753 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
754 "canary": ["gs://canary/instr1"],
755 },
756 )
757 def testOutput(self, _):
758 """Check that a call populates the response object."""
759 request = self._GetRequest(
760 profile="",
761 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
762 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
763 )
764 request.dest_bucket = "gs://foo"
765 response = self._GetResponse()
766 image_controller.PushImage(request, response, self.api_config)
767 self.assertEqual(
768 sorted([i.instructions_file_path for i in response.instructions]),
769 sorted(
770 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
771 ),
772 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700773
Alex Klein1699fab2022-09-08 08:46:06 -0600774 def testCallSucceeds(self, _):
775 """Check that a (dry run) call is made successfully."""
776 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
777 rc = image_controller.PushImage(
778 request, self._GetResponse(), self.api_config
779 )
780 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000781
Alex Klein1699fab2022-09-08 08:46:06 -0600782 def testCallFailsWithBadImageDir(self):
783 """Check that a (dry run) call fails when given a bad gs_image_dir."""
784 request = self._GetRequest(gs_image_dir="foo")
785 rc = image_controller.PushImage(
786 request, self._GetResponse, self.api_config
787 )
788 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)