blob: b0bc2b5aa9c081abc4d7cbcc8867d25de5ce3e5d [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 Klein56355682019-02-07 10:36:54 -070028 """Create image tests."""
29
Alex Klein231d2da2019-07-22 16:44:45 -060030 def setUp(self):
31 self.response = image_pb2.CreateImageResult()
32
Jack Neus761e1842020-12-01 18:20:11 +000033 def _GetRequest(self,
34 board=None,
35 types=None,
36 version=None,
37 builder_path=None,
Joseph Sussman34c01be2022-06-03 14:04:41 +000038 disable_rootfs_verification=False,
39 base_is_recovery=False):
Alex Klein21b95022019-05-09 14:14:46 -060040 """Helper to build a request instance."""
41 return image_pb2.CreateImageRequest(
42 build_target={'name': board},
43 image_types=types,
44 disable_rootfs_verification=disable_rootfs_verification,
45 version=version,
46 builder_path=builder_path,
Joseph Sussman34c01be2022-06-03 14:04:41 +000047 base_is_recovery=base_is_recovery,
Alex Klein21b95022019-05-09 14:14:46 -060048 )
49
Alex Klein231d2da2019-07-22 16:44:45 -060050 def testValidateOnly(self):
51 """Sanity check that a validate only call does not execute any logic."""
52 patch = self.PatchObject(image_service, 'Build')
Alex Klein21b95022019-05-09 14:14:46 -060053
Alex Klein231d2da2019-07-22 16:44:45 -060054 request = self._GetRequest(board='board')
55 image_controller.Create(request, self.response, self.validate_only_config)
56 patch.assert_not_called()
57
Michael Mortensen10146cf2019-11-19 19:59:22 -070058 def testMockCall(self):
59 """Test that mock call does not execute any logic, returns mocked value."""
60 patch = self.PatchObject(image_service, 'Build')
61
62 request = self._GetRequest(board='board')
63 image_controller.Create(request, self.response, self.mock_call_config)
64 patch.assert_not_called()
65 self.assertEqual(self.response.success, True)
66
Michael Mortensen85d38402019-12-12 09:50:29 -070067 def testMockError(self):
68 """Test that mock call does not execute any logic, returns error."""
69 patch = self.PatchObject(image_service, 'Build')
70
71 request = self._GetRequest(board='board')
72 rc = image_controller.Create(request, self.response, self.mock_error_config)
73 patch.assert_not_called()
74 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
75
Alex Klein231d2da2019-07-22 16:44:45 -060076 def testNoBoard(self):
77 """Test no board given fails."""
78 request = self._GetRequest()
Alex Klein56355682019-02-07 10:36:54 -070079
80 # No board should cause it to fail.
Alex Klein4f0eb432019-05-02 13:56:04 -060081 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -060082 image_controller.Create(request, self.response, self.api_config)
Alex Klein56355682019-02-07 10:36:54 -070083
Alex Klein21b95022019-05-09 14:14:46 -060084 def testNoTypeSpecified(self):
85 """Test the image type default."""
86 request = self._GetRequest(board='board')
Alex Klein21b95022019-05-09 14:14:46 -060087
Alex Klein1bcd9882019-03-19 13:25:24 -060088 # Failed result to avoid the success handling logic.
Alex Klein27978a42021-07-27 14:18:10 -060089 result = image_service.BuildResult([constants.IMAGE_TYPE_BASE])
90 result.return_code = 1
Alex Klein1bcd9882019-03-19 13:25:24 -060091 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
Alex Klein56355682019-02-07 10:36:54 -070092
Alex Klein231d2da2019-07-22 16:44:45 -060093 image_controller.Create(request, self.response, self.api_config)
Alex Klein27978a42021-07-27 14:18:10 -060094 build_patch.assert_any_call(
95 'board', [constants.IMAGE_TYPE_BASE], config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -070096
Alex Klein21b95022019-05-09 14:14:46 -060097 def testSingleTypeSpecified(self):
98 """Test it's properly using a specified type."""
George Engelbrechtc55d6312021-05-05 12:11:13 -060099 request = self._GetRequest(board='board', types=[common_pb2.IMAGE_TYPE_DEV])
Alex Klein21b95022019-05-09 14:14:46 -0600100
101 # Failed result to avoid the success handling logic.
Alex Klein27978a42021-07-27 14:18:10 -0600102 result = image_service.BuildResult([constants.IMAGE_TYPE_DEV])
103 result.return_code = 1
Alex Klein21b95022019-05-09 14:14:46 -0600104 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
105
Alex Klein231d2da2019-07-22 16:44:45 -0600106 image_controller.Create(request, self.response, self.api_config)
Alex Klein27978a42021-07-27 14:18:10 -0600107 build_patch.assert_any_call(
108 'board', [constants.IMAGE_TYPE_DEV], config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700109
Alex Klein21b95022019-05-09 14:14:46 -0600110 def testMultipleAndImpliedTypes(self):
111 """Test multiple types and implied type handling."""
112 # The TEST_VM type should force it to build the test image.
George Engelbrechtc55d6312021-05-05 12:11:13 -0600113 types = [common_pb2.IMAGE_TYPE_BASE, common_pb2.IMAGE_TYPE_TEST_VM]
Alex Klein21b95022019-05-09 14:14:46 -0600114 expected_images = [constants.IMAGE_TYPE_BASE, constants.IMAGE_TYPE_TEST]
115
116 request = self._GetRequest(board='board', types=types)
Alex Klein21b95022019-05-09 14:14:46 -0600117
118 # Failed result to avoid the success handling logic.
Alex Klein27978a42021-07-27 14:18:10 -0600119 result = image_service.BuildResult(expected_images)
120 result.return_code = 1
Alex Klein21b95022019-05-09 14:14:46 -0600121 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
122
Alex Klein231d2da2019-07-22 16:44:45 -0600123 image_controller.Create(request, self.response, self.api_config)
Alex Klein27978a42021-07-27 14:18:10 -0600124 build_patch.assert_any_call('board', expected_images, config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700125
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700126 def testRecoveryImpliedTypes(self):
127 """Test implied type handling of recovery images."""
128 # The TEST_VM type should force it to build the test image.
129 types = [common_pb2.IMAGE_TYPE_RECOVERY]
130
131 request = self._GetRequest(board='board', types=types)
132
133 # Failed result to avoid the success handling logic.
Alex Klein27978a42021-07-27 14:18:10 -0600134 result = image_service.BuildResult([])
135 result.return_code = 1
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700136 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
137
138 image_controller.Create(request, self.response, self.api_config)
Alex Klein27978a42021-07-27 14:18:10 -0600139 build_patch.assert_any_call(
140 'board', [constants.IMAGE_TYPE_BASE], config=mock.ANY)
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700141
Alex Klein1bcd9882019-03-19 13:25:24 -0600142 def testFailedPackageHandling(self):
143 """Test failed packages are populated correctly."""
Alex Klein27978a42021-07-27 14:18:10 -0600144 result = image_service.BuildResult([])
145 result.return_code = 1
146 result.failed_packages = ['foo/bar', 'cat/pkg']
Alex Klein1bcd9882019-03-19 13:25:24 -0600147 expected_packages = [('foo', 'bar'), ('cat', 'pkg')]
148 self.PatchObject(image_service, 'Build', return_value=result)
149
Alex Klein231d2da2019-07-22 16:44:45 -0600150 input_proto = self._GetRequest(board='board')
Alex Klein1bcd9882019-03-19 13:25:24 -0600151
Alex Klein231d2da2019-07-22 16:44:45 -0600152 rc = image_controller.Create(input_proto, self.response, self.api_config)
153
Alex Klein8cb365a2019-05-15 16:24:53 -0600154 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Klein231d2da2019-07-22 16:44:45 -0600155 for package in self.response.failed_packages:
Alex Klein1bcd9882019-03-19 13:25:24 -0600156 self.assertIn((package.category, package.package_name), expected_packages)
157
Alex Klein2557b4f2019-07-11 14:34:00 -0600158 def testNoPackagesFailureHandling(self):
159 """Test failed packages are populated correctly."""
Alex Klein27978a42021-07-27 14:18:10 -0600160 result = image_service.BuildResult([])
161 result.return_code = 1
Alex Klein2557b4f2019-07-11 14:34:00 -0600162 self.PatchObject(image_service, 'Build', return_value=result)
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700163
Alex Klein2557b4f2019-07-11 14:34:00 -0600164 input_proto = image_pb2.CreateImageRequest()
165 input_proto.build_target.name = 'board'
Alex Klein2557b4f2019-07-11 14:34:00 -0600166
Alex Klein231d2da2019-07-22 16:44:45 -0600167 rc = image_controller.Create(input_proto, self.response, self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600168 self.assertTrue(rc)
169 self.assertNotEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE,
170 rc)
Alex Klein231d2da2019-07-22 16:44:45 -0600171 self.assertFalse(self.response.failed_packages)
Alex Klein2557b4f2019-07-11 14:34:00 -0600172
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000173
174class RecoveryImageTest(cros_test_lib.RunCommandTempDirTestCase,
175 api_config.ApiConfigMixin):
176 """Recovery image tests."""
177
178 def setUp(self):
179 self.response = image_pb2.CreateImageResult()
180 self.types = [common_pb2.IMAGE_TYPE_BASE, common_pb2.IMAGE_TYPE_RECOVERY]
181 self.build_result = self._CreateMockBuildResult(
182 [common_pb2.IMAGE_TYPE_BASE])
183
184 self.PatchObject(
185 image_service,
186 'Build',
187 side_effect=[
188 self.build_result,
189 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_FACTORY]),
190 ])
191 self.copy_image_mock = self.PatchObject(
192 image_service,
193 'CopyBaseToRecovery',
194 side_effect=[
195 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
196 ])
197 self.recov_image_mock = self.PatchObject(
198 image_service,
199 'BuildRecoveryImage',
200 side_effect=[
201 self._CreateMockBuildResult([common_pb2.IMAGE_TYPE_RECOVERY]),
202 ])
203
204 def _GetRequest(self,
205 board=None,
206 types=None,
207 version=None,
208 builder_path=None,
209 disable_rootfs_verification=False,
210 base_is_recovery=False):
211 """Helper to build a request instance."""
212 return image_pb2.CreateImageRequest(
213 build_target={'name': board},
214 image_types=types,
215 disable_rootfs_verification=disable_rootfs_verification,
216 version=version,
217 builder_path=builder_path,
218 base_is_recovery=base_is_recovery,
219 )
220
221 def _CreateMockBuildResult(
222 self, image_types: List[int]) -> Optional[image_service.BuildResult]:
223 """Helper to create Mock build_image results.
224
225 Args:
226 image_types: A list of image types for which the mock BuildResult has to
227 be creates.
228
229 Returns:
230 A list of mock BuildResult.
231 """
232 image_types_names = [
233 image_controller.SUPPORTED_IMAGE_TYPES[x]
234 for x in image_types
235 if image_controller.SUPPORTED_IMAGE_TYPES[x] in
236 constants.IMAGE_TYPE_TO_NAME
237 ]
238
239 if not image_types_names:
240 if common_pb2.IMAGE_TYPE_FACTORY in image_types and len(image_types) == 1:
241 image_types_names.append(constants.IMAGE_TYPE_FACTORY_SHIM)
242 else:
243 return None
244
245 _build_result = image_service.BuildResult(image_types_names)
246 _build_result.return_code = 0
247 for image_type in image_types_names:
248 test_image = Path(self.tempdir) / constants.IMAGE_TYPE_TO_NAME[image_type]
249 test_image.touch()
250 _build_result.add_image(image_type, test_image)
251
252 return _build_result
253
Joseph Sussman34c01be2022-06-03 14:04:41 +0000254 def testBaseIsRecoveryTrue(self):
255 """Test that cp is called."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000256 input_proto = self._GetRequest(
257 board='board', types=self.types, base_is_recovery=True)
258 image_controller.Create(input_proto, self.response, self.api_config)
259
260 self.copy_image_mock.assert_called_with(
261 board='board',
262 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE])
Joseph Sussman34c01be2022-06-03 14:04:41 +0000263
264 def testBaseIsRecoveryFalse(self):
265 """Test that mod_image_for_recovery.sh is called."""
Ram Chandrasekar53c77cb2022-06-09 22:16:42 +0000266 input_proto = self._GetRequest(
267 board='board', types=self.types, base_is_recovery=False)
268 image_controller.Create(input_proto, self.response, self.api_config)
269
270 self.recov_image_mock.assert_called_with(
271 board='board',
272 image_path=self.build_result.images[constants.IMAGE_TYPE_BASE])
Joseph Sussman34c01be2022-06-03 14:04:41 +0000273
Alex Klein2557b4f2019-07-11 14:34:00 -0600274
Alex Klein231d2da2019-07-22 16:44:45 -0600275class ImageSignerTestTest(cros_test_lib.MockTempDirTestCase,
276 api_config.ApiConfigMixin):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600277 """Image signer test tests."""
278
279 def setUp(self):
280 self.image_path = os.path.join(self.tempdir, 'image.bin')
Michael Mortensenc83c9952019-08-05 12:15:12 -0600281 self.result_directory = os.path.join(self.tempdir, 'results')
282
283 osutils.SafeMakedirs(self.result_directory)
284 osutils.Touch(self.image_path)
285
Alex Klein231d2da2019-07-22 16:44:45 -0600286 def testValidateOnly(self):
287 """Sanity check that validate-only calls don't execute any logic."""
288 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
289 input_proto = image_pb2.TestImageRequest()
290 input_proto.image.path = self.image_path
291 output_proto = image_pb2.TestImageResult()
292
293 image_controller.SignerTest(input_proto, output_proto,
294 self.validate_only_config)
295
296 patch.assert_not_called()
297
Michael Mortensen10146cf2019-11-19 19:59:22 -0700298 def testMockCall(self):
299 """Test that mock call does not execute any logic, returns mocked value."""
300 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
301 input_proto = image_pb2.TestImageRequest()
302 input_proto.image.path = self.image_path
303 output_proto = image_pb2.TestImageResult()
304
305 image_controller.SignerTest(input_proto, output_proto,
306 self.mock_call_config)
307
308 patch.assert_not_called()
309 self.assertEqual(output_proto.success, True)
310
Michael Mortensen85d38402019-12-12 09:50:29 -0700311 def testMockError(self):
312 """Test that mock call does not execute any logic, returns error."""
313 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
314 input_proto = image_pb2.TestImageRequest()
315 input_proto.image.path = self.image_path
316 output_proto = image_pb2.TestImageResult()
317
318 rc = image_controller.SignerTest(input_proto, output_proto,
319 self.mock_error_config)
320
321 patch.assert_not_called()
322 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
323
Alex Klein231d2da2019-07-22 16:44:45 -0600324 def testSignerTestNoImage(self):
325 """Test function argument validation."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600326 input_proto = image_pb2.TestImageRequest()
327 output_proto = image_pb2.TestImageResult()
328
329 # Nothing provided.
330 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600331 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600332
Alex Klein231d2da2019-07-22 16:44:45 -0600333 def testSignerTestSuccess(self):
334 """Test successful call handling."""
335 self.PatchObject(image_lib, 'SecurityTest', return_value=True)
336 input_proto = image_pb2.TestImageRequest()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600337 input_proto.image.path = self.image_path
Alex Klein231d2da2019-07-22 16:44:45 -0600338 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600339
Alex Klein231d2da2019-07-22 16:44:45 -0600340 image_controller.SignerTest(input_proto, output_proto, self.api_config)
341
342 def testSignerTestFailure(self):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600343 """Test function output tests."""
344 input_proto = image_pb2.TestImageRequest()
345 input_proto.image.path = self.image_path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600346 output_proto = image_pb2.TestImageResult()
347
Michael Mortensenc83c9952019-08-05 12:15:12 -0600348 self.PatchObject(image_lib, 'SecurityTest', return_value=False)
Alex Klein231d2da2019-07-22 16:44:45 -0600349 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600350 self.assertFalse(output_proto.success)
351
Michael Mortensenc83c9952019-08-05 12:15:12 -0600352
Alex Klein231d2da2019-07-22 16:44:45 -0600353class ImageTestTest(cros_test_lib.MockTempDirTestCase,
354 api_config.ApiConfigMixin):
Alex Klein2557b4f2019-07-11 14:34:00 -0600355 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700356
357 def setUp(self):
358 self.image_path = os.path.join(self.tempdir, 'image.bin')
359 self.board = 'board'
360 self.result_directory = os.path.join(self.tempdir, 'results')
361
362 osutils.SafeMakedirs(self.result_directory)
363 osutils.Touch(self.image_path)
364
Alex Klein231d2da2019-07-22 16:44:45 -0600365 def testValidateOnly(self):
366 """Sanity check that a validate only call does not execute any logic."""
367 patch = self.PatchObject(image_service, 'Test')
368
369 input_proto = image_pb2.TestImageRequest()
370 input_proto.image.path = self.image_path
371 input_proto.build_target.name = self.board
372 input_proto.result.directory = self.result_directory
373 output_proto = image_pb2.TestImageResult()
374
375 image_controller.Test(input_proto, output_proto, self.validate_only_config)
376 patch.assert_not_called()
377
Michael Mortensen10146cf2019-11-19 19:59:22 -0700378 def testMockCall(self):
379 """Test that mock call does not execute any logic, returns mocked value."""
380 patch = self.PatchObject(image_service, 'Test')
381
382 input_proto = image_pb2.TestImageRequest()
383 input_proto.image.path = self.image_path
384 input_proto.build_target.name = self.board
385 input_proto.result.directory = self.result_directory
386 output_proto = image_pb2.TestImageResult()
387
388 image_controller.Test(input_proto, output_proto, self.mock_call_config)
389 patch.assert_not_called()
390 self.assertEqual(output_proto.success, True)
391
Michael Mortensen85d38402019-12-12 09:50:29 -0700392 def testMockError(self):
393 """Test that mock call does not execute any logic, returns error."""
394 patch = self.PatchObject(image_service, 'Test')
395
396 input_proto = image_pb2.TestImageRequest()
397 input_proto.image.path = self.image_path
398 input_proto.build_target.name = self.board
399 input_proto.result.directory = self.result_directory
400 output_proto = image_pb2.TestImageResult()
401
402 rc = image_controller.Test(input_proto, output_proto,
403 self.mock_error_config)
404 patch.assert_not_called()
405 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
406
Alex Klein2966e302019-01-17 13:29:38 -0700407 def testTestArgumentValidation(self):
408 """Test function argument validation tests."""
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700409 self.PatchObject(image_service, 'Test', return_value=True)
Alex Klein2966e302019-01-17 13:29:38 -0700410 input_proto = image_pb2.TestImageRequest()
411 output_proto = image_pb2.TestImageResult()
412
413 # Nothing provided.
Alex Klein4f0eb432019-05-02 13:56:04 -0600414 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600415 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700416
417 # Just one argument.
418 input_proto.build_target.name = self.board
Alex Klein4f0eb432019-05-02 13:56:04 -0600419 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600420 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700421
422 # Two arguments provided.
423 input_proto.result.directory = self.result_directory
Alex Klein4f0eb432019-05-02 13:56:04 -0600424 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600425 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700426
427 # Invalid image path.
428 input_proto.image.path = '/invalid/image/path'
Alex Klein4f0eb432019-05-02 13:56:04 -0600429 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600430 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700431
432 # All valid arguments.
433 input_proto.image.path = self.image_path
Alex Klein231d2da2019-07-22 16:44:45 -0600434 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700435
436 def testTestOutputHandling(self):
437 """Test function output tests."""
438 input_proto = image_pb2.TestImageRequest()
439 input_proto.image.path = self.image_path
440 input_proto.build_target.name = self.board
441 input_proto.result.directory = self.result_directory
442 output_proto = image_pb2.TestImageResult()
443
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700444 self.PatchObject(image_service, 'Test', return_value=True)
Alex Klein231d2da2019-07-22 16:44:45 -0600445 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700446 self.assertTrue(output_proto.success)
447
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700448 self.PatchObject(image_service, 'Test', return_value=False)
Alex Klein231d2da2019-07-22 16:44:45 -0600449 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700450 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000451
452
Greg Edelston6902e3d2022-01-27 12:19:38 -0700453class PushImageTest(api_config.ApiConfigMixin):
Jack Neus761e1842020-12-01 18:20:11 +0000454 """Push image test."""
455
Jack Neus761e1842020-12-01 18:20:11 +0000456 def _GetRequest(
457 self,
458 gs_image_dir='gs://chromeos-image-archive/atlas-release/R89-13604.0.0',
459 build_target_name='atlas',
460 profile='foo',
461 sign_types=None,
Jack Neuse77614d2021-10-11 14:10:27 +0000462 dryrun=True,
463 channels=None):
Jack Neus761e1842020-12-01 18:20:11 +0000464 return image_pb2.PushImageRequest(
465 gs_image_dir=gs_image_dir,
466 sysroot=sysroot_pb2.Sysroot(
467 build_target=common_pb2.BuildTarget(name=build_target_name)),
468 profile=common_pb2.Profile(name=profile),
469 sign_types=sign_types,
Jack Neuse77614d2021-10-11 14:10:27 +0000470 dryrun=dryrun,
471 channels=channels)
Jack Neus761e1842020-12-01 18:20:11 +0000472
Greg Edelston6902e3d2022-01-27 12:19:38 -0700473 def _GetResponse(self):
474 return image_pb2.PushImageRequest()
Jack Neus761e1842020-12-01 18:20:11 +0000475
Greg Edelston6902e3d2022-01-27 12:19:38 -0700476 @mock.patch.object(pushimage, 'PushImage', return_value={})
477 def testValidateOnly(self, MockPushImage):
478 """Check that a validate only call does not execute any logic."""
Jack Neus761e1842020-12-01 18:20:11 +0000479 req = self._GetRequest(sign_types=[
480 common_pb2.IMAGE_TYPE_RECOVERY, common_pb2.IMAGE_TYPE_FACTORY,
481 common_pb2.IMAGE_TYPE_FIRMWARE, common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
482 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG, common_pb2.IMAGE_TYPE_BASE,
Evan Benn4d061102022-02-14 12:50:45 +1100483 common_pb2.IMAGE_TYPE_GSC_FIRMWARE, common_pb2.IMAGE_TYPE_HPS_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +0000484 ])
Greg Edelston6902e3d2022-01-27 12:19:38 -0700485 rc = image_controller.PushImage(req, self.NewResponse(),
486 self.validate_only_config)
487 MockPushImage.assert_not_called()
488 self.assertEqual(rc, controller.RETURN_CODE_VALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000489
Greg Edelston6902e3d2022-01-27 12:19:38 -0700490 @mock.patch.object(pushimage, 'PushImage', return_value={})
491 def testValidateOnlyInvalid(self, MockPushImage):
Jack Neus761e1842020-12-01 18:20:11 +0000492 """Check that validate call rejects invalid sign types."""
Jack Neus761e1842020-12-01 18:20:11 +0000493 # Pass unsupported image type.
494 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
Greg Edelston6902e3d2022-01-27 12:19:38 -0700495 rc = image_controller.PushImage(req, self._GetResponse(),
496 self.validate_only_config)
497 MockPushImage.assert_not_called()
498 self.assertEqual(rc, controller.RETURN_CODE_INVALID_INPUT)
Jack Neus761e1842020-12-01 18:20:11 +0000499
Greg Edelston6902e3d2022-01-27 12:19:38 -0700500 @mock.patch.object(pushimage, 'PushImage', return_value={})
501 def testMockCall(self, MockPushImage):
Jack Neus761e1842020-12-01 18:20:11 +0000502 """Test that mock call does not execute any logic, returns mocked value."""
Greg Edelston6902e3d2022-01-27 12:19:38 -0700503 rc = image_controller.PushImage(self._GetRequest(), self._GetResponse(),
Jack Neus761e1842020-12-01 18:20:11 +0000504 self.mock_call_config)
Greg Edelston6902e3d2022-01-27 12:19:38 -0700505 MockPushImage.assert_not_called()
Jack Neus761e1842020-12-01 18:20:11 +0000506 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
507
Greg Edelston6902e3d2022-01-27 12:19:38 -0700508 @mock.patch.object(pushimage, 'PushImage', return_value={})
509 def testMockError(self, MockPushImage):
Jack Neus761e1842020-12-01 18:20:11 +0000510 """Test that mock call does not execute any logic, returns error."""
Greg Edelston6902e3d2022-01-27 12:19:38 -0700511 rc = image_controller.PushImage(self._GetRequest(), self._GetResponse(),
Jack Neus761e1842020-12-01 18:20:11 +0000512 self.mock_error_config)
Greg Edelston6902e3d2022-01-27 12:19:38 -0700513 MockPushImage.assert_not_called()
Jack Neus761e1842020-12-01 18:20:11 +0000514 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
515
Greg Edelston6902e3d2022-01-27 12:19:38 -0700516 @mock.patch.object(pushimage, 'PushImage', return_value={})
517 def testNoBuildTarget(self, _):
Jack Neus761e1842020-12-01 18:20:11 +0000518 """Test no build target given fails."""
519 request = self._GetRequest(build_target_name='')
Jack Neus761e1842020-12-01 18:20:11 +0000520 with self.assertRaises(cros_build_lib.DieSystemExit):
Greg Edelston6902e3d2022-01-27 12:19:38 -0700521 image_controller.PushImage(request, self._GetResponse(), self.api_config)
Jack Neus761e1842020-12-01 18:20:11 +0000522
Greg Edelston6902e3d2022-01-27 12:19:38 -0700523 @mock.patch.object(pushimage, 'PushImage', return_value={})
524 def testNoGsImageDir(self, _):
Jack Neus761e1842020-12-01 18:20:11 +0000525 """Test no image dir given fails."""
526 request = self._GetRequest(gs_image_dir='')
Jack Neus761e1842020-12-01 18:20:11 +0000527 with self.assertRaises(cros_build_lib.DieSystemExit):
Greg Edelston6902e3d2022-01-27 12:19:38 -0700528 image_controller.PushImage(request, self._GetResponse(), self.api_config)
Jack Neus761e1842020-12-01 18:20:11 +0000529
Greg Edelston6902e3d2022-01-27 12:19:38 -0700530 @mock.patch.object(pushimage, 'PushImage', return_value={})
531 def testCallCorrect(self, MockPushImage):
Jack Neus761e1842020-12-01 18:20:11 +0000532 """Check that a call is called with the correct parameters."""
Jack Neus761e1842020-12-01 18:20:11 +0000533 request = self._GetRequest(
Jack Neuse77614d2021-10-11 14:10:27 +0000534 dryrun=False, profile='', sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
535 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY])
Jack Neus485a9d22020-12-21 03:15:15 +0000536 request.dest_bucket = 'gs://foo'
Greg Edelston6902e3d2022-01-27 12:19:38 -0700537 image_controller.PushImage(request, self._GetResponse(), self.api_config)
538 MockPushImage.assert_called_with(
Jack Neus761e1842020-12-01 18:20:11 +0000539 request.gs_image_dir,
540 request.sysroot.build_target.name,
541 dry_run=request.dryrun,
Jack Neus485a9d22020-12-21 03:15:15 +0000542 sign_types=['recovery'],
Jack Neuse77614d2021-10-11 14:10:27 +0000543 dest_bucket=request.dest_bucket,
544 force_channels=['dev', 'canary'])
Jack Neus761e1842020-12-01 18:20:11 +0000545
Greg Edelston6902e3d2022-01-27 12:19:38 -0700546 @mock.patch.object(pushimage, 'PushImage', return_value={
547 'dev': ['gs://dev/instr1', 'gs://dev/instr2'],
548 'canary': ['gs://canary/instr1']})
549 def testOutput(self, _):
550 """Check that a call populates the response object."""
551 request = self._GetRequest(
552 profile='', sign_types=[common_pb2.IMAGE_TYPE_RECOVERY],
553 channels=[common_pb2.CHANNEL_DEV, common_pb2.CHANNEL_CANARY])
554 request.dest_bucket = 'gs://foo'
555 response = self._GetResponse()
556 image_controller.PushImage(request, response, self.api_config)
557 self.assertEqual(
558 sorted([i.instructions_file_path for i in response.instructions]),
559 sorted(['gs://dev/instr1', 'gs://dev/instr2', 'gs://canary/instr1']))
560
561 def testCallSucceeds(self, _):
Jack Neus761e1842020-12-01 18:20:11 +0000562 """Check that a (dry run) call is made successfully."""
563 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
Greg Edelston6902e3d2022-01-27 12:19:38 -0700564 rc = image_controller.PushImage(
565 request,
566 self._GetResponse(),
567 self.api_config)
568 self.assertEqual(rc, controller.RETURN_CODE_SUCCESS)
Jack Neus761e1842020-12-01 18:20:11 +0000569
570 def testCallFailsWithBadImageDir(self):
571 """Check that a (dry run) call fails when given a bad gs_image_dir."""
572 request = self._GetRequest(gs_image_dir='foo')
Greg Edelston6902e3d2022-01-27 12:19:38 -0700573 rc = image_controller.PushImage(request, self._GetResponse, self.api_config)
574 self.assertEqual(rc, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)