blob: 8b2c93b170340b772c5d54045711b1bf7833422d [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(
Brian Norris4f251e42023-03-09 15:53:26 -0800252 path=self.tempdir / "chroot",
253 chrome_root=self.tempdir,
254 out_path=self.tempdir / "out",
Josiah Hounyode113e52022-11-30 06:30:33 +0000255 )
256 board = "chell"
257 sysroot_path = "/build/%s" % board
258 self.sysroot_class = sysroot_lib.Sysroot(sysroot_path)
259 self.build_target = build_target_lib.BuildTarget(board)
260
261 def _InputProto(
262 self,
263 artifact_types=_artifact_funcs.keys(),
264 ):
265 """Helper to build an input proto instance."""
266 return common_pb2.ArtifactsByService.Image(
267 output_artifacts=[
268 common_pb2.ArtifactsByService.Image.ArtifactInfo(
269 artifact_types=artifact_types
270 )
271 ]
272 )
273
274 def testNoArtifacts(self):
275 """Test GetArtifacts with no artifact types."""
276 in_proto = self._InputProto(artifact_types=[])
277 image_controller.GetArtifacts(
278 in_proto, self.chroot, self.sysroot_class, self.build_target, ""
279 )
280
281 for _, patch in self._mocks.items():
282 patch.assert_not_called()
283
284 def testArtifactsSuccess(self):
285 """Test GetArtifacts with all artifact types."""
286 image_controller.GetArtifacts(
287 self._InputProto(),
288 self.chroot,
289 self.sysroot_class,
290 self.build_target,
291 "",
292 )
293
294 for _, patch in self._mocks.items():
295 patch.assert_called_once()
296
297 def testArtifactsException(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700298 """Test with all artifact types when one type throws an exception."""
Josiah Hounyode113e52022-11-30 06:30:33 +0000299
300 self._mocks[
301 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES
302 ].side_effect = Exception("foo bar")
303 generated = image_controller.GetArtifacts(
304 self._InputProto(),
305 self.chroot,
306 self.sysroot_class,
307 self.build_target,
308 "",
309 )
310
311 for _, patch in self._mocks.items():
312 patch.assert_called_once()
313
314 found_artifact = False
315 for data in generated:
316 artifact_type = (
317 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
318 data["type"]
319 )
320 )
321 if artifact_type == "STRIPPED_PACKAGES":
322 found_artifact = True
323 self.assertTrue(data["failed"])
324 self.assertEqual(data["failure_reason"], "foo bar")
325 self.assertTrue(found_artifact)
326
327
Alex Klein1699fab2022-09-08 08:46:06 -0600328class RecoveryImageTest(
329 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
330):
331 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 def setUp(self):
334 self.response = image_pb2.CreateImageResult()
335 self.types = [
336 common_pb2.IMAGE_TYPE_BASE,
337 common_pb2.IMAGE_TYPE_RECOVERY,
338 ]
339 self.build_result = self._CreateMockBuildResult(
340 [common_pb2.IMAGE_TYPE_BASE]
341 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000342
Alex Klein1699fab2022-09-08 08:46:06 -0600343 self.PatchObject(
344 image_service,
345 "Build",
346 side_effect=[
347 self.build_result,
348 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
349 ],
350 )
351 self.copy_image_mock = self.PatchObject(
352 image_service,
353 "CopyBaseToRecovery",
354 side_effect=[
355 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
356 ],
357 )
358 self.recov_image_mock = self.PatchObject(
359 image_service,
360 "BuildRecoveryImage",
361 side_effect=[
362 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
363 ],
364 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000365
Alex Klein1699fab2022-09-08 08:46:06 -0600366 def _GetRequest(
367 self,
368 board=None,
369 types=None,
370 version=None,
371 builder_path=None,
372 disable_rootfs_verification=False,
373 base_is_recovery=False,
374 ):
375 """Helper to build a request instance."""
376 return image_pb2.CreateImageRequest(
377 build_target={"name": board},
378 image_types=types,
379 disable_rootfs_verification=disable_rootfs_verification,
380 version=version,
381 builder_path=builder_path,
382 base_is_recovery=base_is_recovery,
383 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 def _CreateMockBuildResult(
386 self, image_types: List[int]
387 ) -> Optional[image_service.BuildResult]:
388 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 Args:
Alex Kleinab87ceb2023-01-24 12:00:51 -0700391 image_types: A list of image types for which the mock BuildResult
392 has to be created.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600395 A list of mock BuildResult.
Alex Klein1699fab2022-09-08 08:46:06 -0600396 """
397 image_types_names = [
398 image_controller.SUPPORTED_IMAGE_TYPES[x]
399 for x in image_types
400 if image_controller.SUPPORTED_IMAGE_TYPES[x]
401 in constants.IMAGE_TYPE_TO_NAME
402 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000403
Alex Klein1699fab2022-09-08 08:46:06 -0600404 if not image_types_names:
405 if (
406 common_pb2.IMAGE_TYPE_FACTORY in image_types
407 and len(image_types) == 1
408 ):
409 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
410 else:
411 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000412
Alex Klein1699fab2022-09-08 08:46:06 -0600413 _build_result = image_service.BuildResult(image_types_names)
414 _build_result.return_code = 0
415 for image_type in image_types_names:
416 test_image = (
417 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
418 )
419 test_image.touch()
420 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000423
Alex Klein1699fab2022-09-08 08:46:06 -0600424 def testBaseIsRecoveryTrue(self):
425 """Test that cp is called."""
426 input_proto = self._GetRequest(
427 board="board", types=self.types, base_is_recovery=True
428 )
429 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 self.copy_image_mock.assert_called_with(
432 board="board",
433 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
434 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 def testBaseIsRecoveryFalse(self):
437 """Test that mod_image_for_recovery.sh is called."""
438 input_proto = self._GetRequest(
439 board="board", types=self.types, base_is_recovery=False
440 )
441 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000442
Alex Klein1699fab2022-09-08 08:46:06 -0600443 self.recov_image_mock.assert_called_with(
444 board="board",
445 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
446 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000447
Alex Klein2557b4f2019-07-11 14:34:00 -0600448
Alex Klein1699fab2022-09-08 08:46:06 -0600449class ImageSignerTestTest(
450 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
451):
452 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600453
Alex Klein1699fab2022-09-08 08:46:06 -0600454 def setUp(self):
455 self.image_path = os.path.join(self.tempdir, "image.bin")
456 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 osutils.SafeMakedirs(self.result_directory)
459 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 def testValidateOnly(self):
462 """Sanity check that validate-only calls don't execute any logic."""
463 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
464 input_proto = image_pb2.TestImageRequest()
465 input_proto.image.path = self.image_path
466 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 image_controller.SignerTest(
469 input_proto, output_proto, self.validate_only_config
470 )
Alex Klein231d2da2019-07-22 16:44:45 -0600471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700475 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600476 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
477 input_proto = image_pb2.TestImageRequest()
478 input_proto.image.path = self.image_path
479 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 image_controller.SignerTest(
482 input_proto, output_proto, self.mock_call_config
483 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 patch.assert_not_called()
486 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 def testMockError(self):
489 """Test that mock call does not execute any logic, returns error."""
490 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
491 input_proto = image_pb2.TestImageRequest()
492 input_proto.image.path = self.image_path
493 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 rc = image_controller.SignerTest(
496 input_proto, output_proto, self.mock_error_config
497 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700498
Alex Klein1699fab2022-09-08 08:46:06 -0600499 patch.assert_not_called()
500 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 def testSignerTestNoImage(self):
503 """Test function argument validation."""
504 input_proto = image_pb2.TestImageRequest()
505 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600506
Alex Klein1699fab2022-09-08 08:46:06 -0600507 # Nothing provided.
508 with self.assertRaises(cros_build_lib.DieSystemExit):
509 image_controller.SignerTest(
510 input_proto, output_proto, self.api_config
511 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600512
Alex Klein1699fab2022-09-08 08:46:06 -0600513 def testSignerTestSuccess(self):
514 """Test successful call handling."""
515 self.PatchObject(image_lib, "SecurityTest", return_value=True)
516 input_proto = image_pb2.TestImageRequest()
517 input_proto.image.path = self.image_path
518 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600521
Alex Klein1699fab2022-09-08 08:46:06 -0600522 def testSignerTestFailure(self):
523 """Test function output tests."""
524 input_proto = image_pb2.TestImageRequest()
525 input_proto.image.path = self.image_path
526 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 self.PatchObject(image_lib, "SecurityTest", return_value=False)
529 image_controller.SignerTest(input_proto, output_proto, self.api_config)
530 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600531
Michael Mortensenc83c9952019-08-05 12:15:12 -0600532
Alex Klein1699fab2022-09-08 08:46:06 -0600533class ImageTestTest(
534 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
535):
536 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700537
Alex Klein1699fab2022-09-08 08:46:06 -0600538 def setUp(self):
539 self.image_path = os.path.join(self.tempdir, "image.bin")
540 self.board = "board"
541 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 osutils.SafeMakedirs(self.result_directory)
544 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700545
Alex Klein1699fab2022-09-08 08:46:06 -0600546 def testValidateOnly(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700547 """Verify a validate-only call does not execute any logic."""
Alex Klein1699fab2022-09-08 08:46:06 -0600548 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 input_proto = image_pb2.TestImageRequest()
551 input_proto.image.path = self.image_path
552 input_proto.build_target.name = self.board
553 input_proto.result.directory = self.result_directory
554 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600555
Alex Klein1699fab2022-09-08 08:46:06 -0600556 image_controller.Test(
557 input_proto, output_proto, self.validate_only_config
558 )
559 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600560
Alex Klein1699fab2022-09-08 08:46:06 -0600561 def testMockCall(self):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700562 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600563 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700564
Alex Klein1699fab2022-09-08 08:46:06 -0600565 input_proto = image_pb2.TestImageRequest()
566 input_proto.image.path = self.image_path
567 input_proto.build_target.name = self.board
568 input_proto.result.directory = self.result_directory
569 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700570
Alex Klein1699fab2022-09-08 08:46:06 -0600571 image_controller.Test(input_proto, output_proto, self.mock_call_config)
572 patch.assert_not_called()
573 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700574
Alex Klein1699fab2022-09-08 08:46:06 -0600575 def testMockError(self):
576 """Test that mock call does not execute any logic, returns error."""
577 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700578
Alex Klein1699fab2022-09-08 08:46:06 -0600579 input_proto = image_pb2.TestImageRequest()
580 input_proto.image.path = self.image_path
581 input_proto.build_target.name = self.board
582 input_proto.result.directory = self.result_directory
583 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700584
Alex Klein1699fab2022-09-08 08:46:06 -0600585 rc = image_controller.Test(
586 input_proto, output_proto, self.mock_error_config
587 )
588 patch.assert_not_called()
589 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 def testTestArgumentValidation(self):
592 """Test function argument validation tests."""
593 self.PatchObject(image_service, "Test", return_value=True)
594 input_proto = image_pb2.TestImageRequest()
595 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700596
Alex Klein1699fab2022-09-08 08:46:06 -0600597 # Nothing provided.
598 with self.assertRaises(cros_build_lib.DieSystemExit):
599 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700600
Alex Klein1699fab2022-09-08 08:46:06 -0600601 # Just one argument.
602 input_proto.build_target.name = self.board
603 with self.assertRaises(cros_build_lib.DieSystemExit):
604 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700605
Alex Klein1699fab2022-09-08 08:46:06 -0600606 # Two arguments provided.
607 input_proto.result.directory = self.result_directory
608 with self.assertRaises(cros_build_lib.DieSystemExit):
609 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700610
Alex Klein1699fab2022-09-08 08:46:06 -0600611 # Invalid image path.
612 input_proto.image.path = "/invalid/image/path"
613 with self.assertRaises(cros_build_lib.DieSystemExit):
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 # All valid arguments.
617 input_proto.image.path = self.image_path
618 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700619
Alex Klein1699fab2022-09-08 08:46:06 -0600620 def testTestOutputHandling(self):
621 """Test function output tests."""
622 input_proto = image_pb2.TestImageRequest()
623 input_proto.image.path = self.image_path
624 input_proto.build_target.name = self.board
625 input_proto.result.directory = self.result_directory
626 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700627
Alex Klein1699fab2022-09-08 08:46:06 -0600628 self.PatchObject(image_service, "Test", return_value=True)
629 image_controller.Test(input_proto, output_proto, self.api_config)
630 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700631
Alex Klein1699fab2022-09-08 08:46:06 -0600632 self.PatchObject(image_service, "Test", return_value=False)
633 image_controller.Test(input_proto, output_proto, self.api_config)
634 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000635
636
Greg Edelston6902e3d2022-01-27 12:19:38 -0700637class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600638 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 def _GetRequest(
641 self,
642 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
643 build_target_name="atlas",
644 profile="foo",
645 sign_types=None,
646 dryrun=True,
647 channels=None,
648 ):
649 return image_pb2.PushImageRequest(
650 gs_image_dir=gs_image_dir,
651 sysroot=sysroot_pb2.Sysroot(
652 build_target=common_pb2.BuildTarget(name=build_target_name)
653 ),
654 profile=common_pb2.Profile(name=profile),
655 sign_types=sign_types,
656 dryrun=dryrun,
657 channels=channels,
658 )
Jack Neus761e1842020-12-01 18:20:11 +0000659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 def _GetResponse(self):
661 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000662
Alex Klein1699fab2022-09-08 08:46:06 -0600663 @mock.patch.object(pushimage, "PushImage", return_value={})
664 def testValidateOnly(self, MockPushImage):
665 """Check that a validate only call does not execute any logic."""
666 req = self._GetRequest(
667 sign_types=[
668 common_pb2.IMAGE_TYPE_RECOVERY,
669 common_pb2.IMAGE_TYPE_FACTORY,
670 common_pb2.IMAGE_TYPE_FIRMWARE,
671 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
672 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
673 common_pb2.IMAGE_TYPE_BASE,
674 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
675 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
676 ]
677 )
678 rc = image_controller.PushImage(
679 req, self.NewResponse(), self.validate_only_config
680 )
681 MockPushImage.assert_not_called()
682 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000683
Alex Klein1699fab2022-09-08 08:46:06 -0600684 @mock.patch.object(pushimage, "PushImage", return_value={})
685 def testValidateOnlyInvalid(self, MockPushImage):
686 """Check that validate call rejects invalid sign types."""
687 # Pass unsupported image type.
688 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
689 rc = image_controller.PushImage(
690 req, self._GetResponse(), self.validate_only_config
691 )
692 MockPushImage.assert_not_called()
693 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000694
Alex Klein1699fab2022-09-08 08:46:06 -0600695 @mock.patch.object(pushimage, "PushImage", return_value={})
696 def testMockCall(self, MockPushImage):
Alex Kleinab87ceb2023-01-24 12:00:51 -0700697 """Test mock call does not execute any logic, returns mocked value."""
Alex Klein1699fab2022-09-08 08:46:06 -0600698 rc = image_controller.PushImage(
699 self._GetRequest(), self._GetResponse(), self.mock_call_config
700 )
701 MockPushImage.assert_not_called()
702 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000703
Alex Klein1699fab2022-09-08 08:46:06 -0600704 @mock.patch.object(pushimage, "PushImage", return_value={})
705 def testMockError(self, MockPushImage):
706 """Test that mock call does not execute any logic, returns error."""
707 rc = image_controller.PushImage(
708 self._GetRequest(), self._GetResponse(), self.mock_error_config
709 )
710 MockPushImage.assert_not_called()
711 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000712
Alex Klein1699fab2022-09-08 08:46:06 -0600713 @mock.patch.object(pushimage, "PushImage", return_value={})
714 def testNoBuildTarget(self, _):
715 """Test no build target given fails."""
716 request = self._GetRequest(build_target_name="")
717 with self.assertRaises(cros_build_lib.DieSystemExit):
718 image_controller.PushImage(
719 request, self._GetResponse(), self.api_config
720 )
Jack Neus761e1842020-12-01 18:20:11 +0000721
Alex Klein1699fab2022-09-08 08:46:06 -0600722 @mock.patch.object(pushimage, "PushImage", return_value={})
723 def testNoGsImageDir(self, _):
724 """Test no image dir given fails."""
725 request = self._GetRequest(gs_image_dir="")
726 with self.assertRaises(cros_build_lib.DieSystemExit):
727 image_controller.PushImage(
728 request, self._GetResponse(), self.api_config
729 )
Jack Neus761e1842020-12-01 18:20:11 +0000730
Alex Klein1699fab2022-09-08 08:46:06 -0600731 @mock.patch.object(pushimage, "PushImage", return_value={})
732 def testCallCorrect(self, MockPushImage):
733 """Check that a call is called with the correct parameters."""
734 request = self._GetRequest(
735 dryrun=False,
736 profile="",
737 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
738 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
739 )
740 request.dest_bucket = "gs://foo"
741 image_controller.PushImage(
742 request, self._GetResponse(), self.api_config
743 )
744 MockPushImage.assert_called_with(
745 request.gs_image_dir,
746 request.sysroot.build_target.name,
Mike Frysinger9d1253c2023-02-02 09:00:07 -0500747 dryrun=request.dryrun,
Alex Klein1699fab2022-09-08 08:46:06 -0600748 sign_types=["recovery"],
749 dest_bucket=request.dest_bucket,
750 force_channels=["dev", "canary"],
751 )
Jack Neus761e1842020-12-01 18:20:11 +0000752
Alex Klein1699fab2022-09-08 08:46:06 -0600753 @mock.patch.object(
754 pushimage,
755 "PushImage",
756 return_value={
757 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
758 "canary": ["gs://canary/instr1"],
759 },
760 )
761 def testOutput(self, _):
762 """Check that a call populates the response object."""
763 request = self._GetRequest(
764 profile="",
765 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
766 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
767 )
768 request.dest_bucket = "gs://foo"
769 response = self._GetResponse()
770 image_controller.PushImage(request, response, self.api_config)
771 self.assertEqual(
772 sorted([i.instructions_file_path for i in response.instructions]),
773 sorted(
774 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
775 ),
776 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700777
Alex Klein1699fab2022-09-08 08:46:06 -0600778 def testCallSucceeds(self, _):
779 """Check that a (dry run) call is made successfully."""
780 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
781 rc = image_controller.PushImage(
782 request, self._GetResponse(), self.api_config
783 )
784 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000785
Alex Klein1699fab2022-09-08 08:46:06 -0600786 def testCallFailsWithBadImageDir(self):
787 """Check that a (dry run) call fails when given a bad gs_image_dir."""
788 request = self._GetRequest(gs_image_dir="foo")
789 rc = image_controller.PushImage(
790 request, self._GetResponse, self.api_config
791 )
792 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)