blob: dc6e03d04f763e0349c6f531413758c82d83ef54 [file] [log] [blame]
Alex Kleinda35fcf2019-03-07 16:01:15 -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"""Sysroot controller tests."""
6
Michael Mortensen798ee192020-01-17 13:04:43 -07007import datetime
Alex Kleinda35fcf2019-03-07 16:01:15 -07008import os
9
Alex Klein231d2da2019-07-22 16:44:45 -060010from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060011from chromite.api import controller
Alex Kleinca572ee2020-09-03 10:47:14 -060012from chromite.api.controller import controller_util
Alex Kleinda35fcf2019-03-07 16:01:15 -070013from chromite.api.controller import sysroot as sysroot_controller
14from chromite.api.gen.chromite.api import sysroot_pb2
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -070015from chromite.api.gen.chromiumos import common_pb2
LaMont Jonesc0343fa2020-08-12 18:58:31 -060016from chromite.lib import binpkg
Alex Kleinda35fcf2019-03-07 16:01:15 -070017from chromite.lib import cros_build_lib
18from chromite.lib import cros_test_lib
19from chromite.lib import osutils
Alex Kleinda35fcf2019-03-07 16:01:15 -070020from chromite.lib import sysroot_lib
Alex Klein18a60af2020-06-11 12:08:47 -060021from chromite.lib.parser import package_info
Alex Kleinda35fcf2019-03-07 16:01:15 -070022from chromite.service import sysroot as sysroot_service
23
24
Alex Klein231d2da2019-07-22 16:44:45 -060025class CreateTest(cros_test_lib.MockTestCase, api_config.ApiConfigMixin):
Alex Kleinda35fcf2019-03-07 16:01:15 -070026 """Create function tests."""
27
28 def _InputProto(self, build_target=None, profile=None, replace=False,
LaMont Jonesc0343fa2020-08-12 18:58:31 -060029 current=False, package_indexes=None):
Alex Kleinda35fcf2019-03-07 16:01:15 -070030 """Helper to build and input proto instance."""
31 proto = sysroot_pb2.SysrootCreateRequest()
32 if build_target:
33 proto.build_target.name = build_target
34 if profile:
35 proto.profile.name = profile
36 if replace:
37 proto.flags.replace = replace
38 if current:
39 proto.flags.chroot_current = current
LaMont Jonesc0343fa2020-08-12 18:58:31 -060040 if package_indexes:
41 proto.package_indexes.extend(package_indexes)
Alex Kleinda35fcf2019-03-07 16:01:15 -070042
43 return proto
44
45 def _OutputProto(self):
46 """Helper to build output proto instance."""
47 return sysroot_pb2.SysrootCreateResponse()
48
Alex Klein231d2da2019-07-22 16:44:45 -060049 def testValidateOnly(self):
50 """Sanity check that a validate only call does not execute any logic."""
51 patch = self.PatchObject(sysroot_service, 'Create')
52
53 board = 'board'
54 profile = None
55 force = False
56 upgrade_chroot = True
57 in_proto = self._InputProto(build_target=board, profile=profile,
58 replace=force, current=not upgrade_chroot)
59 sysroot_controller.Create(in_proto, self._OutputProto(),
60 self.validate_only_config)
61 patch.assert_not_called()
62
Alex Klein076841b2019-08-29 15:19:39 -060063 def testMockCall(self):
64 """Sanity check that a mock call does not execute any logic."""
65 patch = self.PatchObject(sysroot_service, 'Create')
66 request = self._InputProto()
67 response = self._OutputProto()
68
69 rc = sysroot_controller.Create(request, response, self.mock_call_config)
70
71 patch.assert_not_called()
72 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
73
74 def testMockError(self):
75 """Sanity check that a mock error does not execute any logic."""
76 patch = self.PatchObject(sysroot_service, 'Create')
77 request = self._InputProto()
78 response = self._OutputProto()
79
80 rc = sysroot_controller.Create(request, response, self.mock_error_config)
81
82 patch.assert_not_called()
83 self.assertEqual(controller.RETURN_CODE_UNRECOVERABLE, rc)
84
Alex Kleinda35fcf2019-03-07 16:01:15 -070085 def testArgumentValidation(self):
86 """Test the input argument validation."""
87 # Error when no name provided.
88 in_proto = self._InputProto()
89 out_proto = self._OutputProto()
90 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -060091 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -070092
93 # Valid when board passed.
94 result = sysroot_lib.Sysroot('/sysroot/path')
95 patch = self.PatchObject(sysroot_service, 'Create', return_value=result)
96 in_proto = self._InputProto('board')
97 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -060098 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -070099 patch.assert_called_once()
100
101 def testArgumentHandling(self):
102 """Test the arguments get processed and passed correctly."""
103 sysroot_path = '/sysroot/path'
104
105 sysroot = sysroot_lib.Sysroot(sysroot_path)
106 create_patch = self.PatchObject(sysroot_service, 'Create',
107 return_value=sysroot)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700108 rc_patch = self.PatchObject(sysroot_service, 'SetupBoardRunConfig')
109
110 # Default values.
111 board = 'board'
112 profile = None
113 force = False
114 upgrade_chroot = True
115 in_proto = self._InputProto(build_target=board, profile=profile,
116 replace=force, current=not upgrade_chroot)
117 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -0600118 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700119
120 # Default value checks.
LaMont Jonesfeffd1b2020-08-05 18:24:59 -0600121 rc_patch.assert_called_with(force=force, upgrade_chroot=upgrade_chroot,
122 package_indexes=[])
Alex Kleinda35fcf2019-03-07 16:01:15 -0700123 self.assertEqual(board, out_proto.sysroot.build_target.name)
124 self.assertEqual(sysroot_path, out_proto.sysroot.path)
125
126 # Not default values.
127 create_patch.reset_mock()
128 board = 'board'
129 profile = 'profile'
130 force = True
131 upgrade_chroot = False
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600132 package_indexes = [
133 common_pb2.PackageIndexInfo(
134 snapshot_sha='SHA', snapshot_number=5,
135 build_target=common_pb2.BuildTarget(name=board),
136 location='LOCATION', profile=common_pb2.Profile(name=profile)),
137 common_pb2.PackageIndexInfo(
138 snapshot_sha='SHA2', snapshot_number=4,
139 build_target=common_pb2.BuildTarget(name=board),
140 location='LOCATION2', profile=common_pb2.Profile(name=profile))]
141
Alex Kleinda35fcf2019-03-07 16:01:15 -0700142 in_proto = self._InputProto(build_target=board, profile=profile,
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600143 replace=force, current=not upgrade_chroot,
144 package_indexes=package_indexes)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700145 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -0600146 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700147
148 # Not default value checks.
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600149 rc_patch.assert_called_with(
150 force=force, package_indexes=[
151 binpkg.PackageIndexInfo.from_protobuf(x)
152 for x in package_indexes
153 ], upgrade_chroot=upgrade_chroot)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700154 self.assertEqual(board, out_proto.sysroot.build_target.name)
155 self.assertEqual(sysroot_path, out_proto.sysroot.path)
156
157
Michael Mortensen98592f62019-09-27 13:34:18 -0600158class CreateSimpleChromeSysrootTest(cros_test_lib.MockTempDirTestCase,
159 api_config.ApiConfigMixin):
160 """CreateSimpleChromeSysroot function tests."""
161
162 def _InputProto(self, build_target=None, use_flags=None):
163 """Helper to build and input proto instance."""
164 proto = sysroot_pb2.CreateSimpleChromeSysrootRequest()
165 if build_target:
166 proto.build_target.name = build_target
167 if use_flags:
168 proto.use_flags = use_flags
169 return proto
170
171 def _OutputProto(self):
172 """Helper to build output proto instance."""
173 return sysroot_pb2.CreateSimpleChromeSysrootResponse()
174
175 def testValidateOnly(self):
176 """Sanity check that a validate only call does not execute any logic."""
177 patch = self.PatchObject(sysroot_service, 'CreateSimpleChromeSysroot')
178
179 board = 'board'
180 in_proto = self._InputProto(build_target=board, use_flags=[])
181 sysroot_controller.CreateSimpleChromeSysroot(in_proto, self._OutputProto(),
182 self.validate_only_config)
183 patch.assert_not_called()
184
Michael Mortensen3232ab32020-01-05 19:14:36 -0700185 def testMockCall(self):
186 """Sanity check that a mock call does not execute any logic."""
187 patch = self.PatchObject(sysroot_service, 'CreateSimpleChromeSysroot')
188
189 board = 'board'
190 in_proto = self._InputProto(build_target=board, use_flags=[])
191 rc = sysroot_controller.CreateSimpleChromeSysroot(in_proto,
192 self._OutputProto(),
193 self.mock_call_config)
194 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
195 patch.assert_not_called()
196
Michael Mortensen98592f62019-09-27 13:34:18 -0600197 def testArgumentValidation(self):
198 """Test the input argument validation."""
199 # Error when no build target provided.
200 in_proto = self._InputProto()
201 out_proto = self._OutputProto()
202 with self.assertRaises(cros_build_lib.DieSystemExit):
203 sysroot_controller.CreateSimpleChromeSysroot(in_proto, out_proto,
204 self.api_config)
205
206 # Valid when board is specified.
207 patch = self.PatchObject(sysroot_service, 'CreateSimpleChromeSysroot',
208 return_value='/path/to/sysroot/tar.bz')
209 in_proto = self._InputProto(build_target='board')
210 out_proto = self._OutputProto()
211 sysroot_controller.CreateSimpleChromeSysroot(in_proto, out_proto,
212 self.api_config)
213 patch.assert_called_once()
214
215
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700216class GenerateArchiveTest(cros_test_lib.MockTempDirTestCase,
217 api_config.ApiConfigMixin):
218 """GenerateArchive function tests."""
219
220 def setUp(self):
221 self.chroot_path = '/path/to/chroot'
222 self.board = 'board'
223
224 def _InputProto(self, build_target=None, chroot_path=None, pkg_list=None):
225 """Helper to build and input proto instance."""
226 # pkg_list will be a list of category/package strings such as
227 # ['virtual/target-fuzzers'].
228 if pkg_list:
229 package_list = []
230 for pkg in pkg_list:
231 pkg_string_parts = pkg.split('/')
Alex Klein18a60af2020-06-11 12:08:47 -0600232 package_info_msg = common_pb2.PackageInfo(
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700233 category=pkg_string_parts[0],
234 package_name=pkg_string_parts[1])
Alex Klein18a60af2020-06-11 12:08:47 -0600235 package_list.append(package_info_msg)
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700236 else:
237 package_list = []
238
239 return sysroot_pb2.SysrootGenerateArchiveRequest(
240 build_target={'name': build_target},
241 chroot={'path': chroot_path},
242 packages=package_list)
243
244 def _OutputProto(self):
245 """Helper to build output proto instance."""
246 return sysroot_pb2.SysrootGenerateArchiveResponse()
247
248 def testValidateOnly(self):
249 """Sanity check that a validate only call does not execute any logic."""
250 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
251
252 in_proto = self._InputProto(build_target=self.board,
253 chroot_path=self.chroot_path,
254 pkg_list=['virtual/target-fuzzers'])
255 sysroot_controller.GenerateArchive(in_proto, self._OutputProto(),
256 self.validate_only_config)
257 patch.assert_not_called()
258
259 def testMockCall(self):
260 """Sanity check that a mock call does not execute any logic."""
261 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
262
263 in_proto = self._InputProto(build_target=self.board,
264 chroot_path=self.chroot_path,
265 pkg_list=['virtual/target-fuzzers'])
266 sysroot_controller.GenerateArchive(in_proto,
267 self._OutputProto(),
268 self.mock_call_config)
269 patch.assert_not_called()
270
271 def testArgumentValidation(self):
272 """Test the input argument validation."""
273 # Error when no build target provided.
274 in_proto = self._InputProto()
275 out_proto = self._OutputProto()
276 with self.assertRaises(cros_build_lib.DieSystemExit):
277 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
278
279 # Error when packages is not specified.
280 in_proto = self._InputProto(build_target='board',
281 chroot_path=self.chroot_path)
282 with self.assertRaises(cros_build_lib.DieSystemExit):
283 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
284
285 # Valid when board, chroot path, and package are specified.
286 patch = self.PatchObject(sysroot_service, 'GenerateArchive',
287 return_value='/path/to/sysroot/tar.bz')
288 in_proto = self._InputProto(build_target='board',
289 chroot_path=self.chroot_path,
290 pkg_list=['virtual/target-fuzzers'])
291 out_proto = self._OutputProto()
292 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
293 patch.assert_called_once()
294
295
Alex Klein231d2da2019-07-22 16:44:45 -0600296class InstallToolchainTest(cros_test_lib.MockTempDirTestCase,
297 api_config.ApiConfigMixin):
Alex Kleinda35fcf2019-03-07 16:01:15 -0700298 """Install toolchain function tests."""
299
300 def setUp(self):
301 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600302 # Avoid running the portageq command.
303 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleinda35fcf2019-03-07 16:01:15 -0700304 self.board = 'board'
305 self.sysroot = os.path.join(self.tempdir, 'board')
306 self.invalid_sysroot = os.path.join(self.tempdir, 'invalid', 'sysroot')
307 osutils.SafeMakedirs(self.sysroot)
308
309 def _InputProto(self, build_target=None, sysroot_path=None,
310 compile_source=False):
Alex Kleind4e1e422019-03-18 16:00:41 -0600311 """Helper to build an input proto instance."""
Alex Kleinda35fcf2019-03-07 16:01:15 -0700312 proto = sysroot_pb2.InstallToolchainRequest()
313 if build_target:
314 proto.sysroot.build_target.name = build_target
315 if sysroot_path:
316 proto.sysroot.path = sysroot_path
317 if compile_source:
318 proto.flags.compile_source = compile_source
319
320 return proto
321
322 def _OutputProto(self):
323 """Helper to build output proto instance."""
324 return sysroot_pb2.InstallToolchainResponse()
325
Alex Klein231d2da2019-07-22 16:44:45 -0600326 def testValidateOnly(self):
327 """Sanity check that a validate only call does not execute any logic."""
328 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
329
330 in_proto = self._InputProto(build_target=self.board,
331 sysroot_path=self.sysroot)
332 sysroot_controller.InstallToolchain(in_proto, self._OutputProto(),
333 self.validate_only_config)
334 patch.assert_not_called()
335
Alex Klein076841b2019-08-29 15:19:39 -0600336 def testMockCall(self):
337 """Sanity check that a mock call does not execute any logic."""
338 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
339 request = self._InputProto()
340 response = self._OutputProto()
341
342 rc = sysroot_controller.InstallToolchain(request, response,
343 self.mock_call_config)
344
345 patch.assert_not_called()
346 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
347
348 def testMockError(self):
349 """Sanity check that a mock error does not execute any logic."""
350 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
351 request = self._InputProto()
352 response = self._OutputProto()
353
354 rc = sysroot_controller.InstallToolchain(request, response,
355 self.mock_error_config)
356
357 patch.assert_not_called()
358 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
359 self.assertTrue(response.failed_packages)
360
Alex Kleinda35fcf2019-03-07 16:01:15 -0700361 def testArgumentValidation(self):
362 """Test the argument validation."""
363 # Test errors on missing inputs.
364 out_proto = self._OutputProto()
365 # Both missing.
366 in_proto = self._InputProto()
367 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600368 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700369
370 # Sysroot path missing.
371 in_proto = self._InputProto(build_target=self.board)
372 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600373 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700374
375 # Build target name missing.
376 in_proto = self._InputProto(sysroot_path=self.sysroot)
377 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600378 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700379
380 # Both provided, but invalid sysroot path.
381 in_proto = self._InputProto(build_target=self.board,
382 sysroot_path=self.invalid_sysroot)
383 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600384 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700385
386 def testSuccessOutputHandling(self):
387 """Test the output is processed and recorded correctly."""
388 self.PatchObject(sysroot_service, 'InstallToolchain')
389 out_proto = self._OutputProto()
390 in_proto = self._InputProto(build_target=self.board,
391 sysroot_path=self.sysroot)
392
Alex Klein231d2da2019-07-22 16:44:45 -0600393 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
394 self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700395 self.assertFalse(rc)
396 self.assertFalse(out_proto.failed_packages)
397
398
399 def testErrorOutputHandling(self):
400 """Test the error output is processed and recorded correctly."""
401 out_proto = self._OutputProto()
402 in_proto = self._InputProto(build_target=self.board,
403 sysroot_path=self.sysroot)
404
405 err_pkgs = ['cat/pkg', 'cat2/pkg2']
Alex Klein18a60af2020-06-11 12:08:47 -0600406 err_cpvs = [package_info.SplitCPV(pkg, strict=False) for pkg in err_pkgs]
Alex Kleinda35fcf2019-03-07 16:01:15 -0700407 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
408 err = sysroot_lib.ToolchainInstallError('Error',
409 cros_build_lib.CommandResult(),
410 tc_info=err_cpvs)
411 self.PatchObject(sysroot_service, 'InstallToolchain', side_effect=err)
412
Alex Klein231d2da2019-07-22 16:44:45 -0600413 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
414 self.api_config)
Alex Klein8cb365a2019-05-15 16:24:53 -0600415 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700416 self.assertTrue(out_proto.failed_packages)
417 for package in out_proto.failed_packages:
418 cat_pkg = (package.category, package.package_name)
419 self.assertIn(cat_pkg, expected)
Alex Kleind4e1e422019-03-18 16:00:41 -0600420
421
Alex Klein231d2da2019-07-22 16:44:45 -0600422class InstallPackagesTest(cros_test_lib.MockTempDirTestCase,
423 api_config.ApiConfigMixin):
Alex Kleind4e1e422019-03-18 16:00:41 -0600424 """InstallPackages tests."""
425
426 def setUp(self):
427 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600428 # Avoid running the portageq command.
429 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleind4e1e422019-03-18 16:00:41 -0600430 self.build_target = 'board'
431 self.sysroot = os.path.join(self.tempdir, 'build', 'board')
432 osutils.SafeMakedirs(self.sysroot)
Michael Mortensen798ee192020-01-17 13:04:43 -0700433 # Set up goma directories.
434 self.goma_dir = os.path.join(self.tempdir, 'goma_dir')
435 osutils.SafeMakedirs(self.goma_dir)
436 self.goma_out_dir = os.path.join(self.tempdir, 'goma_out_dir')
437 osutils.SafeMakedirs(self.goma_out_dir)
Michael Mortensen4ccfb082020-01-22 16:24:03 -0700438 os.environ['GLOG_log_dir'] = self.goma_dir
Alex Kleind4e1e422019-03-18 16:00:41 -0600439
440 def _InputProto(self, build_target=None, sysroot_path=None,
Michael Mortensen798ee192020-01-17 13:04:43 -0700441 build_source=False, goma_dir=None, goma_log_dir=None,
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600442 goma_stats_file=None, goma_counterz_file=None,
Alex Kleinca572ee2020-09-03 10:47:14 -0600443 package_indexes=None, packages=None):
Alex Kleind4e1e422019-03-18 16:00:41 -0600444 """Helper to build an input proto instance."""
445 instance = sysroot_pb2.InstallPackagesRequest()
446
447 if build_target:
448 instance.sysroot.build_target.name = build_target
449 if sysroot_path:
450 instance.sysroot.path = sysroot_path
451 if build_source:
452 instance.flags.build_source = build_source
Michael Mortensen798ee192020-01-17 13:04:43 -0700453 if goma_dir:
454 instance.goma_config.goma_dir = goma_dir
455 if goma_log_dir:
456 instance.goma_config.log_dir.dir = goma_log_dir
457 if goma_stats_file:
458 instance.goma_config.stats_file = goma_stats_file
459 if goma_counterz_file:
460 instance.goma_config.counterz_file = goma_counterz_file
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600461 if package_indexes:
462 instance.package_indexes.extend(package_indexes)
Alex Kleinca572ee2020-09-03 10:47:14 -0600463 if packages:
464 for pkg in packages:
465 pkg_info = instance.packages.add()
Alex Klein18a60af2020-06-11 12:08:47 -0600466 cpv = package_info.SplitCPV(pkg, strict=False)
Alex Kleinca572ee2020-09-03 10:47:14 -0600467 controller_util.CPVToPackageInfo(cpv, pkg_info)
Alex Kleind4e1e422019-03-18 16:00:41 -0600468 return instance
469
470 def _OutputProto(self):
471 """Helper to build an empty output proto instance."""
472 return sysroot_pb2.InstallPackagesResponse()
473
Michael Mortensen798ee192020-01-17 13:04:43 -0700474 def _CreateGomaLogFile(self, goma_log_dir, name, timestamp):
475 """Creates a log file for testing.
476
477 Args:
478 goma_log_dir (str): Directory where the file will be created.
479 name (str): Log file 'base' name that is combined with the timestamp.
480 timestamp (datetime): timestamp that is written to the file.
481 """
482 path = os.path.join(
483 goma_log_dir,
484 '%s.host.log.INFO.%s' % (name, timestamp.strftime('%Y%m%d-%H%M%S.%f')))
485 osutils.WriteFile(
486 path,
487 timestamp.strftime('Goma log file created at: %Y/%m/%d %H:%M:%S'))
488
Alex Klein231d2da2019-07-22 16:44:45 -0600489 def testValidateOnly(self):
490 """Sanity check that a validate only call does not execute any logic."""
491 patch = self.PatchObject(sysroot_service, 'BuildPackages')
492
493 in_proto = self._InputProto(build_target=self.build_target,
494 sysroot_path=self.sysroot)
495 sysroot_controller.InstallPackages(in_proto, self._OutputProto(),
496 self.validate_only_config)
497 patch.assert_not_called()
498
Alex Klein076841b2019-08-29 15:19:39 -0600499 def testMockCall(self):
500 """Sanity check that a mock call does not execute any logic."""
501 patch = self.PatchObject(sysroot_service, 'BuildPackages')
502 request = self._InputProto()
503 response = self._OutputProto()
504
505 rc = sysroot_controller.InstallPackages(request, response,
506 self.mock_call_config)
507
508 patch.assert_not_called()
509 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
510
511 def testMockError(self):
512 """Sanity check that a mock error does not execute any logic."""
513 patch = self.PatchObject(sysroot_service, 'BuildPackages')
514 request = self._InputProto()
515 response = self._OutputProto()
516
517 rc = sysroot_controller.InstallPackages(request, response,
518 self.mock_error_config)
519
520 patch.assert_not_called()
521 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
522 self.assertTrue(response.failed_packages)
523
Alex Kleind4e1e422019-03-18 16:00:41 -0600524 def testArgumentValidationAllMissing(self):
525 """Test missing all arguments."""
526 out_proto = self._OutputProto()
527 in_proto = self._InputProto()
528 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600529 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600530
531 def testArgumentValidationNoSysroot(self):
532 """Test missing sysroot path."""
533 out_proto = self._OutputProto()
534 in_proto = self._InputProto(build_target=self.build_target)
535 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600536 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600537
538 def testArgumentValidationNoBuildTarget(self):
539 """Test missing build target name."""
540 out_proto = self._OutputProto()
541 in_proto = self._InputProto(sysroot_path=self.sysroot)
542 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600543 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600544
545 def testArgumentValidationInvalidSysroot(self):
546 """Test sysroot that hasn't had the toolchain installed."""
547 out_proto = self._OutputProto()
548 in_proto = self._InputProto(build_target=self.build_target,
549 sysroot_path=self.sysroot)
550 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
551 return_value=False)
552 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600553 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600554
Alex Kleinca572ee2020-09-03 10:47:14 -0600555 def testArgumentValidationInvalidPackage(self):
556 out_proto = self._OutputProto()
557 in_proto = self._InputProto(build_target=self.build_target,
558 sysroot_path=self.sysroot,
559 packages=['package-1.0.0-r2'])
560 with self.assertRaises(cros_build_lib.DieSystemExit):
561 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
562
Alex Kleind4e1e422019-03-18 16:00:41 -0600563 def testSuccessOutputHandling(self):
564 """Test successful call output handling."""
565 # Prevent argument validation error.
566 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
567 return_value=True)
568
569 in_proto = self._InputProto(build_target=self.build_target,
570 sysroot_path=self.sysroot)
571 out_proto = self._OutputProto()
572 self.PatchObject(sysroot_service, 'BuildPackages')
573
Alex Klein231d2da2019-07-22 16:44:45 -0600574 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
575 self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600576 self.assertFalse(rc)
577 self.assertFalse(out_proto.failed_packages)
578
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600579 def testSuccessPackageIndexes(self):
580 """Test successful call with package_indexes."""
581 # Prevent argument validation error.
582 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
583 return_value=True)
584 package_indexes = [
585 common_pb2.PackageIndexInfo(
586 snapshot_sha='SHA', snapshot_number=5,
587 build_target=common_pb2.BuildTarget(name='board'),
588 location='LOCATION', profile=common_pb2.Profile(name='profile')),
589 common_pb2.PackageIndexInfo(
590 snapshot_sha='SHA2', snapshot_number=4,
591 build_target=common_pb2.BuildTarget(name='board'),
592 location='LOCATION2', profile=common_pb2.Profile(name='profile'))]
593
594 in_proto = self._InputProto(build_target=self.build_target,
595 sysroot_path=self.sysroot,
596 package_indexes=package_indexes)
597
598 out_proto = self._OutputProto()
599 rc_patch = self.PatchObject(sysroot_service, 'BuildPackagesRunConfig')
600 self.PatchObject(sysroot_service, 'BuildPackages')
601
602 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
603 self.api_config)
604 self.assertFalse(rc)
Alex Kleineb76da72021-03-19 10:43:09 -0600605 rc_patch.assert_called_with(
606 usepkg=True,
607 install_debug_symbols=True,
608 packages=[],
609 package_indexes=[
610 binpkg.PackageIndexInfo.from_protobuf(x) for x in package_indexes
611 ],
612 use_flags=[],
613 use_goma=False,
614 incremental_build=False,
615 setup_board=False)
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600616
Michael Mortensen798ee192020-01-17 13:04:43 -0700617 def testSuccessWithGomaLogs(self):
618 """Test successful call with goma."""
619 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
620 datetime.datetime(2018, 9, 21, 12, 0, 0))
621 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
622 datetime.datetime(2018, 9, 21, 12, 1, 0))
623 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
624 datetime.datetime(2018, 9, 21, 12, 2, 0))
625
626 # Prevent argument validation error.
627 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
628 return_value=True)
629
630 in_proto = self._InputProto(build_target=self.build_target,
631 sysroot_path=self.sysroot,
632 goma_dir=self.goma_dir,
633 goma_log_dir=self.goma_out_dir)
634
635 out_proto = self._OutputProto()
636 self.PatchObject(sysroot_service, 'BuildPackages')
637
638 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
639 self.api_config)
640 self.assertFalse(rc)
641 self.assertFalse(out_proto.failed_packages)
642 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
643 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
644 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
645 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
646
647 def testSuccessWithGomaLogsAndStatsCounterzFiles(self):
648 """Test successful call with goma including stats and counterz files."""
649 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
650 datetime.datetime(2018, 9, 21, 12, 0, 0))
651 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
652 datetime.datetime(2018, 9, 21, 12, 1, 0))
653 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
654 datetime.datetime(2018, 9, 21, 12, 2, 0))
655 # Create stats and counterz files.
656 osutils.WriteFile(os.path.join(self.goma_dir, 'stats.binaryproto'),
657 'File: stats.binaryproto')
658 osutils.WriteFile(os.path.join(self.goma_dir, 'counterz.binaryproto'),
659 'File: counterz.binaryproto')
660
661 # Prevent argument validation error.
662 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
663 return_value=True)
664
665 in_proto = self._InputProto(build_target=self.build_target,
666 sysroot_path=self.sysroot,
667 goma_dir=self.goma_dir,
668 goma_log_dir=self.goma_out_dir,
669 goma_stats_file='stats.binaryproto',
670 goma_counterz_file='counterz.binaryproto')
671
672 out_proto = self._OutputProto()
673 self.PatchObject(sysroot_service, 'BuildPackages')
674
675 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
676 self.api_config)
677 self.assertFalse(rc)
678 self.assertFalse(out_proto.failed_packages)
679 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
680 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
681 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
682 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
683 # Verify that the output dir has 5 files -- since there should be 3 log
684 # files, the stats file, and the counterz file.
685 output_files = os.listdir(self.goma_out_dir)
686 self.assertCountEqual(output_files, [
687 'stats.binaryproto',
688 'counterz.binaryproto',
689 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
690 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
691 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
Michael Mortensen1c7439c2020-01-24 14:43:19 -0700692 self.assertEqual(out_proto.goma_artifacts.counterz_file,
693 'counterz.binaryproto')
694 self.assertEqual(out_proto.goma_artifacts.stats_file,
695 'stats.binaryproto')
Michael Mortensen798ee192020-01-17 13:04:43 -0700696
697 def testFailureMissingGomaStatsCounterzFiles(self):
698 """Test successful call with goma including stats and counterz files."""
699 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
700 datetime.datetime(2018, 9, 21, 12, 0, 0))
701 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
702 datetime.datetime(2018, 9, 21, 12, 1, 0))
703 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
704 datetime.datetime(2018, 9, 21, 12, 2, 0))
705 # Note that stats and counterz files are not created, but are specified in
706 # the proto below.
707
708 # Prevent argument validation error.
709 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
710 return_value=True)
711
712 in_proto = self._InputProto(build_target=self.build_target,
713 sysroot_path=self.sysroot,
714 goma_dir=self.goma_dir,
715 goma_log_dir=self.goma_out_dir,
716 goma_stats_file='stats.binaryproto',
717 goma_counterz_file='counterz.binaryproto')
718
719 out_proto = self._OutputProto()
720 self.PatchObject(sysroot_service, 'BuildPackages')
721
Michael Mortensen1d6d5b02020-01-22 07:33:50 -0700722 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
723 self.api_config)
724 self.assertFalse(rc)
725 self.assertFalse(out_proto.failed_packages)
726 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
727 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
728 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
729 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
730 self.assertFalse(out_proto.goma_artifacts.counterz_file)
731 self.assertFalse(out_proto.goma_artifacts.stats_file)
Michael Mortensen798ee192020-01-17 13:04:43 -0700732
Alex Kleind4e1e422019-03-18 16:00:41 -0600733 def testFailureOutputHandling(self):
734 """Test failed package handling."""
735 # Prevent argument validation error.
736 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
737 return_value=True)
738
739 in_proto = self._InputProto(build_target=self.build_target,
740 sysroot_path=self.sysroot)
741 out_proto = self._OutputProto()
742
743 # Failed package info and expected list for verification.
744 err_pkgs = ['cat/pkg', 'cat2/pkg2']
Alex Klein18a60af2020-06-11 12:08:47 -0600745 err_cpvs = [package_info.SplitCPV(cpv, strict=False) for cpv in err_pkgs]
Alex Kleind4e1e422019-03-18 16:00:41 -0600746 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
747
748 # Force error to be raised with the packages.
749 error = sysroot_lib.PackageInstallError('Error',
750 cros_build_lib.CommandResult(),
751 packages=err_cpvs)
752 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
753
Alex Klein231d2da2019-07-22 16:44:45 -0600754 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
755 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600756 # This needs to return 2 to indicate the available error response.
757 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleind4e1e422019-03-18 16:00:41 -0600758 for package in out_proto.failed_packages:
759 cat_pkg = (package.category, package.package_name)
760 self.assertIn(cat_pkg, expected)
Alex Klein2557b4f2019-07-11 14:34:00 -0600761
762 def testNoPackageFailureOutputHandling(self):
763 """Test failure handling without packages to report."""
764 # Prevent argument validation error.
765 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
766 return_value=True)
767
768 in_proto = self._InputProto(build_target=self.build_target,
769 sysroot_path=self.sysroot)
770 out_proto = self._OutputProto()
771
772 # Force error to be raised with no packages.
773 error = sysroot_lib.PackageInstallError('Error',
774 cros_build_lib.CommandResult(),
775 packages=[])
776 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
777
Alex Klein231d2da2019-07-22 16:44:45 -0600778 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
779 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600780 # All we really care about is it's not 0 or 2 (response available), so
781 # test for that rather than a specific return code.
782 self.assertTrue(rc)
783 self.assertNotEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE,
784 rc)