blob: c647e11ef1896da65210e225f978c3634cb77dfc [file] [log] [blame]
Alex Klein2966e302019-01-17 13:29:38 -07001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Image service tests."""
7
8from __future__ import print_function
9
10import os
11
Mike Frysinger6db648e2018-07-24 19:57:58 -040012import mock
13
Alex Klein231d2da2019-07-22 16:44:45 -060014from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060015from chromite.api import controller
16from chromite.api.controller import image as image_controller
Alex Klein7107bdd2019-03-14 17:14:31 -060017from chromite.api.gen.chromite.api import image_pb2
David Burger13e06be2019-05-13 20:33:16 -060018from chromite.api.gen.chromiumos import common_pb2
Jack Neus761e1842020-12-01 18:20:11 +000019from chromite.api.gen.chromite.api import sysroot_pb2
Alex Klein56355682019-02-07 10:36:54 -070020from chromite.lib import constants
Alex Klein4f0eb432019-05-02 13:56:04 -060021from chromite.lib import cros_build_lib
Alex Klein2966e302019-01-17 13:29:38 -070022from chromite.lib import cros_test_lib
Michael Mortensenc83c9952019-08-05 12:15:12 -060023from chromite.lib import image_lib
Alex Klein2966e302019-01-17 13:29:38 -070024from chromite.lib import osutils
Jack Neus761e1842020-12-01 18:20:11 +000025from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070026from chromite.service import image as image_service
Alex Klein2966e302019-01-17 13:29:38 -070027
28
Alex Klein231d2da2019-07-22 16:44:45 -060029class CreateTest(cros_test_lib.MockTempDirTestCase, api_config.ApiConfigMixin):
Alex Klein56355682019-02-07 10:36:54 -070030 """Create image tests."""
31
Alex Klein231d2da2019-07-22 16:44:45 -060032 def setUp(self):
33 self.response = image_pb2.CreateImageResult()
34
Jack Neus761e1842020-12-01 18:20:11 +000035 def _GetRequest(self,
36 board=None,
37 types=None,
38 version=None,
39 builder_path=None,
Alex Klein21b95022019-05-09 14:14:46 -060040 disable_rootfs_verification=False):
41 """Helper to build a request instance."""
42 return image_pb2.CreateImageRequest(
43 build_target={'name': board},
44 image_types=types,
45 disable_rootfs_verification=disable_rootfs_verification,
46 version=version,
47 builder_path=builder_path,
48 )
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.
89 result = image_service.BuildResult(1, [])
90 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
Alex Klein56355682019-02-07 10:36:54 -070091
Alex Klein231d2da2019-07-22 16:44:45 -060092 image_controller.Create(request, self.response, self.api_config)
Jack Neus761e1842020-12-01 18:20:11 +000093 build_patch.assert_called_with(
94 images=[constants.IMAGE_TYPE_BASE], board='board', config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -070095
Alex Klein21b95022019-05-09 14:14:46 -060096 def testSingleTypeSpecified(self):
97 """Test it's properly using a specified type."""
98 request = self._GetRequest(board='board', types=[common_pb2.DEV])
Alex Klein21b95022019-05-09 14:14:46 -060099
100 # Failed result to avoid the success handling logic.
101 result = image_service.BuildResult(1, [])
102 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
103
Alex Klein231d2da2019-07-22 16:44:45 -0600104 image_controller.Create(request, self.response, self.api_config)
Jack Neus761e1842020-12-01 18:20:11 +0000105 build_patch.assert_called_with(
106 images=[constants.IMAGE_TYPE_DEV], board='board', config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700107
Alex Klein21b95022019-05-09 14:14:46 -0600108 def testMultipleAndImpliedTypes(self):
109 """Test multiple types and implied type handling."""
110 # The TEST_VM type should force it to build the test image.
111 types = [common_pb2.BASE, common_pb2.TEST_VM]
112 expected_images = [constants.IMAGE_TYPE_BASE, constants.IMAGE_TYPE_TEST]
113
114 request = self._GetRequest(board='board', types=types)
Alex Klein21b95022019-05-09 14:14:46 -0600115
116 # Failed result to avoid the success handling logic.
117 result = image_service.BuildResult(1, [])
118 build_patch = self.PatchObject(image_service, 'Build', return_value=result)
119
Alex Klein231d2da2019-07-22 16:44:45 -0600120 image_controller.Create(request, self.response, self.api_config)
Jack Neus761e1842020-12-01 18:20:11 +0000121 build_patch.assert_called_with(
122 images=expected_images, board='board', config=mock.ANY)
Alex Klein56355682019-02-07 10:36:54 -0700123
Alex Klein1bcd9882019-03-19 13:25:24 -0600124 def testFailedPackageHandling(self):
125 """Test failed packages are populated correctly."""
126 result = image_service.BuildResult(1, ['foo/bar', 'cat/pkg'])
127 expected_packages = [('foo', 'bar'), ('cat', 'pkg')]
128 self.PatchObject(image_service, 'Build', return_value=result)
129
Alex Klein231d2da2019-07-22 16:44:45 -0600130 input_proto = self._GetRequest(board='board')
Alex Klein1bcd9882019-03-19 13:25:24 -0600131
Alex Klein231d2da2019-07-22 16:44:45 -0600132 rc = image_controller.Create(input_proto, self.response, self.api_config)
133
Alex Klein8cb365a2019-05-15 16:24:53 -0600134 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Klein231d2da2019-07-22 16:44:45 -0600135 for package in self.response.failed_packages:
Alex Klein1bcd9882019-03-19 13:25:24 -0600136 self.assertIn((package.category, package.package_name), expected_packages)
137
Alex Klein2557b4f2019-07-11 14:34:00 -0600138 def testNoPackagesFailureHandling(self):
139 """Test failed packages are populated correctly."""
140 result = image_service.BuildResult(1, [])
141 self.PatchObject(image_service, 'Build', return_value=result)
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700142
Alex Klein2557b4f2019-07-11 14:34:00 -0600143 input_proto = image_pb2.CreateImageRequest()
144 input_proto.build_target.name = 'board'
Alex Klein2557b4f2019-07-11 14:34:00 -0600145
Alex Klein231d2da2019-07-22 16:44:45 -0600146 rc = image_controller.Create(input_proto, self.response, self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600147 self.assertTrue(rc)
148 self.assertNotEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE,
149 rc)
Alex Klein231d2da2019-07-22 16:44:45 -0600150 self.assertFalse(self.response.failed_packages)
Alex Klein2557b4f2019-07-11 14:34:00 -0600151
152
Alex Klein231d2da2019-07-22 16:44:45 -0600153class ImageSignerTestTest(cros_test_lib.MockTempDirTestCase,
154 api_config.ApiConfigMixin):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600155 """Image signer test tests."""
156
157 def setUp(self):
158 self.image_path = os.path.join(self.tempdir, 'image.bin')
Michael Mortensenc83c9952019-08-05 12:15:12 -0600159 self.result_directory = os.path.join(self.tempdir, 'results')
160
161 osutils.SafeMakedirs(self.result_directory)
162 osutils.Touch(self.image_path)
163
Alex Klein231d2da2019-07-22 16:44:45 -0600164 def testValidateOnly(self):
165 """Sanity check that validate-only calls don't execute any logic."""
166 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
167 input_proto = image_pb2.TestImageRequest()
168 input_proto.image.path = self.image_path
169 output_proto = image_pb2.TestImageResult()
170
171 image_controller.SignerTest(input_proto, output_proto,
172 self.validate_only_config)
173
174 patch.assert_not_called()
175
Michael Mortensen10146cf2019-11-19 19:59:22 -0700176 def testMockCall(self):
177 """Test that mock call does not execute any logic, returns mocked value."""
178 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
179 input_proto = image_pb2.TestImageRequest()
180 input_proto.image.path = self.image_path
181 output_proto = image_pb2.TestImageResult()
182
183 image_controller.SignerTest(input_proto, output_proto,
184 self.mock_call_config)
185
186 patch.assert_not_called()
187 self.assertEqual(output_proto.success, True)
188
Michael Mortensen85d38402019-12-12 09:50:29 -0700189 def testMockError(self):
190 """Test that mock call does not execute any logic, returns error."""
191 patch = self.PatchObject(image_lib, 'SecurityTest', return_value=True)
192 input_proto = image_pb2.TestImageRequest()
193 input_proto.image.path = self.image_path
194 output_proto = image_pb2.TestImageResult()
195
196 rc = image_controller.SignerTest(input_proto, output_proto,
197 self.mock_error_config)
198
199 patch.assert_not_called()
200 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
201
Alex Klein231d2da2019-07-22 16:44:45 -0600202 def testSignerTestNoImage(self):
203 """Test function argument validation."""
Michael Mortensenc83c9952019-08-05 12:15:12 -0600204 input_proto = image_pb2.TestImageRequest()
205 output_proto = image_pb2.TestImageResult()
206
207 # Nothing provided.
208 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600209 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600210
Alex Klein231d2da2019-07-22 16:44:45 -0600211 def testSignerTestSuccess(self):
212 """Test successful call handling."""
213 self.PatchObject(image_lib, 'SecurityTest', return_value=True)
214 input_proto = image_pb2.TestImageRequest()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600215 input_proto.image.path = self.image_path
Alex Klein231d2da2019-07-22 16:44:45 -0600216 output_proto = image_pb2.TestImageResult()
Michael Mortensenc83c9952019-08-05 12:15:12 -0600217
Alex Klein231d2da2019-07-22 16:44:45 -0600218 image_controller.SignerTest(input_proto, output_proto, self.api_config)
219
220 def testSignerTestFailure(self):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600221 """Test function output tests."""
222 input_proto = image_pb2.TestImageRequest()
223 input_proto.image.path = self.image_path
Michael Mortensenc83c9952019-08-05 12:15:12 -0600224 output_proto = image_pb2.TestImageResult()
225
Michael Mortensenc83c9952019-08-05 12:15:12 -0600226 self.PatchObject(image_lib, 'SecurityTest', return_value=False)
Alex Klein231d2da2019-07-22 16:44:45 -0600227 image_controller.SignerTest(input_proto, output_proto, self.api_config)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600228 self.assertFalse(output_proto.success)
229
Michael Mortensenc83c9952019-08-05 12:15:12 -0600230
Alex Klein231d2da2019-07-22 16:44:45 -0600231class ImageTestTest(cros_test_lib.MockTempDirTestCase,
232 api_config.ApiConfigMixin):
Alex Klein2557b4f2019-07-11 14:34:00 -0600233 """Image test tests."""
Alex Klein2966e302019-01-17 13:29:38 -0700234
235 def setUp(self):
236 self.image_path = os.path.join(self.tempdir, 'image.bin')
237 self.board = 'board'
238 self.result_directory = os.path.join(self.tempdir, 'results')
239
240 osutils.SafeMakedirs(self.result_directory)
241 osutils.Touch(self.image_path)
242
Alex Klein231d2da2019-07-22 16:44:45 -0600243 def testValidateOnly(self):
244 """Sanity check that a validate only call does not execute any logic."""
245 patch = self.PatchObject(image_service, 'Test')
246
247 input_proto = image_pb2.TestImageRequest()
248 input_proto.image.path = self.image_path
249 input_proto.build_target.name = self.board
250 input_proto.result.directory = self.result_directory
251 output_proto = image_pb2.TestImageResult()
252
253 image_controller.Test(input_proto, output_proto, self.validate_only_config)
254 patch.assert_not_called()
255
Michael Mortensen10146cf2019-11-19 19:59:22 -0700256 def testMockCall(self):
257 """Test that mock call does not execute any logic, returns mocked value."""
258 patch = self.PatchObject(image_service, 'Test')
259
260 input_proto = image_pb2.TestImageRequest()
261 input_proto.image.path = self.image_path
262 input_proto.build_target.name = self.board
263 input_proto.result.directory = self.result_directory
264 output_proto = image_pb2.TestImageResult()
265
266 image_controller.Test(input_proto, output_proto, self.mock_call_config)
267 patch.assert_not_called()
268 self.assertEqual(output_proto.success, True)
269
Michael Mortensen85d38402019-12-12 09:50:29 -0700270 def testMockError(self):
271 """Test that mock call does not execute any logic, returns error."""
272 patch = self.PatchObject(image_service, 'Test')
273
274 input_proto = image_pb2.TestImageRequest()
275 input_proto.image.path = self.image_path
276 input_proto.build_target.name = self.board
277 input_proto.result.directory = self.result_directory
278 output_proto = image_pb2.TestImageResult()
279
280 rc = image_controller.Test(input_proto, output_proto,
281 self.mock_error_config)
282 patch.assert_not_called()
283 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
284
Alex Klein2966e302019-01-17 13:29:38 -0700285 def testTestArgumentValidation(self):
286 """Test function argument validation tests."""
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700287 self.PatchObject(image_service, 'Test', return_value=True)
Alex Klein2966e302019-01-17 13:29:38 -0700288 input_proto = image_pb2.TestImageRequest()
289 output_proto = image_pb2.TestImageResult()
290
291 # Nothing provided.
Alex Klein4f0eb432019-05-02 13:56:04 -0600292 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600293 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700294
295 # Just one argument.
296 input_proto.build_target.name = self.board
Alex Klein4f0eb432019-05-02 13:56:04 -0600297 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600298 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700299
300 # Two arguments provided.
301 input_proto.result.directory = self.result_directory
Alex Klein4f0eb432019-05-02 13:56:04 -0600302 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600303 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700304
305 # Invalid image path.
306 input_proto.image.path = '/invalid/image/path'
Alex Klein4f0eb432019-05-02 13:56:04 -0600307 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600308 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700309
310 # All valid arguments.
311 input_proto.image.path = self.image_path
Alex Klein231d2da2019-07-22 16:44:45 -0600312 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700313
314 def testTestOutputHandling(self):
315 """Test function output tests."""
316 input_proto = image_pb2.TestImageRequest()
317 input_proto.image.path = self.image_path
318 input_proto.build_target.name = self.board
319 input_proto.result.directory = self.result_directory
320 output_proto = image_pb2.TestImageResult()
321
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700322 self.PatchObject(image_service, 'Test', return_value=True)
Alex Klein231d2da2019-07-22 16:44:45 -0600323 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700324 self.assertTrue(output_proto.success)
325
Alex Kleinb7cdbe62019-02-22 11:41:32 -0700326 self.PatchObject(image_service, 'Test', return_value=False)
Alex Klein231d2da2019-07-22 16:44:45 -0600327 image_controller.Test(input_proto, output_proto, self.api_config)
Alex Klein2966e302019-01-17 13:29:38 -0700328 self.assertFalse(output_proto.success)
Jack Neus761e1842020-12-01 18:20:11 +0000329
330
331class PushImageTest(cros_test_lib.MockTestCase, api_config.ApiConfigMixin):
332 """Push image test."""
333
334 def setUp(self):
335 self.response = image_pb2.PushImageResponse()
336
337 def _GetRequest(
338 self,
339 gs_image_dir='gs://chromeos-image-archive/atlas-release/R89-13604.0.0',
340 build_target_name='atlas',
341 profile='foo',
342 sign_types=None,
343 dryrun=True):
344 return image_pb2.PushImageRequest(
345 gs_image_dir=gs_image_dir,
346 sysroot=sysroot_pb2.Sysroot(
347 build_target=common_pb2.BuildTarget(name=build_target_name)),
348 profile=common_pb2.Profile(name=profile),
349 sign_types=sign_types,
350 dryrun=dryrun)
351
352 def testValidateOnly(self):
353 """Check that a validate only call does not execute any logic."""
354 patch = self.PatchObject(pushimage, 'PushImage')
355
356 req = self._GetRequest(sign_types=[
357 common_pb2.IMAGE_TYPE_RECOVERY, common_pb2.IMAGE_TYPE_FACTORY,
358 common_pb2.IMAGE_TYPE_FIRMWARE, common_pb2.IMAGE_TYPE_ACCESSORY_USBPD,
359 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG, common_pb2.IMAGE_TYPE_BASE,
360 common_pb2.IMAGE_TYPE_GSC_FIRMWARE
361 ])
362 res = image_controller.PushImage(req, self.response,
363 self.validate_only_config)
364 patch.assert_not_called()
365 self.assertEqual(res, controller.RETURN_CODE_VALID_INPUT)
366
367 def testValidateOnlyInvalid(self):
368 """Check that validate call rejects invalid sign types."""
369 patch = self.PatchObject(pushimage, 'PushImage')
370
371 # Pass unsupported image type.
372 req = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_DLC])
373 res = image_controller.PushImage(req, self.response,
374 self.validate_only_config)
375 patch.assert_not_called()
376 self.assertEqual(res, controller.RETURN_CODE_INVALID_INPUT)
377
378 def testMockCall(self):
379 """Test that mock call does not execute any logic, returns mocked value."""
380 patch = self.PatchObject(pushimage, 'PushImage')
381
382 rc = image_controller.PushImage(self._GetRequest(), self.response,
383 self.mock_call_config)
384 patch.assert_not_called()
385 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
386
387 def testMockError(self):
388 """Test that mock call does not execute any logic, returns error."""
389 patch = self.PatchObject(pushimage, 'PushImage')
390
391 rc = image_controller.PushImage(self._GetRequest(), self.response,
392 self.mock_error_config)
393 patch.assert_not_called()
394 self.assertEqual(controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY, rc)
395
396 def testNoBuildTarget(self):
397 """Test no build target given fails."""
398 request = self._GetRequest(build_target_name='')
399
400 # No build target should cause it to fail.
401 with self.assertRaises(cros_build_lib.DieSystemExit):
402 image_controller.PushImage(request, self.response, self.api_config)
403
404 def testNoGsImageDir(self):
405 """Test no image dir given fails."""
406 request = self._GetRequest(gs_image_dir='')
407
408 # No image dir should cause it to fail.
409 with self.assertRaises(cros_build_lib.DieSystemExit):
410 image_controller.PushImage(request, self.response, self.api_config)
411
412 def testCallCorrect(self):
413 """Check that a call is called with the correct parameters."""
414 patch = self.PatchObject(pushimage, 'PushImage')
415
416 request = self._GetRequest(
417 dryrun=False, profile='', sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
418 image_controller.PushImage(request, self.response, self.api_config)
419 patch.assert_called_with(
420 request.gs_image_dir,
421 request.sysroot.build_target.name,
422 dry_run=request.dryrun,
423 profile=request.profile.name,
424 sign_types=['recovery'])
425
426 def testCallSucceeds(self):
427 """Check that a (dry run) call is made successfully."""
428 request = self._GetRequest(sign_types=[common_pb2.IMAGE_TYPE_RECOVERY])
429 res = image_controller.PushImage(request, self.response, self.api_config)
430 self.assertEqual(res, controller.RETURN_CODE_SUCCESS)
431
432 def testCallFailsWithBadImageDir(self):
433 """Check that a (dry run) call fails when given a bad gs_image_dir."""
434 request = self._GetRequest(gs_image_dir='foo')
435 res = image_controller.PushImage(request, self.response, self.api_config)
436 self.assertEqual(res, controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY)