blob: fb0b45d4c6321740da30ce03088b907e96e00e7a [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
Alex Klein56355682019-02-07 10:36:54 -070018from chromite.lib import constants
Alex Klein4f0eb432019-05-02 13:56:04 -060019from chromite.lib import cros_build_lib
Alex Klein2966e302019-01-17 13:29:38 -070020from chromite.lib import cros_test_lib
Michael Mortensenc83c9952019-08-05 12:15:12 -060021from chromite.lib import image_lib
Alex Klein2966e302019-01-17 13:29:38 -070022from chromite.lib import osutils
Jack Neus761e1842020-12-01 18:20:11 +000023from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070024from chromite.service import image as image_service
Alex Klein2966e302019-01-17 13:29:38 -070025
26
Alex Klein231d2da2019-07-22 16:44:45 -060027class CreateTest(cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -060028 """Create image tests."""
Alex Klein56355682019-02-07 10:36:54 -070029
Alex Klein1699fab2022-09-08 08:46:06 -060030 def setUp(self):
31 self.response = image_pb2.CreateImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -060032
Alex Klein1699fab2022-09-08 08:46:06 -060033 def _GetRequest(
34 self,
35 board=None,
36 types=None,
37 version=None,
38 builder_path=None,
39 disable_rootfs_verification=False,
40 base_is_recovery=False,
41 ):
42 """Helper to build a request instance."""
43 return image_pb2.CreateImageRequest(
44 build_target={"name": board},
45 image_types=types,
46 disable_rootfs_verification=disable_rootfs_verification,
47 version=version,
48 builder_path=builder_path,
49 base_is_recovery=base_is_recovery,
50 )
Alex Klein21b95022019-05-09 14:14:46 -060051
Alex Klein1699fab2022-09-08 08:46:06 -060052 def testValidateOnly(self):
53 """Sanity check that a validate only call does not execute any logic."""
54 patch = self.PatchObject(image_service, "Build")
Alex Klein21b95022019-05-09 14:14:46 -060055
Alex Klein1699fab2022-09-08 08:46:06 -060056 request = self._GetRequest(board="board")
57 image_controller.Create(
58 request, self.response, self.validate_only_config
59 )
60 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -060061
Alex Klein1699fab2022-09-08 08:46:06 -060062 def testMockCall(self):
63 """Test that mock call does not execute any logic, returns mocked value."""
64 patch = self.PatchObject(image_service, "Build")
Michael Mortensen10146cf2019-11-19 19:59:22 -070065
Alex Klein1699fab2022-09-08 08:46:06 -060066 request = self._GetRequest(board="board")
67 image_controller.Create(request, self.response, self.mock_call_config)
68 patch.assert_not_called()
69 self.assertEqual(self.response.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -070070
Alex Klein1699fab2022-09-08 08:46:06 -060071 def testMockError(self):
72 """Test that mock call does not execute any logic, returns error."""
73 patch = self.PatchObject(image_service, "Build")
Michael Mortensen85d38402019-12-12 09:50:29 -070074
Alex Klein1699fab2022-09-08 08:46:06 -060075 request = self._GetRequest(board="board")
76 rc = image_controller.Create(
77 request, self.response, self.mock_error_config
78 )
79 patch.assert_not_called()
80 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -070081
Alex Klein1699fab2022-09-08 08:46:06 -060082 def testNoBoard(self):
83 """Test no board given fails."""
84 request = self._GetRequest()
Alex Klein56355682019-02-07 10:36:54 -070085
Alex Klein1699fab2022-09-08 08:46:06 -060086 # No board should cause it to fail.
87 with self.assertRaises(cros_build_lib.DieSystemExit):
88 image_controller.Create(request, self.response, self.api_config)
Alex Klein56355682019-02-07 10:36:54 -070089
Alex Klein1699fab2022-09-08 08:46:06 -060090 def testNoTypeSpecified(self):
91 """Test the image type default."""
92 request = self._GetRequest(board="board")
Alex Klein21b95022019-05-09 14:14:46 -060093
Alex Klein1699fab2022-09-08 08:46:06 -060094 # Failed result to avoid the success handling logic.
95 result = image_service.BuildResult([constants.IMAGE_TYPE_BASE])
96 result.return_code = 1
97 build_patch = self.PatchObject(
98 image_service, "Build", return_value=result
99 )
Alex Klein56355682019-02-07 10:36:54 -0700100
Alex Klein1699fab2022-09-08 08:46:06 -0600101 image_controller.Create(request, self.response, self.api_config)
102 build_patch.assert_any_call(
103 "board", [constants.IMAGE_TYPE_BASE], config=mock.ANY
104 )
Alex Klein56355682019-02-07 10:36:54 -0700105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 def testSingleTypeSpecified(self):
107 """Test it's properly using a specified type."""
108 request = self._GetRequest(
109 board="board", types=[common_pb2.IMAGE_TYPE_DEV]
110 )
Alex Klein21b95022019-05-09 14:14:46 -0600111
Alex Klein1699fab2022-09-08 08:46:06 -0600112 # Failed result to avoid the success handling logic.
113 result = image_service.BuildResult([constants.IMAGE_TYPE_DEV])
114 result.return_code = 1
115 build_patch = self.PatchObject(
116 image_service, "Build", return_value=result
117 )
Alex Klein21b95022019-05-09 14:14:46 -0600118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 image_controller.Create(request, self.response, self.api_config)
120 build_patch.assert_any_call(
121 "board", [constants.IMAGE_TYPE_DEV], config=mock.ANY
122 )
Alex Klein56355682019-02-07 10:36:54 -0700123
Alex Klein1699fab2022-09-08 08:46:06 -0600124 def testMultipleAndImpliedTypes(self):
125 """Test multiple types and implied type handling."""
126 # The TEST_VM type should force it to build the test image.
127 types = [common_pb2.IMAGE_TYPE_BASE, common_pb2.IMAGE_TYPE_TEST_VM]
128 expected_images = [constants.IMAGE_TYPE_BASE, constants.IMAGE_TYPE_TEST]
Alex Klein21b95022019-05-09 14:14:46 -0600129
Alex Klein1699fab2022-09-08 08:46:06 -0600130 request = self._GetRequest(board="board", types=types)
Alex Klein21b95022019-05-09 14:14:46 -0600131
Alex Klein1699fab2022-09-08 08:46:06 -0600132 # Failed result to avoid the success handling logic.
133 result = image_service.BuildResult(expected_images)
134 result.return_code = 1
135 build_patch = self.PatchObject(
136 image_service, "Build", return_value=result
137 )
Alex Klein21b95022019-05-09 14:14:46 -0600138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 image_controller.Create(request, self.response, self.api_config)
140 build_patch.assert_any_call("board", expected_images, config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700141
Alex Klein1699fab2022-09-08 08:46:06 -0600142 def testRecoveryImpliedTypes(self):
143 """Test implied type handling of recovery images."""
144 # The TEST_VM type should force it to build the test image.
145 types = [common_pb2.IMAGE_TYPE_RECOVERY]
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700146
Alex Klein1699fab2022-09-08 08:46:06 -0600147 request = self._GetRequest(board="board", types=types)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700148
Alex Klein1699fab2022-09-08 08:46:06 -0600149 # Failed result to avoid the success handling logic.
150 result = image_service.BuildResult([])
151 result.return_code = 1
152 build_patch = self.PatchObject(
153 image_service, "Build", return_value=result
154 )
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700155
Alex Klein1699fab2022-09-08 08:46:06 -0600156 image_controller.Create(request, self.response, self.api_config)
157 build_patch.assert_any_call(
158 "board", [constants.IMAGE_TYPE_BASE], config=mock.ANY
159 )
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700160
Alex Klein1699fab2022-09-08 08:46:06 -0600161 def testFailedPackageHandling(self):
162 """Test failed packages are populated correctly."""
163 result = image_service.BuildResult([])
164 result.return_code = 1
165 result.failed_packages = ["foo/bar", "cat/pkg"]
166 expected_packages = [("foo", "bar"), ("cat", "pkg")]
167 self.PatchObject(image_service, "Build", return_value=result)
Alex Klein1bcd9882019-03-19 13:25:24 -0600168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 input_proto = self._GetRequest(board="board")
Alex Klein1bcd9882019-03-19 13:25:24 -0600170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 rc = image_controller.Create(
172 input_proto, self.response, self.api_config
173 )
Alex Klein231d2da2019-07-22 16:44:45 -0600174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 self.assertEqual(
176 controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc
177 )
178 for package in self.response.failed_packages:
179 self.assertIn(
180 (package.category, package.package_name), expected_packages
181 )
Alex Klein1bcd9882019-03-19 13:25:24 -0600182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 def testNoPackagesFailureHandling(self):
184 """Test failed packages are populated correctly."""
185 result = image_service.BuildResult([])
186 result.return_code = 1
187 self.PatchObject(image_service, "Build", return_value=result)
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 input_proto = image_pb2.CreateImageRequest()
190 input_proto.build_target.name = "board"
Alex Klein2557b4f2019-07-11 14:34:00 -0600191
Alex Klein1699fab2022-09-08 08:46:06 -0600192 rc = image_controller.Create(
193 input_proto, self.response, self.api_config
194 )
195 self.assertTrue(rc)
196 self.assertNotEqual(
197 controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc
198 )
199 self.assertFalse(self.response.failed_packages)
Alex Klein2557b4f2019-07-11 14:34:00 -0600200
Jack Neus70490b52022-09-12 14:41:45 +0000201 def testFactory(self):
202 """Test it's properly building factory."""
203 request = self._GetRequest(
204 board="board",
205 types=[
206 common_pb2.IMAGE_TYPE_FACTORY,
207 common_pb2.IMAGE_TYPE_NETBOOT,
208 ],
209 )
210 factory_path = self.tempdir / "factory-shim"
211 factory_path.touch()
212 result = image_service.BuildResult([constants.IMAGE_TYPE_FACTORY_SHIM])
213 result.add_image(constants.IMAGE_TYPE_FACTORY_SHIM, factory_path)
214 result.return_code = 0
215 build_patch = self.PatchObject(
216 image_service, "Build", return_value=result
217 )
218 netboot_patch = self.PatchObject(image_service, "create_netboot_kernel")
219
220 image_controller.Create(request, self.response, self.api_config)
221 build_patch.assert_any_call(
222 "board", [constants.IMAGE_TYPE_FACTORY_SHIM], config=mock.ANY
223 )
224 netboot_patch.assert_any_call("board", factory_path.name)
225
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000226
Alex Klein1699fab2022-09-08 08:46:06 -0600227class RecoveryImageTest(
228 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
229):
230 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 def setUp(self):
233 self.response = image_pb2.CreateImageResult()
234 self.types = [
235 common_pb2.IMAGE_TYPE_BASE,
236 common_pb2.IMAGE_TYPE_RECOVERY,
237 ]
238 self.build_result = self._CreateMockBuildResult(
239 [common_pb2.IMAGE_TYPE_BASE]
240 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 self.PatchObject(
243 image_service,
244 "Build",
245 side_effect=[
246 self.build_result,
247 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
248 ],
249 )
250 self.copy_image_mock = self.PatchObject(
251 image_service,
252 "CopyBaseToRecovery",
253 side_effect=[
254 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
255 ],
256 )
257 self.recov_image_mock = self.PatchObject(
258 image_service,
259 "BuildRecoveryImage",
260 side_effect=[
261 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
262 ],
263 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 def _GetRequest(
266 self,
267 board=None,
268 types=None,
269 version=None,
270 builder_path=None,
271 disable_rootfs_verification=False,
272 base_is_recovery=False,
273 ):
274 """Helper to build a request instance."""
275 return image_pb2.CreateImageRequest(
276 build_target={"name": board},
277 image_types=types,
278 disable_rootfs_verification=disable_rootfs_verification,
279 version=version,
280 builder_path=builder_path,
281 base_is_recovery=base_is_recovery,
282 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 def _CreateMockBuildResult(
285 self, image_types: List[int]
286 ) -> Optional[image_service.BuildResult]:
287 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 Args:
290 image_types: A list of image types for which the mock BuildResult has to
291 be creates.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 Returns:
294 A list of mock BuildResult.
295 """
296 image_types_names = [
297 image_controller.SUPPORTED_IMAGE_TYPES[x]
298 for x in image_types
299 if image_controller.SUPPORTED_IMAGE_TYPES[x]
300 in constants.IMAGE_TYPE_TO_NAME
301 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 if not image_types_names:
304 if (
305 common_pb2.IMAGE_TYPE_FACTORY in image_types
306 and len(image_types) == 1
307 ):
308 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
309 else:
310 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 _build_result = image_service.BuildResult(image_types_names)
313 _build_result.return_code = 0
314 for image_type in image_types_names:
315 test_image = (
316 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
317 )
318 test_image.touch()
319 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 def testBaseIsRecoveryTrue(self):
324 """Test that cp is called."""
325 input_proto = self._GetRequest(
326 board="board", types=self.types, base_is_recovery=True
327 )
328 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 self.copy_image_mock.assert_called_with(
331 board="board",
332 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
333 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 def testBaseIsRecoveryFalse(self):
336 """Test that mod_image_for_recovery.sh is called."""
337 input_proto = self._GetRequest(
338 board="board", types=self.types, base_is_recovery=False
339 )
340 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000341
Alex Klein1699fab2022-09-08 08:46:06 -0600342 self.recov_image_mock.assert_called_with(
343 board="board",
344 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
345 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000346
Alex Klein2557b4f2019-07-11 14:34:00 -0600347
Alex Klein1699fab2022-09-08 08:46:06 -0600348class ImageSignerTestTest(
349 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
350):
351 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600352
Alex Klein1699fab2022-09-08 08:46:06 -0600353 def setUp(self):
354 self.image_path = os.path.join(self.tempdir, "image.bin")
355 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600356
Alex Klein1699fab2022-09-08 08:46:06 -0600357 osutils.SafeMakedirs(self.result_directory)
358 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600359
Alex Klein1699fab2022-09-08 08:46:06 -0600360 def testValidateOnly(self):
361 """Sanity check that validate-only calls don't execute any logic."""
362 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
363 input_proto = image_pb2.TestImageRequest()
364 input_proto.image.path = self.image_path
365 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600366
Alex Klein1699fab2022-09-08 08:46:06 -0600367 image_controller.SignerTest(
368 input_proto, output_proto, self.validate_only_config
369 )
Alex Klein231d2da2019-07-22 16:44:45 -0600370
Alex Klein1699fab2022-09-08 08:46:06 -0600371 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600372
Alex Klein1699fab2022-09-08 08:46:06 -0600373 def testMockCall(self):
374 """Test that mock call does not execute any logic, returns mocked value."""
375 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
376 input_proto = image_pb2.TestImageRequest()
377 input_proto.image.path = self.image_path
378 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 image_controller.SignerTest(
381 input_proto, output_proto, self.mock_call_config
382 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 patch.assert_not_called()
385 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 def testMockError(self):
388 """Test that mock call does not execute any logic, returns error."""
389 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
390 input_proto = image_pb2.TestImageRequest()
391 input_proto.image.path = self.image_path
392 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 rc = image_controller.SignerTest(
395 input_proto, output_proto, self.mock_error_config
396 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700397
Alex Klein1699fab2022-09-08 08:46:06 -0600398 patch.assert_not_called()
399 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700400
Alex Klein1699fab2022-09-08 08:46:06 -0600401 def testSignerTestNoImage(self):
402 """Test function argument validation."""
403 input_proto = image_pb2.TestImageRequest()
404 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 # Nothing provided.
407 with self.assertRaises(cros_build_lib.DieSystemExit):
408 image_controller.SignerTest(
409 input_proto, output_proto, self.api_config
410 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600411
Alex Klein1699fab2022-09-08 08:46:06 -0600412 def testSignerTestSuccess(self):
413 """Test successful call handling."""
414 self.PatchObject(image_lib, "SecurityTest", return_value=True)
415 input_proto = image_pb2.TestImageRequest()
416 input_proto.image.path = self.image_path
417 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600418
Alex Klein1699fab2022-09-08 08:46:06 -0600419 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600420
Alex Klein1699fab2022-09-08 08:46:06 -0600421 def testSignerTestFailure(self):
422 """Test function output tests."""
423 input_proto = image_pb2.TestImageRequest()
424 input_proto.image.path = self.image_path
425 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600426
Alex Klein1699fab2022-09-08 08:46:06 -0600427 self.PatchObject(image_lib, "SecurityTest", return_value=False)
428 image_controller.SignerTest(input_proto, output_proto, self.api_config)
429 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600430
Michael Mortensenc83c9952019-08-05 12:15:12 -0600431
Alex Klein1699fab2022-09-08 08:46:06 -0600432class ImageTestTest(
433 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
434):
435 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 def setUp(self):
438 self.image_path = os.path.join(self.tempdir, "image.bin")
439 self.board = "board"
440 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700441
Alex Klein1699fab2022-09-08 08:46:06 -0600442 osutils.SafeMakedirs(self.result_directory)
443 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 def testValidateOnly(self):
446 """Sanity check that a validate only call does not execute any logic."""
447 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 input_proto = image_pb2.TestImageRequest()
450 input_proto.image.path = self.image_path
451 input_proto.build_target.name = self.board
452 input_proto.result.directory = self.result_directory
453 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 image_controller.Test(
456 input_proto, output_proto, self.validate_only_config
457 )
458 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 def testMockCall(self):
461 """Test that mock call does not execute any logic, returns mocked value."""
462 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700463
Alex Klein1699fab2022-09-08 08:46:06 -0600464 input_proto = image_pb2.TestImageRequest()
465 input_proto.image.path = self.image_path
466 input_proto.build_target.name = self.board
467 input_proto.result.directory = self.result_directory
468 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700469
Alex Klein1699fab2022-09-08 08:46:06 -0600470 image_controller.Test(input_proto, output_proto, self.mock_call_config)
471 patch.assert_not_called()
472 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 def testMockError(self):
475 """Test that mock call does not execute any logic, returns error."""
476 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700477
Alex Klein1699fab2022-09-08 08:46:06 -0600478 input_proto = image_pb2.TestImageRequest()
479 input_proto.image.path = self.image_path
480 input_proto.build_target.name = self.board
481 input_proto.result.directory = self.result_directory
482 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700483
Alex Klein1699fab2022-09-08 08:46:06 -0600484 rc = image_controller.Test(
485 input_proto, output_proto, self.mock_error_config
486 )
487 patch.assert_not_called()
488 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700489
Alex Klein1699fab2022-09-08 08:46:06 -0600490 def testTestArgumentValidation(self):
491 """Test function argument validation tests."""
492 self.PatchObject(image_service, "Test", return_value=True)
493 input_proto = image_pb2.TestImageRequest()
494 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700495
Alex Klein1699fab2022-09-08 08:46:06 -0600496 # Nothing provided.
497 with self.assertRaises(cros_build_lib.DieSystemExit):
498 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700499
Alex Klein1699fab2022-09-08 08:46:06 -0600500 # Just one argument.
501 input_proto.build_target.name = self.board
502 with self.assertRaises(cros_build_lib.DieSystemExit):
503 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 # Two arguments provided.
506 input_proto.result.directory = self.result_directory
507 with self.assertRaises(cros_build_lib.DieSystemExit):
508 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 # Invalid image path.
511 input_proto.image.path = "/invalid/image/path"
512 with self.assertRaises(cros_build_lib.DieSystemExit):
513 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700514
Alex Klein1699fab2022-09-08 08:46:06 -0600515 # All valid arguments.
516 input_proto.image.path = self.image_path
517 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700518
Alex Klein1699fab2022-09-08 08:46:06 -0600519 def testTestOutputHandling(self):
520 """Test function output tests."""
521 input_proto = image_pb2.TestImageRequest()
522 input_proto.image.path = self.image_path
523 input_proto.build_target.name = self.board
524 input_proto.result.directory = self.result_directory
525 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700526
Alex Klein1699fab2022-09-08 08:46:06 -0600527 self.PatchObject(image_service, "Test", return_value=True)
528 image_controller.Test(input_proto, output_proto, self.api_config)
529 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700530
Alex Klein1699fab2022-09-08 08:46:06 -0600531 self.PatchObject(image_service, "Test", return_value=False)
532 image_controller.Test(input_proto, output_proto, self.api_config)
533 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000534
535
Greg Edelston6902e3d2022-01-27 12:19:38 -0700536class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600537 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000538
Alex Klein1699fab2022-09-08 08:46:06 -0600539 def _GetRequest(
540 self,
541 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
542 build_target_name="atlas",
543 profile="foo",
544 sign_types=None,
545 dryrun=True,
546 channels=None,
547 ):
548 return image_pb2.PushImageRequest(
549 gs_image_dir=gs_image_dir,
550 sysroot=sysroot_pb2.Sysroot(
551 build_target=common_pb2.BuildTarget(name=build_target_name)
552 ),
553 profile=common_pb2.Profile(name=profile),
554 sign_types=sign_types,
555 dryrun=dryrun,
556 channels=channels,
557 )
Jack Neus761e1842020-12-01 18:20:11 +0000558
Alex Klein1699fab2022-09-08 08:46:06 -0600559 def _GetResponse(self):
560 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 @mock.patch.object(pushimage, "PushImage", return_value={})
563 def testValidateOnly(self, MockPushImage):
564 """Check that a validate only call does not execute any logic."""
565 req = self._GetRequest(
566 sign_types=[
567 common_pb2.IMAGE_TYPE_RECOVERY,
568 common_pb2.IMAGE_TYPE_FACTORY,
569 common_pb2.IMAGE_TYPE_FIRMWARE,
570 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
571 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
572 common_pb2.IMAGE_TYPE_BASE,
573 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
574 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
575 ]
576 )
577 rc = image_controller.PushImage(
578 req, self.NewResponse(), self.validate_only_config
579 )
580 MockPushImage.assert_not_called()
581 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 @mock.patch.object(pushimage, "PushImage", return_value={})
584 def testValidateOnlyInvalid(self, MockPushImage):
585 """Check that validate call rejects invalid sign types."""
586 # Pass unsupported image type.
587 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
588 rc = image_controller.PushImage(
589 req, self._GetResponse(), self.validate_only_config
590 )
591 MockPushImage.assert_not_called()
592 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000593
Alex Klein1699fab2022-09-08 08:46:06 -0600594 @mock.patch.object(pushimage, "PushImage", return_value={})
595 def testMockCall(self, MockPushImage):
596 """Test that mock call does not execute any logic, returns mocked value."""
597 rc = image_controller.PushImage(
598 self._GetRequest(), self._GetResponse(), self.mock_call_config
599 )
600 MockPushImage.assert_not_called()
601 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000602
Alex Klein1699fab2022-09-08 08:46:06 -0600603 @mock.patch.object(pushimage, "PushImage", return_value={})
604 def testMockError(self, MockPushImage):
605 """Test that mock call does not execute any logic, returns error."""
606 rc = image_controller.PushImage(
607 self._GetRequest(), self._GetResponse(), self.mock_error_config
608 )
609 MockPushImage.assert_not_called()
610 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 @mock.patch.object(pushimage, "PushImage", return_value={})
613 def testNoBuildTarget(self, _):
614 """Test no build target given fails."""
615 request = self._GetRequest(build_target_name="")
616 with self.assertRaises(cros_build_lib.DieSystemExit):
617 image_controller.PushImage(
618 request, self._GetResponse(), self.api_config
619 )
Jack Neus761e1842020-12-01 18:20:11 +0000620
Alex Klein1699fab2022-09-08 08:46:06 -0600621 @mock.patch.object(pushimage, "PushImage", return_value={})
622 def testNoGsImageDir(self, _):
623 """Test no image dir given fails."""
624 request = self._GetRequest(gs_image_dir="")
625 with self.assertRaises(cros_build_lib.DieSystemExit):
626 image_controller.PushImage(
627 request, self._GetResponse(), self.api_config
628 )
Jack Neus761e1842020-12-01 18:20:11 +0000629
Alex Klein1699fab2022-09-08 08:46:06 -0600630 @mock.patch.object(pushimage, "PushImage", return_value={})
631 def testCallCorrect(self, MockPushImage):
632 """Check that a call is called with the correct parameters."""
633 request = self._GetRequest(
634 dryrun=False,
635 profile="",
636 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
637 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
638 )
639 request.dest_bucket = "gs://foo"
640 image_controller.PushImage(
641 request, self._GetResponse(), self.api_config
642 )
643 MockPushImage.assert_called_with(
644 request.gs_image_dir,
645 request.sysroot.build_target.name,
646 dry_run=request.dryrun,
647 sign_types=["recovery"],
648 dest_bucket=request.dest_bucket,
649 force_channels=["dev", "canary"],
650 )
Jack Neus761e1842020-12-01 18:20:11 +0000651
Alex Klein1699fab2022-09-08 08:46:06 -0600652 @mock.patch.object(
653 pushimage,
654 "PushImage",
655 return_value={
656 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
657 "canary": ["gs://canary/instr1"],
658 },
659 )
660 def testOutput(self, _):
661 """Check that a call populates the response object."""
662 request = self._GetRequest(
663 profile="",
664 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
665 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
666 )
667 request.dest_bucket = "gs://foo"
668 response = self._GetResponse()
669 image_controller.PushImage(request, response, self.api_config)
670 self.assertEqual(
671 sorted([i.instructions_file_path for i in response.instructions]),
672 sorted(
673 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
674 ),
675 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700676
Alex Klein1699fab2022-09-08 08:46:06 -0600677 def testCallSucceeds(self, _):
678 """Check that a (dry run) call is made successfully."""
679 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
680 rc = image_controller.PushImage(
681 request, self._GetResponse(), self.api_config
682 )
683 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000684
Alex Klein1699fab2022-09-08 08:46:06 -0600685 def testCallFailsWithBadImageDir(self):
686 """Check that a (dry run) call fails when given a bad gs_image_dir."""
687 request = self._GetRequest(gs_image_dir="foo")
688 rc = image_controller.PushImage(
689 request, self._GetResponse, self.api_config
690 )
691 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)