blob: 8700322b7edd2b7ca289c5f14be84ae48d8f52e9 [file] [log] [blame]
Alex Klein2966e302019-01-17 13:29:38 -07001# Copyright 2019 The Chromium OS Authors. All rights reserved.
2# 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
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000201
Alex Klein1699fab2022-09-08 08:46:06 -0600202class RecoveryImageTest(
203 cros_test_lib.RunCommandTempDirTestCase, api_config.ApiConfigMixin
204):
205 """Recovery image tests."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000206
Alex Klein1699fab2022-09-08 08:46:06 -0600207 def setUp(self):
208 self.response = image_pb2.CreateImageResult()
209 self.types = [
210 common_pb2.IMAGE_TYPE_BASE,
211 common_pb2.IMAGE_TYPE_RECOVERY,
212 ]
213 self.build_result = self._CreateMockBuildResult(
214 [common_pb2.IMAGE_TYPE_BASE]
215 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000216
Alex Klein1699fab2022-09-08 08:46:06 -0600217 self.PatchObject(
218 image_service,
219 "Build",
220 side_effect=[
221 self.build_result,
222 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
223 ],
224 )
225 self.copy_image_mock = self.PatchObject(
226 image_service,
227 "CopyBaseToRecovery",
228 side_effect=[
229 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
230 ],
231 )
232 self.recov_image_mock = self.PatchObject(
233 image_service,
234 "BuildRecoveryImage",
235 side_effect=[
236 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
237 ],
238 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 def _GetRequest(
241 self,
242 board=None,
243 types=None,
244 version=None,
245 builder_path=None,
246 disable_rootfs_verification=False,
247 base_is_recovery=False,
248 ):
249 """Helper to build a request instance."""
250 return image_pb2.CreateImageRequest(
251 build_target={"name": board},
252 image_types=types,
253 disable_rootfs_verification=disable_rootfs_verification,
254 version=version,
255 builder_path=builder_path,
256 base_is_recovery=base_is_recovery,
257 )
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000258
Alex Klein1699fab2022-09-08 08:46:06 -0600259 def _CreateMockBuildResult(
260 self, image_types: List[int]
261 ) -> Optional[image_service.BuildResult]:
262 """Helper to create Mock build_image results.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000263
Alex Klein1699fab2022-09-08 08:46:06 -0600264 Args:
265 image_types: A list of image types for which the mock BuildResult has to
266 be creates.
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000267
Alex Klein1699fab2022-09-08 08:46:06 -0600268 Returns:
269 A list of mock BuildResult.
270 """
271 image_types_names = [
272 image_controller.SUPPORTED_IMAGE_TYPES[x]
273 for x in image_types
274 if image_controller.SUPPORTED_IMAGE_TYPES[x]
275 in constants.IMAGE_TYPE_TO_NAME
276 ]
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 if not image_types_names:
279 if (
280 common_pb2.IMAGE_TYPE_FACTORY in image_types
281 and len(image_types) == 1
282 ):
283 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
284 else:
285 return None
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 _build_result = image_service.BuildResult(image_types_names)
288 _build_result.return_code = 0
289 for image_type in image_types_names:
290 test_image = (
291 Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
292 )
293 test_image.touch()
294 _build_result.add_image(image_type, test_image)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000295
Alex Klein1699fab2022-09-08 08:46:06 -0600296 return _build_result
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000297
Alex Klein1699fab2022-09-08 08:46:06 -0600298 def testBaseIsRecoveryTrue(self):
299 """Test that cp is called."""
300 input_proto = self._GetRequest(
301 board="board", types=self.types, base_is_recovery=True
302 )
303 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000304
Alex Klein1699fab2022-09-08 08:46:06 -0600305 self.copy_image_mock.assert_called_with(
306 board="board",
307 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
308 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 def testBaseIsRecoveryFalse(self):
311 """Test that mod_image_for_recovery.sh is called."""
312 input_proto = self._GetRequest(
313 board="board", types=self.types, base_is_recovery=False
314 )
315 image_controller.Create(input_proto, self.response, self.api_config)
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000316
Alex Klein1699fab2022-09-08 08:46:06 -0600317 self.recov_image_mock.assert_called_with(
318 board="board",
319 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE],
320 )
Joseph Sussman34c01be2022-06-03 14:04:41 +0000321
Alex Klein2557b4f2019-07-11 14:34:00 -0600322
Alex Klein1699fab2022-09-08 08:46:06 -0600323class ImageSignerTestTest(
324 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
325):
326 """Image signer test tests."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 def setUp(self):
329 self.image_path = os.path.join(self.tempdir, "image.bin")
330 self.result_directory = os.path.join(self.tempdir, "results")
Michael Mortensenc83c9952019-08-05 12:15:12 -0600331
Alex Klein1699fab2022-09-08 08:46:06 -0600332 osutils.SafeMakedirs(self.result_directory)
333 osutils.Touch(self.image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 def testValidateOnly(self):
336 """Sanity check that validate-only calls don't execute any logic."""
337 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
338 input_proto = image_pb2.TestImageRequest()
339 input_proto.image.path = self.image_path
340 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600341
Alex Klein1699fab2022-09-08 08:46:06 -0600342 image_controller.SignerTest(
343 input_proto, output_proto, self.validate_only_config
344 )
Alex Klein231d2da2019-07-22 16:44:45 -0600345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600347
Alex Klein1699fab2022-09-08 08:46:06 -0600348 def testMockCall(self):
349 """Test that mock call does not execute any logic, returns mocked value."""
350 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
351 input_proto = image_pb2.TestImageRequest()
352 input_proto.image.path = self.image_path
353 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 image_controller.SignerTest(
356 input_proto, output_proto, self.mock_call_config
357 )
Michael Mortensen10146cf2019-11-19 19:59:22 -0700358
Alex Klein1699fab2022-09-08 08:46:06 -0600359 patch.assert_not_called()
360 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700361
Alex Klein1699fab2022-09-08 08:46:06 -0600362 def testMockError(self):
363 """Test that mock call does not execute any logic, returns error."""
364 patch = self.PatchObject(image_lib, "SecurityTest", return_value=True)
365 input_proto = image_pb2.TestImageRequest()
366 input_proto.image.path = self.image_path
367 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700368
Alex Klein1699fab2022-09-08 08:46:06 -0600369 rc = image_controller.SignerTest(
370 input_proto, output_proto, self.mock_error_config
371 )
Michael Mortensen85d38402019-12-12 09:50:29 -0700372
Alex Klein1699fab2022-09-08 08:46:06 -0600373 patch.assert_not_called()
374 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 def testSignerTestNoImage(self):
377 """Test function argument validation."""
378 input_proto = image_pb2.TestImageRequest()
379 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 # Nothing provided.
382 with self.assertRaises(cros_build_lib.DieSystemExit):
383 image_controller.SignerTest(
384 input_proto, output_proto, self.api_config
385 )
Michael Mortensenc83c9952019-08-05 12:15:12 -0600386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 def testSignerTestSuccess(self):
388 """Test successful call handling."""
389 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 Mortensenc83c9952019-08-05 12:15:12 -0600393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600395
Alex Klein1699fab2022-09-08 08:46:06 -0600396 def testSignerTestFailure(self):
397 """Test function output tests."""
398 input_proto = image_pb2.TestImageRequest()
399 input_proto.image.path = self.image_path
400 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600401
Alex Klein1699fab2022-09-08 08:46:06 -0600402 self.PatchObject(image_lib, "SecurityTest", return_value=False)
403 image_controller.SignerTest(input_proto, output_proto, self.api_config)
404 self.assertFalse(output_proto.success)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600405
Michael Mortensenc83c9952019-08-05 12:15:12 -0600406
Alex Klein1699fab2022-09-08 08:46:06 -0600407class ImageTestTest(
408 cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin
409):
410 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700411
Alex Klein1699fab2022-09-08 08:46:06 -0600412 def setUp(self):
413 self.image_path = os.path.join(self.tempdir, "image.bin")
414 self.board = "board"
415 self.result_directory = os.path.join(self.tempdir, "results")
Alex Klein2966e302019-01-17 13:29:38 -0700416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 osutils.SafeMakedirs(self.result_directory)
418 osutils.Touch(self.image_path)
Alex Klein2966e302019-01-17 13:29:38 -0700419
Alex Klein1699fab2022-09-08 08:46:06 -0600420 def testValidateOnly(self):
421 """Sanity check that a validate only call does not execute any logic."""
422 patch = self.PatchObject(image_service, "Test")
Alex Klein231d2da2019-07-22 16:44:45 -0600423
Alex Klein1699fab2022-09-08 08:46:06 -0600424 input_proto = image_pb2.TestImageRequest()
425 input_proto.image.path = self.image_path
426 input_proto.build_target.name = self.board
427 input_proto.result.directory = self.result_directory
428 output_proto = image_pb2.TestImageResult()
Alex Klein231d2da2019-07-22 16:44:45 -0600429
Alex Klein1699fab2022-09-08 08:46:06 -0600430 image_controller.Test(
431 input_proto, output_proto, self.validate_only_config
432 )
433 patch.assert_not_called()
Alex Klein231d2da2019-07-22 16:44:45 -0600434
Alex Klein1699fab2022-09-08 08:46:06 -0600435 def testMockCall(self):
436 """Test that mock call does not execute any logic, returns mocked value."""
437 patch = self.PatchObject(image_service, "Test")
Michael Mortensen10146cf2019-11-19 19:59:22 -0700438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 input_proto = image_pb2.TestImageRequest()
440 input_proto.image.path = self.image_path
441 input_proto.build_target.name = self.board
442 input_proto.result.directory = self.result_directory
443 output_proto = image_pb2.TestImageResult()
Michael Mortensen10146cf2019-11-19 19:59:22 -0700444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 image_controller.Test(input_proto, output_proto, self.mock_call_config)
446 patch.assert_not_called()
447 self.assertEqual(output_proto.success, True)
Michael Mortensen10146cf2019-11-19 19:59:22 -0700448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 def testMockError(self):
450 """Test that mock call does not execute any logic, returns error."""
451 patch = self.PatchObject(image_service, "Test")
Michael Mortensen85d38402019-12-12 09:50:29 -0700452
Alex Klein1699fab2022-09-08 08:46:06 -0600453 input_proto = image_pb2.TestImageRequest()
454 input_proto.image.path = self.image_path
455 input_proto.build_target.name = self.board
456 input_proto.result.directory = self.result_directory
457 output_proto = image_pb2.TestImageResult()
Michael Mortensen85d38402019-12-12 09:50:29 -0700458
Alex Klein1699fab2022-09-08 08:46:06 -0600459 rc = image_controller.Test(
460 input_proto, output_proto, self.mock_error_config
461 )
462 patch.assert_not_called()
463 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Michael Mortensen85d38402019-12-12 09:50:29 -0700464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 def testTestArgumentValidation(self):
466 """Test function argument validation tests."""
467 self.PatchObject(image_service, "Test", return_value=True)
468 input_proto = image_pb2.TestImageRequest()
469 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700470
Alex Klein1699fab2022-09-08 08:46:06 -0600471 # Nothing provided.
472 with self.assertRaises(cros_build_lib.DieSystemExit):
473 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700474
Alex Klein1699fab2022-09-08 08:46:06 -0600475 # Just one argument.
476 input_proto.build_target.name = self.board
477 with self.assertRaises(cros_build_lib.DieSystemExit):
478 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700479
Alex Klein1699fab2022-09-08 08:46:06 -0600480 # Two arguments provided.
481 input_proto.result.directory = self.result_directory
482 with self.assertRaises(cros_build_lib.DieSystemExit):
483 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 # Invalid image path.
486 input_proto.image.path = "/invalid/image/path"
487 with self.assertRaises(cros_build_lib.DieSystemExit):
488 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700489
Alex Klein1699fab2022-09-08 08:46:06 -0600490 # All valid arguments.
491 input_proto.image.path = self.image_path
492 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 def testTestOutputHandling(self):
495 """Test function output tests."""
496 input_proto = image_pb2.TestImageRequest()
497 input_proto.image.path = self.image_path
498 input_proto.build_target.name = self.board
499 input_proto.result.directory = self.result_directory
500 output_proto = image_pb2.TestImageResult()
Alex Klein2966e302019-01-17 13:29:38 -0700501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 self.PatchObject(image_service, "Test", return_value=True)
503 image_controller.Test(input_proto, output_proto, self.api_config)
504 self.assertTrue(output_proto.success)
Alex Klein2966e302019-01-17 13:29:38 -0700505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 self.PatchObject(image_service, "Test", return_value=False)
507 image_controller.Test(input_proto, output_proto, self.api_config)
508 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000509
510
Greg Edelston6902e3d2022-01-27 12:19:38 -0700511class PushImageTest(api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600512 """Push image test."""
Jack Neus761e1842020-12-01 18:20:11 +0000513
Alex Klein1699fab2022-09-08 08:46:06 -0600514 def _GetRequest(
515 self,
516 gs_image_dir="gs://chromeos-image-archive/atlas-release/R89-13604.0.0",
517 build_target_name="atlas",
518 profile="foo",
519 sign_types=None,
520 dryrun=True,
521 channels=None,
522 ):
523 return image_pb2.PushImageRequest(
524 gs_image_dir=gs_image_dir,
525 sysroot=sysroot_pb2.Sysroot(
526 build_target=common_pb2.BuildTarget(name=build_target_name)
527 ),
528 profile=common_pb2.Profile(name=profile),
529 sign_types=sign_types,
530 dryrun=dryrun,
531 channels=channels,
532 )
Jack Neus761e1842020-12-01 18:20:11 +0000533
Alex Klein1699fab2022-09-08 08:46:06 -0600534 def _GetResponse(self):
535 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000536
Alex Klein1699fab2022-09-08 08:46:06 -0600537 @mock.patch.object(pushimage, "PushImage", return_value={})
538 def testValidateOnly(self, MockPushImage):
539 """Check that a validate only call does not execute any logic."""
540 req = self._GetRequest(
541 sign_types=[
542 common_pb2.IMAGE_TYPE_RECOVERY,
543 common_pb2.IMAGE_TYPE_FACTORY,
544 common_pb2.IMAGE_TYPE_FIRMWARE,
545 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
546 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG,
547 common_pb2.IMAGE_TYPE_BASE,
548 common_pb2.IMAGE_TYPE_GSC_FIRMWARE,
549 common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
550 ]
551 )
552 rc = image_controller.PushImage(
553 req, self.NewResponse(), self.validate_only_config
554 )
555 MockPushImage.assert_not_called()
556 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000557
Alex Klein1699fab2022-09-08 08:46:06 -0600558 @mock.patch.object(pushimage, "PushImage", return_value={})
559 def testValidateOnlyInvalid(self, MockPushImage):
560 """Check that validate call rejects invalid sign types."""
561 # Pass unsupported image type.
562 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
563 rc = image_controller.PushImage(
564 req, self._GetResponse(), self.validate_only_config
565 )
566 MockPushImage.assert_not_called()
567 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000568
Alex Klein1699fab2022-09-08 08:46:06 -0600569 @mock.patch.object(pushimage, "PushImage", return_value={})
570 def testMockCall(self, MockPushImage):
571 """Test that mock call does not execute any logic, returns mocked value."""
572 rc = image_controller.PushImage(
573 self._GetRequest(), self._GetResponse(), self.mock_call_config
574 )
575 MockPushImage.assert_not_called()
576 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000577
Alex Klein1699fab2022-09-08 08:46:06 -0600578 @mock.patch.object(pushimage, "PushImage", return_value={})
579 def testMockError(self, MockPushImage):
580 """Test that mock call does not execute any logic, returns error."""
581 rc = image_controller.PushImage(
582 self._GetRequest(), self._GetResponse(), self.mock_error_config
583 )
584 MockPushImage.assert_not_called()
585 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
Jack Neus761e1842020-12-01 18:20:11 +0000586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 @mock.patch.object(pushimage, "PushImage", return_value={})
588 def testNoBuildTarget(self, _):
589 """Test no build target given fails."""
590 request = self._GetRequest(build_target_name="")
591 with self.assertRaises(cros_build_lib.DieSystemExit):
592 image_controller.PushImage(
593 request, self._GetResponse(), self.api_config
594 )
Jack Neus761e1842020-12-01 18:20:11 +0000595
Alex Klein1699fab2022-09-08 08:46:06 -0600596 @mock.patch.object(pushimage, "PushImage", return_value={})
597 def testNoGsImageDir(self, _):
598 """Test no image dir given fails."""
599 request = self._GetRequest(gs_image_dir="")
600 with self.assertRaises(cros_build_lib.DieSystemExit):
601 image_controller.PushImage(
602 request, self._GetResponse(), self.api_config
603 )
Jack Neus761e1842020-12-01 18:20:11 +0000604
Alex Klein1699fab2022-09-08 08:46:06 -0600605 @mock.patch.object(pushimage, "PushImage", return_value={})
606 def testCallCorrect(self, MockPushImage):
607 """Check that a call is called with the correct parameters."""
608 request = self._GetRequest(
609 dryrun=False,
610 profile="",
611 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
612 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
613 )
614 request.dest_bucket = "gs://foo"
615 image_controller.PushImage(
616 request, self._GetResponse(), self.api_config
617 )
618 MockPushImage.assert_called_with(
619 request.gs_image_dir,
620 request.sysroot.build_target.name,
621 dry_run=request.dryrun,
622 sign_types=["recovery"],
623 dest_bucket=request.dest_bucket,
624 force_channels=["dev", "canary"],
625 )
Jack Neus761e1842020-12-01 18:20:11 +0000626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 @mock.patch.object(
628 pushimage,
629 "PushImage",
630 return_value={
631 "dev": ["gs://dev/instr1", "gs://dev/instr2"],
632 "canary": ["gs://canary/instr1"],
633 },
634 )
635 def testOutput(self, _):
636 """Check that a call populates the response object."""
637 request = self._GetRequest(
638 profile="",
639 sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
640 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY],
641 )
642 request.dest_bucket = "gs://foo"
643 response = self._GetResponse()
644 image_controller.PushImage(request, response, self.api_config)
645 self.assertEqual(
646 sorted([i.instructions_file_path for i in response.instructions]),
647 sorted(
648 ["gs://dev/instr1", "gs://dev/instr2", "gs://canary/instr1"]
649 ),
650 )
Greg Edelston6902e3d2022-01-27 12:19:38 -0700651
Alex Klein1699fab2022-09-08 08:46:06 -0600652 def testCallSucceeds(self, _):
653 """Check that a (dry run) call is made successfully."""
654 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
655 rc = image_controller.PushImage(
656 request, self._GetResponse(), self.api_config
657 )
658 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 def testCallFailsWithBadImageDir(self):
661 """Check that a (dry run) call fails when given a bad gs_image_dir."""
662 request = self._GetRequest(gs_image_dir="foo")
663 rc = image_controller.PushImage(
664 request, self._GetResponse, self.api_config
665 )
666 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)