blob: 57e64dbf83de1841101fd541e074fc74f4ca270d [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,
240 }
241
242 def setUp(self):
243 self._mocks = {}
244 for artifact, func in self._artifact_funcs.items():
245 self._mocks[artifact] = self.PatchObject(
246 image_service, func.__name__
247 )
248 self.chroot = chroot_lib.Chroot(
249 path=self.tempdir, chrome_root=self.tempdir
250 )
251 board = "chell"
252 sysroot_path = "/build/%s" % board
253 self.sysroot_class = sysroot_lib.Sysroot(sysroot_path)
254 self.build_target = build_target_lib.BuildTarget(board)
255
256 def _InputProto(
257 self,
258 artifact_types=_artifact_funcs.keys(),
259 ):
260 """Helper to build an input proto instance."""
261 return common_pb2.ArtifactsByService.Image(
262 output_artifacts=[
263 common_pb2.ArtifactsByService.Image.ArtifactInfo(
264 artifact_types=artifact_types
265 )
266 ]
267 )
268
269 def testNoArtifacts(self):
270 """Test GetArtifacts with no artifact types."""
271 in_proto = self._InputProto(artifact_types=[])
272 image_controller.GetArtifacts(
273 in_proto, self.chroot, self.sysroot_class, self.build_target, ""
274 )
275
276 for _, patch in self._mocks.items():
277 patch.assert_not_called()
278
279 def testArtifactsSuccess(self):
280 """Test GetArtifacts with all artifact types."""
281 image_controller.GetArtifacts(
282 self._InputProto(),
283 self.chroot,
284 self.sysroot_class,
285 self.build_target,
286 "",
287 )
288
289 for _, patch in self._mocks.items():
290 patch.assert_called_once()
291
292 def testArtifactsException(self):
293 """Test GetArtifacts with all artifact types when one type throws an exception."""
294
295 self._mocks[
296 common_pb2.ArtifactsByService.Image.ArtifactType.STRIPPED_PACKAGES
297 ].side_effect = Exception("foo bar")
298 generated = image_controller.GetArtifacts(
299 self._InputProto(),
300 self.chroot,
301 self.sysroot_class,
302 self.build_target,
303 "",
304 )
305
306 for _, patch in self._mocks.items():
307 patch.assert_called_once()
308
309 found_artifact = False
310 for data in generated:
311 artifact_type = (
312 common_pb2.ArtifactsByService.Image.ArtifactType.Name(
313 data["type"]
314 )
315 )
316 if artifact_type == "STRIPPED_PACKAGES":
317 found_artifact = True
318 self.assertTrue(data["failed"])
319 self.assertEqual(data["failure_reason"], "foo bar")
320 self.assertTrue(found_artifact)
321
322
Alex Klein1699fab2022-09-08 08:46:06 -0600323class RecoveryImageTest(
324 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
325):
326 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 def setUp(self):
329 self.response = image_pb2.CreateImageResult()
330 self.types = [
331 common_pb2.IMAGE_TYPE_BASE,
332 common_pb2.IMAGE_TYPE_RECOVERY,
333 ]
334 self.build_result = self._CreateMockBuildResult(
335 [common_pb2.IMAGE_TYPE_BASE]
336 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 self.PatchObject(
339 image_service,
340 "Build",
341 side_effect=[
342 self.build_result,
343 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
344 ],
345 )
346 self.copy_image_mock = self.PatchObject(
347 image_service,
348 "CopyBaseToRecovery",
349 side_effect=[
350 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
351 ],
352 )
353 self.recov_image_mock = self.PatchObject(
354 image_service,
355 "BuildRecoveryImage",
356 side_effect=[
357 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
358 ],
359 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000360
Alex Klein1699fab2022-09-08 08:46:06 -0600361 def _GetRequest(
362 self,
363 board=None,
364 types=None,
365 version=None,
366 builder_path=None,
367 disable_rootfs_verification=False,
368 base_is_recovery=False,
369 ):
370 """Helper to build a request instance."""
371 return image_pb2.CreateImageRequest(
372 build_target={"name": board},
373 image_types=types,
374 disable_rootfs_verification=disable_rootfs_verification,
375 version=version,
376 builder_path=builder_path,
377 base_is_recovery=base_is_recovery,
378 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 def _CreateMockBuildResult(
381 self, image_types: List[int]
382 ) -> Optional[image_service.BuildResult]:
383 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600386 image_types: A list of image types for which the mock BuildResult has to
387 be creates.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600390 A list of mock BuildResult.
Alex Klein1699fab2022-09-08 08:46:06 -0600391 """
392 image_types_names = [
393 image_controller.SUPPORTED_IMAGE_TYPES[x]
394 for x in image_types
395 if image_controller.SUPPORTED_IMAGE_TYPES[x]
396 in constants.IMAGE_TYPE_TO_NAME
397 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000398
Alex Klein1699fab2022-09-08 08:46:06 -0600399 if not image_types_names:
400 if (
401 common_pb2.IMAGE_TYPE_FACTORY in image_types
402 and len(image_types) == 1
403 ):
404 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
405 else:
406 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000407
Alex Klein1699fab2022-09-08 08:46:06 -0600408 _build_result = image_service.BuildResult(image_types_names)
409 _build_result.return_code = 0
410 for image_type in image_types_names:
411 test_image = (
412 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
413 )
414 test_image.touch()
415 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000418
Alex Klein1699fab2022-09-08 08:46:06 -0600419 def testBaseIsRecoveryTrue(self):
420 """Test that cp is called."""
421 input_proto = self._GetRequest(
422 board="board", types=self.types, base_is_recovery=True
423 )
424 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000425
Alex Klein1699fab2022-09-08 08:46:06 -0600426 self.copy_image_mock.assert_called_with(
427 board="board",
428 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
429 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 def testBaseIsRecoveryFalse(self):
432 """Test that mod_image_for_recovery.sh is called."""
433 input_proto = self._GetRequest(
434 board="board", types=self.types, base_is_recovery=False
435 )
436 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 self.recov_image_mock.assert_called_with(
439 board="board",
440 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
441 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000442
Alex Klein2557b4f2019-07-11 14:34:00 -0600443
Alex Klein1699fab2022-09-08 08:46:06 -0600444class ImageSignerTestTest(
445 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
446):
447 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 def setUp(self):
450 self.image_path = os.path.join(self.tempdir, "image.bin")
451 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600452
Alex Klein1699fab2022-09-08 08:46:06 -0600453 osutils.SafeMakedirs(self.result_directory)
454 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 def testValidateOnly(self):
457 """Sanity check that validate-only calls don't execute any logic."""
458 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
459 input_proto = image_pb2.TestImageRequest()
460 input_proto.image.path = self.image_path
461 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600462
Alex Klein1699fab2022-09-08 08:46:06 -0600463 image_controller.SignerTest(
464 input_proto, output_proto, self.validate_only_config
465 )
Alex Klein231d2da2019-07-22 16:44:45 -0600466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600468
Alex Klein1699fab2022-09-08 08:46:06 -0600469 def testMockCall(self):
470 """Test that mock call does not execute any logic, returns mocked value."""
471 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
472 input_proto = image_pb2.TestImageRequest()
473 input_proto.image.path = self.image_path
474 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 image_controller.SignerTest(
477 input_proto, output_proto, self.mock_call_config
478 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700479
Alex Klein1699fab2022-09-08 08:46:06 -0600480 patch.assert_not_called()
481 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 def testMockError(self):
484 """Test that mock call does not execute any logic, returns error."""
485 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
486 input_proto = image_pb2.TestImageRequest()
487 input_proto.image.path = self.image_path
488 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700489
Alex Klein1699fab2022-09-08 08:46:06 -0600490 rc = image_controller.SignerTest(
491 input_proto, output_proto, self.mock_error_config
492 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 patch.assert_not_called()
495 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 def testSignerTestNoImage(self):
498 """Test function argument validation."""
499 input_proto = image_pb2.TestImageRequest()
500 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 # Nothing provided.
503 with self.assertRaises(cros_build_lib.DieSystemExit):
504 image_controller.SignerTest(
505 input_proto, output_proto, self.api_config
506 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 def testSignerTestSuccess(self):
509 """Test successful call handling."""
510 self.PatchObject(image_lib, "SecurityTest", return_value=True)
511 input_proto = image_pb2.TestImageRequest()
512 input_proto.image.path = self.image_path
513 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600514
Alex Klein1699fab2022-09-08 08:46:06 -0600515 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 def testSignerTestFailure(self):
518 """Test function output tests."""
519 input_proto = image_pb2.TestImageRequest()
520 input_proto.image.path = self.image_path
521 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600522
Alex Klein1699fab2022-09-08 08:46:06 -0600523 self.PatchObject(image_lib, "SecurityTest", return_value=False)
524 image_controller.SignerTest(input_proto, output_proto, self.api_config)
525 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600526
Michael Mortensenc83c9952019-08-05 12:15:12 -0600527
Alex Klein1699fab2022-09-08 08:46:06 -0600528class ImageTestTest(
529 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
530):
531 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 def setUp(self):
534 self.image_path = os.path.join(self.tempdir, "image.bin")
535 self.board = "board"
536 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700537
Alex Klein1699fab2022-09-08 08:46:06 -0600538 osutils.SafeMakedirs(self.result_directory)
539 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700540
Alex Klein1699fab2022-09-08 08:46:06 -0600541 def testValidateOnly(self):
542 """Sanity check that a validate only call does not execute any logic."""
543 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600544
Alex Klein1699fab2022-09-08 08:46:06 -0600545 input_proto = image_pb2.TestImageRequest()
546 input_proto.image.path = self.image_path
547 input_proto.build_target.name = self.board
548 input_proto.result.directory = self.result_directory
549 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600550
Alex Klein1699fab2022-09-08 08:46:06 -0600551 image_controller.Test(
552 input_proto, output_proto, self.validate_only_config
553 )
554 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600555
Alex Klein1699fab2022-09-08 08:46:06 -0600556 def testMockCall(self):
557 """Test that mock call does not execute any logic, returns mocked value."""
558 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700559
Alex Klein1699fab2022-09-08 08:46:06 -0600560 input_proto = image_pb2.TestImageRequest()
561 input_proto.image.path = self.image_path
562 input_proto.build_target.name = self.board
563 input_proto.result.directory = self.result_directory
564 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700565
Alex Klein1699fab2022-09-08 08:46:06 -0600566 image_controller.Test(input_proto, output_proto, self.mock_call_config)
567 patch.assert_not_called()
568 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700569
Alex Klein1699fab2022-09-08 08:46:06 -0600570 def testMockError(self):
571 """Test that mock call does not execute any logic, returns error."""
572 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700573
Alex Klein1699fab2022-09-08 08:46:06 -0600574 input_proto = image_pb2.TestImageRequest()
575 input_proto.image.path = self.image_path
576 input_proto.build_target.name = self.board
577 input_proto.result.directory = self.result_directory
578 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700579
Alex Klein1699fab2022-09-08 08:46:06 -0600580 rc = image_controller.Test(
581 input_proto, output_proto, self.mock_error_config
582 )
583 patch.assert_not_called()
584 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700585
Alex Klein1699fab2022-09-08 08:46:06 -0600586 def testTestArgumentValidation(self):
587 """Test function argument validation tests."""
588 self.PatchObject(image_service, "Test", return_value=True)
589 input_proto = image_pb2.TestImageRequest()
590 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700591
Alex Klein1699fab2022-09-08 08:46:06 -0600592 # Nothing provided.
593 with self.assertRaises(cros_build_lib.DieSystemExit):
594 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700595
Alex Klein1699fab2022-09-08 08:46:06 -0600596 # Just one argument.
597 input_proto.build_target.name = self.board
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 # Two arguments provided.
602 input_proto.result.directory = self.result_directory
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 # Invalid image path.
607 input_proto.image.path = "/invalid/image/path"
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 # All valid arguments.
612 input_proto.image.path = self.image_path
613 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700614
Alex Klein1699fab2022-09-08 08:46:06 -0600615 def testTestOutputHandling(self):
616 """Test function output tests."""
617 input_proto = image_pb2.TestImageRequest()
618 input_proto.image.path = self.image_path
619 input_proto.build_target.name = self.board
620 input_proto.result.directory = self.result_directory
621 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700622
Alex Klein1699fab2022-09-08 08:46:06 -0600623 self.PatchObject(image_service, "Test", return_value=True)
624 image_controller.Test(input_proto, output_proto, self.api_config)
625 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 self.PatchObject(image_service, "Test", return_value=False)
628 image_controller.Test(input_proto, output_proto, self.api_config)
629 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000630
631
Greg Edelston6902e3d2022-01-27 12:19:38 -0700632class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600633 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000634
Alex Klein1699fab2022-09-08 08:46:06 -0600635 def _GetRequest(
636 self,
637 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
638 build_target_name="atlas",
639 profile="foo",
640 sign_types=None,
641 dryrun=True,
642 channels=None,
643 ):
644 return image_pb2.PushImageRequest(
645 gs_image_dir=gs_image_dir,
646 sysroot=sysroot_pb2.Sysroot(
647 build_target=common_pb2.BuildTarget(name=build_target_name)
648 ),
649 profile=common_pb2.Profile(name=profile),
650 sign_types=sign_types,
651 dryrun=dryrun,
652 channels=channels,
653 )
Jack Neus761e1842020-12-01 18:20:11 +0000654
Alex Klein1699fab2022-09-08 08:46:06 -0600655 def _GetResponse(self):
656 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000657
Alex Klein1699fab2022-09-08 08:46:06 -0600658 @mock.patch.object(pushimage, "PushImage", return_value={})
659 def testValidateOnly(self, MockPushImage):
660 """Check that a validate only call does not execute any logic."""
661 req = self._GetRequest(
662 sign_types=[
663 common_pb2.IMAGE_TYPE_RECOVERY,
664 common_pb2.IMAGE_TYPE_FACTORY,
665 common_pb2.IMAGE_TYPE_FIRMWARE,
666 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
667 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
668 common_pb2.IMAGE_TYPE_BASE,
669 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
670 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
671 ]
672 )
673 rc = image_controller.PushImage(
674 req, self.NewResponse(), self.validate_only_config
675 )
676 MockPushImage.assert_not_called()
677 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000678
Alex Klein1699fab2022-09-08 08:46:06 -0600679 @mock.patch.object(pushimage, "PushImage", return_value={})
680 def testValidateOnlyInvalid(self, MockPushImage):
681 """Check that validate call rejects invalid sign types."""
682 # Pass unsupported image type.
683 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
684 rc = image_controller.PushImage(
685 req, self._GetResponse(), self.validate_only_config
686 )
687 MockPushImage.assert_not_called()
688 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000689
Alex Klein1699fab2022-09-08 08:46:06 -0600690 @mock.patch.object(pushimage, "PushImage", return_value={})
691 def testMockCall(self, MockPushImage):
692 """Test that mock call does not execute any logic, returns mocked value."""
693 rc = image_controller.PushImage(
694 self._GetRequest(), self._GetResponse(), self.mock_call_config
695 )
696 MockPushImage.assert_not_called()
697 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000698
Alex Klein1699fab2022-09-08 08:46:06 -0600699 @mock.patch.object(pushimage, "PushImage", return_value={})
700 def testMockError(self, MockPushImage):
701 """Test that mock call does not execute any logic, returns error."""
702 rc = image_controller.PushImage(
703 self._GetRequest(), self._GetResponse(), self.mock_error_config
704 )
705 MockPushImage.assert_not_called()
706 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000707
Alex Klein1699fab2022-09-08 08:46:06 -0600708 @mock.patch.object(pushimage, "PushImage", return_value={})
709 def testNoBuildTarget(self, _):
710 """Test no build target given fails."""
711 request = self._GetRequest(build_target_name="")
712 with self.assertRaises(cros_build_lib.DieSystemExit):
713 image_controller.PushImage(
714 request, self._GetResponse(), self.api_config
715 )
Jack Neus761e1842020-12-01 18:20:11 +0000716
Alex Klein1699fab2022-09-08 08:46:06 -0600717 @mock.patch.object(pushimage, "PushImage", return_value={})
718 def testNoGsImageDir(self, _):
719 """Test no image dir given fails."""
720 request = self._GetRequest(gs_image_dir="")
721 with self.assertRaises(cros_build_lib.DieSystemExit):
722 image_controller.PushImage(
723 request, self._GetResponse(), self.api_config
724 )
Jack Neus761e1842020-12-01 18:20:11 +0000725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 @mock.patch.object(pushimage, "PushImage", return_value={})
727 def testCallCorrect(self, MockPushImage):
728 """Check that a call is called with the correct parameters."""
729 request = self._GetRequest(
730 dryrun=False,
731 profile="",
732 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
733 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
734 )
735 request.dest_bucket = "gs://foo"
736 image_controller.PushImage(
737 request, self._GetResponse(), self.api_config
738 )
739 MockPushImage.assert_called_with(
740 request.gs_image_dir,
741 request.sysroot.build_target.name,
742 dry_run=request.dryrun,
743 sign_types=["recovery"],
744 dest_bucket=request.dest_bucket,
745 force_channels=["dev", "canary"],
746 )
Jack Neus761e1842020-12-01 18:20:11 +0000747
Alex Klein1699fab2022-09-08 08:46:06 -0600748 @mock.patch.object(
749 pushimage,
750 "PushImage",
751 return_value={
752 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
753 "canary": ["gs://canary/instr1"],
754 },
755 )
756 def testOutput(self, _):
757 """Check that a call populates the response object."""
758 request = self._GetRequest(
759 profile="",
760 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
761 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
762 )
763 request.dest_bucket = "gs://foo"
764 response = self._GetResponse()
765 image_controller.PushImage(request, response, self.api_config)
766 self.assertEqual(
767 sorted([i.instructions_file_path for i in response.instructions]),
768 sorted(
769 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
770 ),
771 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 def testCallSucceeds(self, _):
774 """Check that a (dry run) call is made successfully."""
775 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
776 rc = image_controller.PushImage(
777 request, self._GetResponse(), self.api_config
778 )
779 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000780
Alex Klein1699fab2022-09-08 08:46:06 -0600781 def testCallFailsWithBadImageDir(self):
782 """Check that a (dry run) call fails when given a bad gs_image_dir."""
783 request = self._GetRequest(gs_image_dir="foo")
784 rc = image_controller.PushImage(
785 request, self._GetResponse, self.api_config
786 )
787 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)