blob: fa549dcb7848b8ab6db051a540c8de7d0dc9cd7b [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 Mortensen3f6b4bd2020-02-07 14:16:43 -0700158class GenerateArchiveTest(cros_test_lib.MockTempDirTestCase,
159 api_config.ApiConfigMixin):
160 """GenerateArchive function tests."""
161
162 def setUp(self):
163 self.chroot_path = '/path/to/chroot'
164 self.board = 'board'
165
166 def _InputProto(self, build_target=None, chroot_path=None, pkg_list=None):
167 """Helper to build and input proto instance."""
168 # pkg_list will be a list of category/package strings such as
169 # ['virtual/target-fuzzers'].
170 if pkg_list:
171 package_list = []
172 for pkg in pkg_list:
173 pkg_string_parts = pkg.split('/')
Alex Klein18a60af2020-06-11 12:08:47 -0600174 package_info_msg = common_pb2.PackageInfo(
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700175 category=pkg_string_parts[0],
176 package_name=pkg_string_parts[1])
Alex Klein18a60af2020-06-11 12:08:47 -0600177 package_list.append(package_info_msg)
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700178 else:
179 package_list = []
180
181 return sysroot_pb2.SysrootGenerateArchiveRequest(
182 build_target={'name': build_target},
183 chroot={'path': chroot_path},
184 packages=package_list)
185
186 def _OutputProto(self):
187 """Helper to build output proto instance."""
188 return sysroot_pb2.SysrootGenerateArchiveResponse()
189
190 def testValidateOnly(self):
191 """Sanity check that a validate only call does not execute any logic."""
192 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
193
194 in_proto = self._InputProto(build_target=self.board,
195 chroot_path=self.chroot_path,
196 pkg_list=['virtual/target-fuzzers'])
197 sysroot_controller.GenerateArchive(in_proto, self._OutputProto(),
198 self.validate_only_config)
199 patch.assert_not_called()
200
201 def testMockCall(self):
202 """Sanity check that a mock call does not execute any logic."""
203 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
204
205 in_proto = self._InputProto(build_target=self.board,
206 chroot_path=self.chroot_path,
207 pkg_list=['virtual/target-fuzzers'])
208 sysroot_controller.GenerateArchive(in_proto,
209 self._OutputProto(),
210 self.mock_call_config)
211 patch.assert_not_called()
212
213 def testArgumentValidation(self):
214 """Test the input argument validation."""
215 # Error when no build target provided.
216 in_proto = self._InputProto()
217 out_proto = self._OutputProto()
218 with self.assertRaises(cros_build_lib.DieSystemExit):
219 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
220
221 # Error when packages is not specified.
222 in_proto = self._InputProto(build_target='board',
223 chroot_path=self.chroot_path)
224 with self.assertRaises(cros_build_lib.DieSystemExit):
225 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
226
227 # Valid when board, chroot path, and package are specified.
228 patch = self.PatchObject(sysroot_service, 'GenerateArchive',
229 return_value='/path/to/sysroot/tar.bz')
230 in_proto = self._InputProto(build_target='board',
231 chroot_path=self.chroot_path,
232 pkg_list=['virtual/target-fuzzers'])
233 out_proto = self._OutputProto()
234 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
235 patch.assert_called_once()
236
237
Alex Klein231d2da2019-07-22 16:44:45 -0600238class InstallToolchainTest(cros_test_lib.MockTempDirTestCase,
239 api_config.ApiConfigMixin):
Alex Kleinda35fcf2019-03-07 16:01:15 -0700240 """Install toolchain function tests."""
241
242 def setUp(self):
243 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600244 # Avoid running the portageq command.
245 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleinda35fcf2019-03-07 16:01:15 -0700246 self.board = 'board'
247 self.sysroot = os.path.join(self.tempdir, 'board')
248 self.invalid_sysroot = os.path.join(self.tempdir, 'invalid', 'sysroot')
249 osutils.SafeMakedirs(self.sysroot)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000250 # Set up portage log directory.
Lizzy Presland4c279832021-11-19 20:27:43 +0000251 self.target_sysroot = sysroot_lib.Sysroot(self.sysroot)
Alex Kleine1c4d4b2022-01-05 14:51:04 -0700252 self.portage_dir = os.path.join(self.tempdir, 'portage_logdir')
253 self.PatchObject(
254 sysroot_lib.Sysroot, 'portage_logdir', new=self.portage_dir)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000255 osutils.SafeMakedirs(self.portage_dir)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700256
257 def _InputProto(self, build_target=None, sysroot_path=None,
258 compile_source=False):
Alex Kleind4e1e422019-03-18 16:00:41 -0600259 """Helper to build an input proto instance."""
Alex Kleinda35fcf2019-03-07 16:01:15 -0700260 proto = sysroot_pb2.InstallToolchainRequest()
261 if build_target:
262 proto.sysroot.build_target.name = build_target
263 if sysroot_path:
264 proto.sysroot.path = sysroot_path
265 if compile_source:
266 proto.flags.compile_source = compile_source
267
268 return proto
269
270 def _OutputProto(self):
271 """Helper to build output proto instance."""
272 return sysroot_pb2.InstallToolchainResponse()
273
Lizzy Presland4c279832021-11-19 20:27:43 +0000274 def _CreatePortageLogFile(self, log_path, pkg_info, timestamp):
Lizzy Presland7e23a612021-11-09 21:49:42 +0000275 """Creates a log file for testing for individual packages built by Portage.
276
277 Args:
Lizzy Presland4c279832021-11-19 20:27:43 +0000278 log_path (pathlike): the PORTAGE_LOGDIR path
Lizzy Presland7e23a612021-11-09 21:49:42 +0000279 pkg_info (PackageInfo): name components for log file.
280 timestamp (datetime): timestamp used to name the file.
281 """
Lizzy Presland4c279832021-11-19 20:27:43 +0000282 path = os.path.join(log_path,
Lizzy Presland77741782021-12-13 19:46:42 +0000283 f'{pkg_info.category}:{pkg_info.pvr}:' \
Lizzy Presland7e23a612021-11-09 21:49:42 +0000284 f'{timestamp.strftime("%Y%m%d-%H%M%S")}.log')
285 osutils.WriteFile(path,
286 f'Test log file for package {pkg_info.category}/'
287 f'{pkg_info.package} written to {path}')
288 return path
289
Alex Klein231d2da2019-07-22 16:44:45 -0600290 def testValidateOnly(self):
291 """Sanity check that a validate only call does not execute any logic."""
292 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
293
294 in_proto = self._InputProto(build_target=self.board,
295 sysroot_path=self.sysroot)
296 sysroot_controller.InstallToolchain(in_proto, self._OutputProto(),
297 self.validate_only_config)
298 patch.assert_not_called()
299
Alex Klein076841b2019-08-29 15:19:39 -0600300 def testMockCall(self):
301 """Sanity check that a mock call does not execute any logic."""
302 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
303 request = self._InputProto()
304 response = self._OutputProto()
305
306 rc = sysroot_controller.InstallToolchain(request, response,
307 self.mock_call_config)
308
309 patch.assert_not_called()
310 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
311
312 def testMockError(self):
313 """Sanity check that a mock error does not execute any logic."""
314 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
315 request = self._InputProto()
316 response = self._OutputProto()
317
318 rc = sysroot_controller.InstallToolchain(request, response,
319 self.mock_error_config)
320
321 patch.assert_not_called()
322 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
323 self.assertTrue(response.failed_packages)
324
Alex Kleinda35fcf2019-03-07 16:01:15 -0700325 def testArgumentValidation(self):
326 """Test the argument validation."""
327 # Test errors on missing inputs.
328 out_proto = self._OutputProto()
329 # Both missing.
330 in_proto = self._InputProto()
331 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600332 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700333
334 # Sysroot path missing.
335 in_proto = self._InputProto(build_target=self.board)
336 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600337 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700338
339 # Build target name missing.
340 in_proto = self._InputProto(sysroot_path=self.sysroot)
341 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600342 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700343
344 # Both provided, but invalid sysroot path.
345 in_proto = self._InputProto(build_target=self.board,
346 sysroot_path=self.invalid_sysroot)
347 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600348 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700349
350 def testSuccessOutputHandling(self):
351 """Test the output is processed and recorded correctly."""
352 self.PatchObject(sysroot_service, 'InstallToolchain')
353 out_proto = self._OutputProto()
354 in_proto = self._InputProto(build_target=self.board,
355 sysroot_path=self.sysroot)
356
Alex Klein231d2da2019-07-22 16:44:45 -0600357 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
358 self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700359 self.assertFalse(rc)
360 self.assertFalse(out_proto.failed_packages)
361
362
363 def testErrorOutputHandling(self):
364 """Test the error output is processed and recorded correctly."""
365 out_proto = self._OutputProto()
366 in_proto = self._InputProto(build_target=self.board,
367 sysroot_path=self.sysroot)
368
Lizzy Presland77741782021-12-13 19:46:42 +0000369 err_pkgs = ['cat/pkg-1.0-r1', 'cat2/pkg2-1.0-r1']
Alex Kleinea0c89e2021-09-09 15:17:35 -0600370 err_cpvs = [package_info.parse(pkg) for pkg in err_pkgs]
Alex Kleinda35fcf2019-03-07 16:01:15 -0700371 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
Lizzy Presland7e23a612021-11-09 21:49:42 +0000372
373 new_logs = {}
374 for i, pkg in enumerate(err_pkgs):
Lizzy Presland4c279832021-11-19 20:27:43 +0000375 self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000376 datetime.datetime(2021, 6, 9, 13, 37, 0))
Lizzy Presland4c279832021-11-19 20:27:43 +0000377 new_logs[pkg] = self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000378 datetime.datetime(2021, 6, 9,
379 16, 20, 0)
380 )
381
Alex Kleinda35fcf2019-03-07 16:01:15 -0700382 err = sysroot_lib.ToolchainInstallError('Error',
383 cros_build_lib.CommandResult(),
384 tc_info=err_cpvs)
385 self.PatchObject(sysroot_service, 'InstallToolchain', side_effect=err)
386
Alex Klein231d2da2019-07-22 16:44:45 -0600387 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
388 self.api_config)
Alex Klein8cb365a2019-05-15 16:24:53 -0600389 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700390 self.assertTrue(out_proto.failed_packages)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000391 self.assertTrue(out_proto.failed_package_data)
392 # This needs to return 2 to indicate the available error response.
393 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
394 for data in out_proto.failed_package_data:
395 package = controller_util.deserialize_package_info(data.name)
396 cat_pkg = (data.name.category, data.name.package_name)
397 self.assertIn(cat_pkg, expected)
Lizzy Presland77741782021-12-13 19:46:42 +0000398 self.assertEqual(data.log_path.path, new_logs[package.cpvr])
Lizzy Presland7e23a612021-11-09 21:49:42 +0000399
400 # TODO(b/206514844): remove when field is deleted
Alex Kleinda35fcf2019-03-07 16:01:15 -0700401 for package in out_proto.failed_packages:
402 cat_pkg = (package.category, package.package_name)
403 self.assertIn(cat_pkg, expected)
Alex Kleind4e1e422019-03-18 16:00:41 -0600404
405
Alex Klein231d2da2019-07-22 16:44:45 -0600406class InstallPackagesTest(cros_test_lib.MockTempDirTestCase,
407 api_config.ApiConfigMixin):
Alex Kleind4e1e422019-03-18 16:00:41 -0600408 """InstallPackages tests."""
409
410 def setUp(self):
411 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600412 # Avoid running the portageq command.
413 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleind4e1e422019-03-18 16:00:41 -0600414 self.build_target = 'board'
415 self.sysroot = os.path.join(self.tempdir, 'build', 'board')
416 osutils.SafeMakedirs(self.sysroot)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000417 # Set up portage log directory.
Lizzy Presland4c279832021-11-19 20:27:43 +0000418 self.target_sysroot = sysroot_lib.Sysroot(self.sysroot)
Alex Kleine1c4d4b2022-01-05 14:51:04 -0700419 self.portage_dir = os.path.join(self.tempdir, 'portage_logdir')
420 self.PatchObject(
421 sysroot_lib.Sysroot, 'portage_logdir', new=self.portage_dir)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000422 osutils.SafeMakedirs(self.portage_dir)
Michael Mortensen798ee192020-01-17 13:04:43 -0700423 # Set up goma directories.
424 self.goma_dir = os.path.join(self.tempdir, 'goma_dir')
425 osutils.SafeMakedirs(self.goma_dir)
426 self.goma_out_dir = os.path.join(self.tempdir, 'goma_out_dir')
427 osutils.SafeMakedirs(self.goma_out_dir)
Michael Mortensen4ccfb082020-01-22 16:24:03 -0700428 os.environ['GLOG_log_dir'] = self.goma_dir
Alex Kleind4e1e422019-03-18 16:00:41 -0600429
430 def _InputProto(self, build_target=None, sysroot_path=None,
Michael Mortensen798ee192020-01-17 13:04:43 -0700431 build_source=False, goma_dir=None, goma_log_dir=None,
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600432 goma_stats_file=None, goma_counterz_file=None,
Alex Kleinca572ee2020-09-03 10:47:14 -0600433 package_indexes=None, packages=None):
Alex Kleind4e1e422019-03-18 16:00:41 -0600434 """Helper to build an input proto instance."""
435 instance = sysroot_pb2.InstallPackagesRequest()
436
437 if build_target:
438 instance.sysroot.build_target.name = build_target
439 if sysroot_path:
440 instance.sysroot.path = sysroot_path
441 if build_source:
442 instance.flags.build_source = build_source
Michael Mortensen798ee192020-01-17 13:04:43 -0700443 if goma_dir:
444 instance.goma_config.goma_dir = goma_dir
445 if goma_log_dir:
446 instance.goma_config.log_dir.dir = goma_log_dir
447 if goma_stats_file:
448 instance.goma_config.stats_file = goma_stats_file
449 if goma_counterz_file:
450 instance.goma_config.counterz_file = goma_counterz_file
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600451 if package_indexes:
452 instance.package_indexes.extend(package_indexes)
Alex Kleinca572ee2020-09-03 10:47:14 -0600453 if packages:
454 for pkg in packages:
Alex Kleinb397b792021-09-09 15:55:45 -0600455 pkg_info = package_info.parse(pkg)
456 pkg_info_msg = instance.packages.add()
457 controller_util.serialize_package_info(pkg_info, pkg_info_msg)
Alex Kleind4e1e422019-03-18 16:00:41 -0600458 return instance
459
460 def _OutputProto(self):
461 """Helper to build an empty output proto instance."""
462 return sysroot_pb2.InstallPackagesResponse()
463
Michael Mortensen798ee192020-01-17 13:04:43 -0700464 def _CreateGomaLogFile(self, goma_log_dir, name, timestamp):
465 """Creates a log file for testing.
466
467 Args:
468 goma_log_dir (str): Directory where the file will be created.
469 name (str): Log file 'base' name that is combined with the timestamp.
470 timestamp (datetime): timestamp that is written to the file.
471 """
472 path = os.path.join(
473 goma_log_dir,
474 '%s.host.log.INFO.%s' % (name, timestamp.strftime('%Y%m%d-%H%M%S.%f')))
475 osutils.WriteFile(
476 path,
477 timestamp.strftime('Goma log file created at: %Y/%m/%d %H:%M:%S'))
478
Lizzy Presland4c279832021-11-19 20:27:43 +0000479 def _CreatePortageLogFile(self, log_path, pkg_info, timestamp):
Lizzy Presland7e23a612021-11-09 21:49:42 +0000480 """Creates a log file for testing for individual packages built by Portage.
481
482 Args:
Lizzy Presland4c279832021-11-19 20:27:43 +0000483 log_path (pathlike): the PORTAGE_LOGDIR path
Lizzy Presland7e23a612021-11-09 21:49:42 +0000484 pkg_info (PackageInfo): name components for log file.
485 timestamp (datetime): timestamp used to name the file.
486 """
Lizzy Presland4c279832021-11-19 20:27:43 +0000487 path = os.path.join(log_path,
Lizzy Presland77741782021-12-13 19:46:42 +0000488 f'{pkg_info.category}:{pkg_info.pvr}:' \
Lizzy Presland7e23a612021-11-09 21:49:42 +0000489 f'{timestamp.strftime("%Y%m%d-%H%M%S")}.log')
490 osutils.WriteFile(path, f'Test log file for package {pkg_info.category}/'
491 f'{pkg_info.package} written to {path}')
492 return path
493
Alex Klein231d2da2019-07-22 16:44:45 -0600494 def testValidateOnly(self):
495 """Sanity check that a validate only call does not execute any logic."""
496 patch = self.PatchObject(sysroot_service, 'BuildPackages')
497
498 in_proto = self._InputProto(build_target=self.build_target,
499 sysroot_path=self.sysroot)
500 sysroot_controller.InstallPackages(in_proto, self._OutputProto(),
501 self.validate_only_config)
502 patch.assert_not_called()
503
Alex Klein076841b2019-08-29 15:19:39 -0600504 def testMockCall(self):
505 """Sanity check that a mock call does not execute any logic."""
506 patch = self.PatchObject(sysroot_service, 'BuildPackages')
507 request = self._InputProto()
508 response = self._OutputProto()
509
510 rc = sysroot_controller.InstallPackages(request, response,
511 self.mock_call_config)
512
513 patch.assert_not_called()
514 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
515
516 def testMockError(self):
517 """Sanity check that a mock error does not execute any logic."""
518 patch = self.PatchObject(sysroot_service, 'BuildPackages')
519 request = self._InputProto()
520 response = self._OutputProto()
521
522 rc = sysroot_controller.InstallPackages(request, response,
523 self.mock_error_config)
524
525 patch.assert_not_called()
526 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
527 self.assertTrue(response.failed_packages)
528
Alex Kleind4e1e422019-03-18 16:00:41 -0600529 def testArgumentValidationAllMissing(self):
530 """Test missing all arguments."""
531 out_proto = self._OutputProto()
532 in_proto = self._InputProto()
533 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600534 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600535
536 def testArgumentValidationNoSysroot(self):
537 """Test missing sysroot path."""
538 out_proto = self._OutputProto()
539 in_proto = self._InputProto(build_target=self.build_target)
540 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600541 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600542
543 def testArgumentValidationNoBuildTarget(self):
544 """Test missing build target name."""
545 out_proto = self._OutputProto()
546 in_proto = self._InputProto(sysroot_path=self.sysroot)
547 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600548 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600549
550 def testArgumentValidationInvalidSysroot(self):
551 """Test sysroot that hasn't had the toolchain installed."""
552 out_proto = self._OutputProto()
553 in_proto = self._InputProto(build_target=self.build_target,
554 sysroot_path=self.sysroot)
555 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
556 return_value=False)
557 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600558 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600559
Alex Kleinca572ee2020-09-03 10:47:14 -0600560 def testArgumentValidationInvalidPackage(self):
561 out_proto = self._OutputProto()
562 in_proto = self._InputProto(build_target=self.build_target,
563 sysroot_path=self.sysroot,
564 packages=['package-1.0.0-r2'])
565 with self.assertRaises(cros_build_lib.DieSystemExit):
566 sysroot_controller.InstallPackages(in_proto, out_proto, self.api_config)
567
Alex Kleind4e1e422019-03-18 16:00:41 -0600568 def testSuccessOutputHandling(self):
569 """Test successful call output handling."""
570 # Prevent argument validation error.
571 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
572 return_value=True)
573
574 in_proto = self._InputProto(build_target=self.build_target,
575 sysroot_path=self.sysroot)
576 out_proto = self._OutputProto()
577 self.PatchObject(sysroot_service, 'BuildPackages')
578
Alex Klein231d2da2019-07-22 16:44:45 -0600579 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
580 self.api_config)
Alex Kleind4e1e422019-03-18 16:00:41 -0600581 self.assertFalse(rc)
582 self.assertFalse(out_proto.failed_packages)
583
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600584 def testSuccessPackageIndexes(self):
585 """Test successful call with package_indexes."""
586 # Prevent argument validation error.
587 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
588 return_value=True)
589 package_indexes = [
590 common_pb2.PackageIndexInfo(
591 snapshot_sha='SHA', snapshot_number=5,
592 build_target=common_pb2.BuildTarget(name='board'),
593 location='LOCATION', profile=common_pb2.Profile(name='profile')),
594 common_pb2.PackageIndexInfo(
595 snapshot_sha='SHA2', snapshot_number=4,
596 build_target=common_pb2.BuildTarget(name='board'),
597 location='LOCATION2', profile=common_pb2.Profile(name='profile'))]
598
599 in_proto = self._InputProto(build_target=self.build_target,
600 sysroot_path=self.sysroot,
601 package_indexes=package_indexes)
602
603 out_proto = self._OutputProto()
604 rc_patch = self.PatchObject(sysroot_service, 'BuildPackagesRunConfig')
605 self.PatchObject(sysroot_service, 'BuildPackages')
606
607 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
608 self.api_config)
609 self.assertFalse(rc)
Alex Kleineb76da72021-03-19 10:43:09 -0600610 rc_patch.assert_called_with(
Cindy Lin7487daa2022-02-23 04:14:10 +0000611 accept_licenses='@CHROMEOS',
612 upgrade_chroot=False,
613 use_any_chrome=False,
Alex Kleineb76da72021-03-19 10:43:09 -0600614 usepkg=True,
615 install_debug_symbols=True,
616 packages=[],
617 package_indexes=[
618 binpkg.PackageIndexInfo.from_protobuf(x) for x in package_indexes
619 ],
620 use_flags=[],
621 use_goma=False,
Joanna Wang1ec0c812021-11-17 17:41:27 -0800622 use_remoteexec=False,
Alex Kleineb76da72021-03-19 10:43:09 -0600623 incremental_build=False,
Navil Perez5766d1b2021-05-26 17:38:15 +0000624 setup_board=False,
625 dryrun=False)
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600626
Michael Mortensen798ee192020-01-17 13:04:43 -0700627 def testSuccessWithGomaLogs(self):
628 """Test successful call with goma."""
629 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
630 datetime.datetime(2018, 9, 21, 12, 0, 0))
631 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
632 datetime.datetime(2018, 9, 21, 12, 1, 0))
633 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
634 datetime.datetime(2018, 9, 21, 12, 2, 0))
635
636 # Prevent argument validation error.
637 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
638 return_value=True)
639
640 in_proto = self._InputProto(build_target=self.build_target,
641 sysroot_path=self.sysroot,
642 goma_dir=self.goma_dir,
643 goma_log_dir=self.goma_out_dir)
644
645 out_proto = self._OutputProto()
646 self.PatchObject(sysroot_service, 'BuildPackages')
647
648 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
649 self.api_config)
650 self.assertFalse(rc)
651 self.assertFalse(out_proto.failed_packages)
652 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
653 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
654 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
655 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
656
657 def testSuccessWithGomaLogsAndStatsCounterzFiles(self):
658 """Test successful call with goma including stats and counterz files."""
659 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
660 datetime.datetime(2018, 9, 21, 12, 0, 0))
661 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
662 datetime.datetime(2018, 9, 21, 12, 1, 0))
663 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
664 datetime.datetime(2018, 9, 21, 12, 2, 0))
665 # Create stats and counterz files.
666 osutils.WriteFile(os.path.join(self.goma_dir, 'stats.binaryproto'),
667 'File: stats.binaryproto')
668 osutils.WriteFile(os.path.join(self.goma_dir, 'counterz.binaryproto'),
669 'File: counterz.binaryproto')
670
671 # Prevent argument validation error.
672 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
673 return_value=True)
674
675 in_proto = self._InputProto(build_target=self.build_target,
676 sysroot_path=self.sysroot,
677 goma_dir=self.goma_dir,
678 goma_log_dir=self.goma_out_dir,
679 goma_stats_file='stats.binaryproto',
680 goma_counterz_file='counterz.binaryproto')
681
682 out_proto = self._OutputProto()
683 self.PatchObject(sysroot_service, 'BuildPackages')
684
685 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
686 self.api_config)
687 self.assertFalse(rc)
688 self.assertFalse(out_proto.failed_packages)
689 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
690 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
691 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
692 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
693 # Verify that the output dir has 5 files -- since there should be 3 log
694 # files, the stats file, and the counterz file.
695 output_files = os.listdir(self.goma_out_dir)
696 self.assertCountEqual(output_files, [
697 'stats.binaryproto',
698 'counterz.binaryproto',
699 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
700 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
701 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
Michael Mortensen1c7439c2020-01-24 14:43:19 -0700702 self.assertEqual(out_proto.goma_artifacts.counterz_file,
703 'counterz.binaryproto')
704 self.assertEqual(out_proto.goma_artifacts.stats_file,
705 'stats.binaryproto')
Michael Mortensen798ee192020-01-17 13:04:43 -0700706
707 def testFailureMissingGomaStatsCounterzFiles(self):
708 """Test successful call with goma including stats and counterz files."""
709 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
710 datetime.datetime(2018, 9, 21, 12, 0, 0))
711 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
712 datetime.datetime(2018, 9, 21, 12, 1, 0))
713 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
714 datetime.datetime(2018, 9, 21, 12, 2, 0))
715 # Note that stats and counterz files are not created, but are specified in
716 # the proto below.
717
718 # Prevent argument validation error.
719 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
720 return_value=True)
721
722 in_proto = self._InputProto(build_target=self.build_target,
723 sysroot_path=self.sysroot,
724 goma_dir=self.goma_dir,
725 goma_log_dir=self.goma_out_dir,
726 goma_stats_file='stats.binaryproto',
727 goma_counterz_file='counterz.binaryproto')
728
729 out_proto = self._OutputProto()
730 self.PatchObject(sysroot_service, 'BuildPackages')
731
Michael Mortensen1d6d5b02020-01-22 07:33:50 -0700732 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
733 self.api_config)
734 self.assertFalse(rc)
735 self.assertFalse(out_proto.failed_packages)
736 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
737 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
738 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
739 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
740 self.assertFalse(out_proto.goma_artifacts.counterz_file)
741 self.assertFalse(out_proto.goma_artifacts.stats_file)
Michael Mortensen798ee192020-01-17 13:04:43 -0700742
Alex Kleind4e1e422019-03-18 16:00:41 -0600743 def testFailureOutputHandling(self):
744 """Test failed package handling."""
745 # Prevent argument validation error.
746 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
747 return_value=True)
748
749 in_proto = self._InputProto(build_target=self.build_target,
750 sysroot_path=self.sysroot)
751 out_proto = self._OutputProto()
752
753 # Failed package info and expected list for verification.
Lizzy Presland77741782021-12-13 19:46:42 +0000754 err_pkgs = ['cat/pkg-1.0-r3', 'cat2/pkg2-1.0-r1']
Lizzy Presland7e23a612021-11-09 21:49:42 +0000755 err_cpvs = [package_info.parse(cpv) for cpv in err_pkgs]
Alex Kleind4e1e422019-03-18 16:00:41 -0600756 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
757
Lizzy Presland7e23a612021-11-09 21:49:42 +0000758 new_logs = {}
759 for i, pkg in enumerate(err_pkgs):
Lizzy Presland4c279832021-11-19 20:27:43 +0000760 self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000761 datetime.datetime(2021, 6, 9, 13, 37, 0))
Lizzy Presland4c279832021-11-19 20:27:43 +0000762 new_logs[pkg] = self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000763 datetime.datetime(2021, 6, 9,
764 16, 20, 0)
765 )
Alex Kleind4e1e422019-03-18 16:00:41 -0600766 # Force error to be raised with the packages.
767 error = sysroot_lib.PackageInstallError('Error',
768 cros_build_lib.CommandResult(),
769 packages=err_cpvs)
770 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
771
Alex Klein231d2da2019-07-22 16:44:45 -0600772 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
773 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600774 # This needs to return 2 to indicate the available error response.
775 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000776 for data in out_proto.failed_package_data:
777 package = controller_util.deserialize_package_info(data.name)
778 cat_pkg = (data.name.category, data.name.package_name)
779 self.assertIn(cat_pkg, expected)
Lizzy Presland77741782021-12-13 19:46:42 +0000780 self.assertEqual(data.log_path.path, new_logs[package.cpvr])
Lizzy Presland7e23a612021-11-09 21:49:42 +0000781
782 # TODO(b/206514844): remove when field is deleted
Alex Kleind4e1e422019-03-18 16:00:41 -0600783 for package in out_proto.failed_packages:
784 cat_pkg = (package.category, package.package_name)
785 self.assertIn(cat_pkg, expected)
Alex Klein2557b4f2019-07-11 14:34:00 -0600786
787 def testNoPackageFailureOutputHandling(self):
788 """Test failure handling without packages to report."""
789 # Prevent argument validation error.
790 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
791 return_value=True)
792
793 in_proto = self._InputProto(build_target=self.build_target,
794 sysroot_path=self.sysroot)
795 out_proto = self._OutputProto()
796
797 # Force error to be raised with no packages.
798 error = sysroot_lib.PackageInstallError('Error',
799 cros_build_lib.CommandResult(),
800 packages=[])
801 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
802
Alex Klein231d2da2019-07-22 16:44:45 -0600803 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
804 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600805 # All we really care about is it's not 0 or 2 (response available), so
806 # test for that rather than a specific return code.
807 self.assertTrue(rc)
808 self.assertNotEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE,
809 rc)