blob: c49498d7d4886ca05a7726c0256c36fb111cabfa [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):
Alex Kleinab87ceb2023-01-24 12:00:51 -070056 """Verify a validate-only call does not execute any logic."""
Alex Klein1699fab2022-09-08 08:46:06 -060057 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):
Alex Kleinab87ceb2023-01-24 12:00:51 -070066 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -060067 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
Alex Kleinab87ceb2023-01-24 12:00:51 -0700235 # pylint: disable=line-too-long
Josiah Hounyode113e52022-11-30 06:30:33 +0000236 _artifact_funcs = {
237 common_pb2.ArtifactsByService.Image.ArtifactType.DLC_IMAGE: image_service.copy_dlc_image,
238 common_pb2.ArtifactsByService.Image.ArtifactType.LICENSE_CREDITS: image_service.copy_license_credits,
239 common_pb2.ArtifactsByService.Image.ArtifactType.FACTORY_IMAGE: image_service.create_factory_image_zip,
240 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES: image_service.create_stripped_packages_tar,
Jack Neus91846002022-12-01 23:49:22 +0000241 common_pb2.ArtifactsByService.Image.ArtifactType.IMAGE_SCRIPTS: image_service.create_image_scripts_archive,
Josiah Hounyode113e52022-11-30 06:30:33 +0000242 }
Alex Kleinab87ceb2023-01-24 12:00:51 -0700243 # pylint: enable=line-too-long
Josiah Hounyode113e52022-11-30 06:30:33 +0000244
245 def setUp(self):
246 self._mocks = {}
247 for artifact, func in self._artifact_funcs.items():
248 self._mocks[artifact] = self.PatchObject(
249 image_service, func.__name__
250 )
251 self.chroot = chroot_lib.Chroot(
252 path=self.tempdir, chrome_root=self.tempdir
253 )
254 board = "chell"
255 sysroot_path = "/build/%s" % board
256 self.sysroot_class = sysroot_lib.Sysroot(sysroot_path)
257 self.build_target = build_target_lib.BuildTarget(board)
258
259 def _InputProto(
260 self,
261 artifact_types=_artifact_funcs.keys(),
262 ):
263 """Helper to build an input proto instance."""
264 return common_pb2.ArtifactsByService.Image(
265 output_artifacts=[
266 common_pb2.ArtifactsByService.Image.ArtifactInfo(
267 artifact_types=artifact_types
268 )
269 ]
270 )
271
272 def testNoArtifacts(self):
273 """Test GetArtifacts with no artifact types."""
274 in_proto = self._InputProto(artifact_types=[])
275 image_controller.GetArtifacts(
276 in_proto, self.chroot, self.sysroot_class, self.build_target, ""
277 )
278
279 for _, patch in self._mocks.items():
280 patch.assert_not_called()
281
282 def testArtifactsSuccess(self):
283 """Test GetArtifacts with all artifact types."""
284 image_controller.GetArtifacts(
285 self._InputProto(),
286 self.chroot,
287 self.sysroot_class,
288 self.build_target,
289 "",
290 )
291
292 for _, patch in self._mocks.items():
293 patch.assert_called_once()
294
295 def testArtifactsException(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700296 """Test with all artifact types when one type throws an exception."""
Josiah Hounyode113e52022-11-30 06:30:33 +0000297
298 self._mocks[
299 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES
300 ].side_effect = Exception("foo bar")
301 generated = image_controller.GetArtifacts(
302 self._InputProto(),
303 self.chroot,
304 self.sysroot_class,
305 self.build_target,
306 "",
307 )
308
309 for _, patch in self._mocks.items():
310 patch.assert_called_once()
311
312 found_artifact = False
313 for data in generated:
314 artifact_type = (
315 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
316 data["type"]
317 )
318 )
319 if artifact_type == "STRIPPED_PACKAGES":
320 found_artifact = True
321 self.assertTrue(data["failed"])
322 self.assertEqual(data["failure_reason"], "foo bar")
323 self.assertTrue(found_artifact)
324
325
Alex Klein1699fab2022-09-08 08:46:06 -0600326class RecoveryImageTest(
327 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
328):
329 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000330
Alex Klein1699fab2022-09-08 08:46:06 -0600331 def setUp(self):
332 self.response = image_pb2.CreateImageResult()
333 self.types = [
334 common_pb2.IMAGE_TYPE_BASE,
335 common_pb2.IMAGE_TYPE_RECOVERY,
336 ]
337 self.build_result = self._CreateMockBuildResult(
338 [common_pb2.IMAGE_TYPE_BASE]
339 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000340
Alex Klein1699fab2022-09-08 08:46:06 -0600341 self.PatchObject(
342 image_service,
343 "Build",
344 side_effect=[
345 self.build_result,
346 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
347 ],
348 )
349 self.copy_image_mock = self.PatchObject(
350 image_service,
351 "CopyBaseToRecovery",
352 side_effect=[
353 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
354 ],
355 )
356 self.recov_image_mock = self.PatchObject(
357 image_service,
358 "BuildRecoveryImage",
359 side_effect=[
360 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
361 ],
362 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000363
Alex Klein1699fab2022-09-08 08:46:06 -0600364 def _GetRequest(
365 self,
366 board=None,
367 types=None,
368 version=None,
369 builder_path=None,
370 disable_rootfs_verification=False,
371 base_is_recovery=False,
372 ):
373 """Helper to build a request instance."""
374 return image_pb2.CreateImageRequest(
375 build_target={"name": board},
376 image_types=types,
377 disable_rootfs_verification=disable_rootfs_verification,
378 version=version,
379 builder_path=builder_path,
380 base_is_recovery=base_is_recovery,
381 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000382
Alex Klein1699fab2022-09-08 08:46:06 -0600383 def _CreateMockBuildResult(
384 self, image_types: List[int]
385 ) -> Optional[image_service.BuildResult]:
386 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 Args:
Alex Kleinab87ceb2023-01-24 12:00:51 -0700389 image_types: A list of image types for which the mock BuildResult
390 has to be created.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600393 A list of mock BuildResult.
Alex Klein1699fab2022-09-08 08:46:06 -0600394 """
395 image_types_names = [
396 image_controller.SUPPORTED_IMAGE_TYPES[x]
397 for x in image_types
398 if image_controller.SUPPORTED_IMAGE_TYPES[x]
399 in constants.IMAGE_TYPE_TO_NAME
400 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000401
Alex Klein1699fab2022-09-08 08:46:06 -0600402 if not image_types_names:
403 if (
404 common_pb2.IMAGE_TYPE_FACTORY in image_types
405 and len(image_types) == 1
406 ):
407 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
408 else:
409 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 _build_result = image_service.BuildResult(image_types_names)
412 _build_result.return_code = 0
413 for image_type in image_types_names:
414 test_image = (
415 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
416 )
417 test_image.touch()
418 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000419
Alex Klein1699fab2022-09-08 08:46:06 -0600420 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 def testBaseIsRecoveryTrue(self):
423 """Test that cp is called."""
424 input_proto = self._GetRequest(
425 board="board", types=self.types, base_is_recovery=True
426 )
427 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 self.copy_image_mock.assert_called_with(
430 board="board",
431 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
432 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000433
Alex Klein1699fab2022-09-08 08:46:06 -0600434 def testBaseIsRecoveryFalse(self):
435 """Test that mod_image_for_recovery.sh is called."""
436 input_proto = self._GetRequest(
437 board="board", types=self.types, base_is_recovery=False
438 )
439 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 self.recov_image_mock.assert_called_with(
442 board="board",
443 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
444 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000445
Alex Klein2557b4f2019-07-11 14:34:00 -0600446
Alex Klein1699fab2022-09-08 08:46:06 -0600447class ImageSignerTestTest(
448 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
449):
450 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600451
Alex Klein1699fab2022-09-08 08:46:06 -0600452 def setUp(self):
453 self.image_path = os.path.join(self.tempdir, "image.bin")
454 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 osutils.SafeMakedirs(self.result_directory)
457 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600458
Alex Klein1699fab2022-09-08 08:46:06 -0600459 def testValidateOnly(self):
460 """Sanity check that validate-only calls don't execute any logic."""
461 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
462 input_proto = image_pb2.TestImageRequest()
463 input_proto.image.path = self.image_path
464 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600465
Alex Klein1699fab2022-09-08 08:46:06 -0600466 image_controller.SignerTest(
467 input_proto, output_proto, self.validate_only_config
468 )
Alex Klein231d2da2019-07-22 16:44:45 -0600469
Alex Klein1699fab2022-09-08 08:46:06 -0600470 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700473 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600474 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
475 input_proto = image_pb2.TestImageRequest()
476 input_proto.image.path = self.image_path
477 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700478
Alex Klein1699fab2022-09-08 08:46:06 -0600479 image_controller.SignerTest(
480 input_proto, output_proto, self.mock_call_config
481 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 patch.assert_not_called()
484 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700485
Alex Klein1699fab2022-09-08 08:46:06 -0600486 def testMockError(self):
487 """Test that mock call does not execute any logic, returns error."""
488 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
489 input_proto = image_pb2.TestImageRequest()
490 input_proto.image.path = self.image_path
491 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700492
Alex Klein1699fab2022-09-08 08:46:06 -0600493 rc = image_controller.SignerTest(
494 input_proto, output_proto, self.mock_error_config
495 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 patch.assert_not_called()
498 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700499
Alex Klein1699fab2022-09-08 08:46:06 -0600500 def testSignerTestNoImage(self):
501 """Test function argument validation."""
502 input_proto = image_pb2.TestImageRequest()
503 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 # Nothing provided.
506 with self.assertRaises(cros_build_lib.DieSystemExit):
507 image_controller.SignerTest(
508 input_proto, output_proto, self.api_config
509 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600510
Alex Klein1699fab2022-09-08 08:46:06 -0600511 def testSignerTestSuccess(self):
512 """Test successful call handling."""
513 self.PatchObject(image_lib, "SecurityTest", return_value=True)
514 input_proto = image_pb2.TestImageRequest()
515 input_proto.image.path = self.image_path
516 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600517
Alex Klein1699fab2022-09-08 08:46:06 -0600518 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 def testSignerTestFailure(self):
521 """Test function output tests."""
522 input_proto = image_pb2.TestImageRequest()
523 input_proto.image.path = self.image_path
524 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 self.PatchObject(image_lib, "SecurityTest", return_value=False)
527 image_controller.SignerTest(input_proto, output_proto, self.api_config)
528 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600529
Michael Mortensenc83c9952019-08-05 12:15:12 -0600530
Alex Klein1699fab2022-09-08 08:46:06 -0600531class ImageTestTest(
532 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
533):
534 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 def setUp(self):
537 self.image_path = os.path.join(self.tempdir, "image.bin")
538 self.board = "board"
539 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700540
Alex Klein1699fab2022-09-08 08:46:06 -0600541 osutils.SafeMakedirs(self.result_directory)
542 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 def testValidateOnly(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700545 """Verify a validate-only call does not execute any logic."""
Alex Klein1699fab2022-09-08 08:46:06 -0600546 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600547
Alex Klein1699fab2022-09-08 08:46:06 -0600548 input_proto = image_pb2.TestImageRequest()
549 input_proto.image.path = self.image_path
550 input_proto.build_target.name = self.board
551 input_proto.result.directory = self.result_directory
552 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 image_controller.Test(
555 input_proto, output_proto, self.validate_only_config
556 )
557 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600558
Alex Klein1699fab2022-09-08 08:46:06 -0600559 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700560 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600561 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700562
Alex Klein1699fab2022-09-08 08:46:06 -0600563 input_proto = image_pb2.TestImageRequest()
564 input_proto.image.path = self.image_path
565 input_proto.build_target.name = self.board
566 input_proto.result.directory = self.result_directory
567 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700568
Alex Klein1699fab2022-09-08 08:46:06 -0600569 image_controller.Test(input_proto, output_proto, self.mock_call_config)
570 patch.assert_not_called()
571 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700572
Alex Klein1699fab2022-09-08 08:46:06 -0600573 def testMockError(self):
574 """Test that mock call does not execute any logic, returns error."""
575 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700576
Alex Klein1699fab2022-09-08 08:46:06 -0600577 input_proto = image_pb2.TestImageRequest()
578 input_proto.image.path = self.image_path
579 input_proto.build_target.name = self.board
580 input_proto.result.directory = self.result_directory
581 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 rc = image_controller.Test(
584 input_proto, output_proto, self.mock_error_config
585 )
586 patch.assert_not_called()
587 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700588
Alex Klein1699fab2022-09-08 08:46:06 -0600589 def testTestArgumentValidation(self):
590 """Test function argument validation tests."""
591 self.PatchObject(image_service, "Test", return_value=True)
592 input_proto = image_pb2.TestImageRequest()
593 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700594
Alex Klein1699fab2022-09-08 08:46:06 -0600595 # Nothing provided.
596 with self.assertRaises(cros_build_lib.DieSystemExit):
597 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700598
Alex Klein1699fab2022-09-08 08:46:06 -0600599 # Just one argument.
600 input_proto.build_target.name = self.board
601 with self.assertRaises(cros_build_lib.DieSystemExit):
602 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700603
Alex Klein1699fab2022-09-08 08:46:06 -0600604 # Two arguments provided.
605 input_proto.result.directory = self.result_directory
606 with self.assertRaises(cros_build_lib.DieSystemExit):
607 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700608
Alex Klein1699fab2022-09-08 08:46:06 -0600609 # Invalid image path.
610 input_proto.image.path = "/invalid/image/path"
611 with self.assertRaises(cros_build_lib.DieSystemExit):
612 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 # All valid arguments.
615 input_proto.image.path = self.image_path
616 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700617
Alex Klein1699fab2022-09-08 08:46:06 -0600618 def testTestOutputHandling(self):
619 """Test function output tests."""
620 input_proto = image_pb2.TestImageRequest()
621 input_proto.image.path = self.image_path
622 input_proto.build_target.name = self.board
623 input_proto.result.directory = self.result_directory
624 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700625
Alex Klein1699fab2022-09-08 08:46:06 -0600626 self.PatchObject(image_service, "Test", return_value=True)
627 image_controller.Test(input_proto, output_proto, self.api_config)
628 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700629
Alex Klein1699fab2022-09-08 08:46:06 -0600630 self.PatchObject(image_service, "Test", return_value=False)
631 image_controller.Test(input_proto, output_proto, self.api_config)
632 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000633
634
Greg Edelston6902e3d2022-01-27 12:19:38 -0700635class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600636 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000637
Alex Klein1699fab2022-09-08 08:46:06 -0600638 def _GetRequest(
639 self,
640 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
641 build_target_name="atlas",
642 profile="foo",
643 sign_types=None,
644 dryrun=True,
645 channels=None,
646 ):
647 return image_pb2.PushImageRequest(
648 gs_image_dir=gs_image_dir,
649 sysroot=sysroot_pb2.Sysroot(
650 build_target=common_pb2.BuildTarget(name=build_target_name)
651 ),
652 profile=common_pb2.Profile(name=profile),
653 sign_types=sign_types,
654 dryrun=dryrun,
655 channels=channels,
656 )
Jack Neus761e1842020-12-01 18:20:11 +0000657
Alex Klein1699fab2022-09-08 08:46:06 -0600658 def _GetResponse(self):
659 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000660
Alex Klein1699fab2022-09-08 08:46:06 -0600661 @mock.patch.object(pushimage, "PushImage", return_value={})
662 def testValidateOnly(self, MockPushImage):
663 """Check that a validate only call does not execute any logic."""
664 req = self._GetRequest(
665 sign_types=[
666 common_pb2.IMAGE_TYPE_RECOVERY,
667 common_pb2.IMAGE_TYPE_FACTORY,
668 common_pb2.IMAGE_TYPE_FIRMWARE,
669 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
670 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
671 common_pb2.IMAGE_TYPE_BASE,
672 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
673 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
674 ]
675 )
676 rc = image_controller.PushImage(
677 req, self.NewResponse(), self.validate_only_config
678 )
679 MockPushImage.assert_not_called()
680 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000681
Alex Klein1699fab2022-09-08 08:46:06 -0600682 @mock.patch.object(pushimage, "PushImage", return_value={})
683 def testValidateOnlyInvalid(self, MockPushImage):
684 """Check that validate call rejects invalid sign types."""
685 # Pass unsupported image type.
686 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
687 rc = image_controller.PushImage(
688 req, self._GetResponse(), self.validate_only_config
689 )
690 MockPushImage.assert_not_called()
691 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000692
Alex Klein1699fab2022-09-08 08:46:06 -0600693 @mock.patch.object(pushimage, "PushImage", return_value={})
694 def testMockCall(self, MockPushImage):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700695 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600696 rc = image_controller.PushImage(
697 self._GetRequest(), self._GetResponse(), self.mock_call_config
698 )
699 MockPushImage.assert_not_called()
700 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000701
Alex Klein1699fab2022-09-08 08:46:06 -0600702 @mock.patch.object(pushimage, "PushImage", return_value={})
703 def testMockError(self, MockPushImage):
704 """Test that mock call does not execute any logic, returns error."""
705 rc = image_controller.PushImage(
706 self._GetRequest(), self._GetResponse(), self.mock_error_config
707 )
708 MockPushImage.assert_not_called()
709 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000710
Alex Klein1699fab2022-09-08 08:46:06 -0600711 @mock.patch.object(pushimage, "PushImage", return_value={})
712 def testNoBuildTarget(self, _):
713 """Test no build target given fails."""
714 request = self._GetRequest(build_target_name="")
715 with self.assertRaises(cros_build_lib.DieSystemExit):
716 image_controller.PushImage(
717 request, self._GetResponse(), self.api_config
718 )
Jack Neus761e1842020-12-01 18:20:11 +0000719
Alex Klein1699fab2022-09-08 08:46:06 -0600720 @mock.patch.object(pushimage, "PushImage", return_value={})
721 def testNoGsImageDir(self, _):
722 """Test no image dir given fails."""
723 request = self._GetRequest(gs_image_dir="")
724 with self.assertRaises(cros_build_lib.DieSystemExit):
725 image_controller.PushImage(
726 request, self._GetResponse(), self.api_config
727 )
Jack Neus761e1842020-12-01 18:20:11 +0000728
Alex Klein1699fab2022-09-08 08:46:06 -0600729 @mock.patch.object(pushimage, "PushImage", return_value={})
730 def testCallCorrect(self, MockPushImage):
731 """Check that a call is called with the correct parameters."""
732 request = self._GetRequest(
733 dryrun=False,
734 profile="",
735 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
736 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
737 )
738 request.dest_bucket = "gs://foo"
739 image_controller.PushImage(
740 request, self._GetResponse(), self.api_config
741 )
742 MockPushImage.assert_called_with(
743 request.gs_image_dir,
744 request.sysroot.build_target.name,
745 dry_run=request.dryrun,
746 sign_types=["recovery"],
747 dest_bucket=request.dest_bucket,
748 force_channels=["dev", "canary"],
749 )
Jack Neus761e1842020-12-01 18:20:11 +0000750
Alex Klein1699fab2022-09-08 08:46:06 -0600751 @mock.patch.object(
752 pushimage,
753 "PushImage",
754 return_value={
755 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
756 "canary": ["gs://canary/instr1"],
757 },
758 )
759 def testOutput(self, _):
760 """Check that a call populates the response object."""
761 request = self._GetRequest(
762 profile="",
763 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
764 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
765 )
766 request.dest_bucket = "gs://foo"
767 response = self._GetResponse()
768 image_controller.PushImage(request, response, self.api_config)
769 self.assertEqual(
770 sorted([i.instructions_file_path for i in response.instructions]),
771 sorted(
772 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
773 ),
774 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700775
Alex Klein1699fab2022-09-08 08:46:06 -0600776 def testCallSucceeds(self, _):
777 """Check that a (dry run) call is made successfully."""
778 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
779 rc = image_controller.PushImage(
780 request, self._GetResponse(), self.api_config
781 )
782 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000783
Alex Klein1699fab2022-09-08 08:46:06 -0600784 def testCallFailsWithBadImageDir(self):
785 """Check that a (dry run) call fails when given a bad gs_image_dir."""
786 request = self._GetRequest(gs_image_dir="foo")
787 rc = image_controller.PushImage(
788 request, self._GetResponse, self.api_config
789 )
790 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)