blob: 2bfcdfdfdc896e9e6130679bdd1bf0b32c1d8e69 [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
Lizzy Preslandfc1db002022-05-06 18:19:49 +00009from typing import Union
Alex Kleinda35fcf2019-03-07 16:01:15 -070010
Alex Klein231d2da2019-07-22 16:44:45 -060011from chromite.api import api_config
Alex Klein8cb365a2019-05-15 16:24:53 -060012from chromite.api import controller
Alex Kleinca572ee2020-09-03 10:47:14 -060013from chromite.api.controller import controller_util
Alex Kleinda35fcf2019-03-07 16:01:15 -070014from chromite.api.controller import sysroot as sysroot_controller
15from chromite.api.gen.chromite.api import sysroot_pb2
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -070016from chromite.api.gen.chromiumos import common_pb2
LaMont Jonesc0343fa2020-08-12 18:58:31 -060017from chromite.lib import binpkg
Alex Kleinda35fcf2019-03-07 16:01:15 -070018from chromite.lib import cros_build_lib
19from chromite.lib import cros_test_lib
20from chromite.lib import osutils
Alex Kleinda35fcf2019-03-07 16:01:15 -070021from chromite.lib import sysroot_lib
Alex Klein18a60af2020-06-11 12:08:47 -060022from chromite.lib.parser import package_info
Alex Kleinda35fcf2019-03-07 16:01:15 -070023from chromite.service import sysroot as sysroot_service
24
25
Alex Klein231d2da2019-07-22 16:44:45 -060026class CreateTest(cros_test_lib.MockTestCase, api_config.ApiConfigMixin):
Alex Kleinda35fcf2019-03-07 16:01:15 -070027 """Create function tests."""
28
29 def _InputProto(self, build_target=None, profile=None, replace=False,
LaMont Jonesc0343fa2020-08-12 18:58:31 -060030 current=False, package_indexes=None):
Alex Kleinda35fcf2019-03-07 16:01:15 -070031 """Helper to build and input proto instance."""
32 proto = sysroot_pb2.SysrootCreateRequest()
33 if build_target:
34 proto.build_target.name = build_target
35 if profile:
36 proto.profile.name = profile
37 if replace:
38 proto.flags.replace = replace
39 if current:
40 proto.flags.chroot_current = current
LaMont Jonesc0343fa2020-08-12 18:58:31 -060041 if package_indexes:
42 proto.package_indexes.extend(package_indexes)
Alex Kleinda35fcf2019-03-07 16:01:15 -070043
44 return proto
45
46 def _OutputProto(self):
47 """Helper to build output proto instance."""
48 return sysroot_pb2.SysrootCreateResponse()
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(sysroot_service, 'Create')
53
54 board = 'board'
55 profile = None
56 force = False
57 upgrade_chroot = True
58 in_proto = self._InputProto(build_target=board, profile=profile,
59 replace=force, current=not upgrade_chroot)
60 sysroot_controller.Create(in_proto, self._OutputProto(),
61 self.validate_only_config)
62 patch.assert_not_called()
63
Alex Klein076841b2019-08-29 15:19:39 -060064 def testMockCall(self):
65 """Sanity check that a mock call does not execute any logic."""
66 patch = self.PatchObject(sysroot_service, 'Create')
67 request = self._InputProto()
68 response = self._OutputProto()
69
70 rc = sysroot_controller.Create(request, response, self.mock_call_config)
71
72 patch.assert_not_called()
73 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
74
75 def testMockError(self):
76 """Sanity check that a mock error does not execute any logic."""
77 patch = self.PatchObject(sysroot_service, 'Create')
78 request = self._InputProto()
79 response = self._OutputProto()
80
81 rc = sysroot_controller.Create(request, response, self.mock_error_config)
82
83 patch.assert_not_called()
84 self.assertEqual(controller.RETURN_CODE_UNRECOVERABLE, rc)
85
Alex Kleinda35fcf2019-03-07 16:01:15 -070086 def testArgumentValidation(self):
87 """Test the input argument validation."""
88 # Error when no name provided.
89 in_proto = self._InputProto()
90 out_proto = self._OutputProto()
91 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -060092 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -070093
94 # Valid when board passed.
95 result = sysroot_lib.Sysroot('/sysroot/path')
96 patch = self.PatchObject(sysroot_service, 'Create', return_value=result)
97 in_proto = self._InputProto('board')
98 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -060099 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700100 patch.assert_called_once()
101
102 def testArgumentHandling(self):
103 """Test the arguments get processed and passed correctly."""
104 sysroot_path = '/sysroot/path'
105
106 sysroot = sysroot_lib.Sysroot(sysroot_path)
107 create_patch = self.PatchObject(sysroot_service, 'Create',
108 return_value=sysroot)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700109 rc_patch = self.PatchObject(sysroot_service, 'SetupBoardRunConfig')
110
111 # Default values.
112 board = 'board'
113 profile = None
114 force = False
115 upgrade_chroot = True
116 in_proto = self._InputProto(build_target=board, profile=profile,
117 replace=force, current=not upgrade_chroot)
118 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -0600119 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700120
121 # Default value checks.
LaMont Jonesfeffd1b2020-08-05 18:24:59 -0600122 rc_patch.assert_called_with(force=force, upgrade_chroot=upgrade_chroot,
123 package_indexes=[])
Alex Kleinda35fcf2019-03-07 16:01:15 -0700124 self.assertEqual(board, out_proto.sysroot.build_target.name)
125 self.assertEqual(sysroot_path, out_proto.sysroot.path)
126
127 # Not default values.
128 create_patch.reset_mock()
129 board = 'board'
130 profile = 'profile'
131 force = True
132 upgrade_chroot = False
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600133 package_indexes = [
134 common_pb2.PackageIndexInfo(
135 snapshot_sha='SHA', snapshot_number=5,
136 build_target=common_pb2.BuildTarget(name=board),
137 location='LOCATION', profile=common_pb2.Profile(name=profile)),
138 common_pb2.PackageIndexInfo(
139 snapshot_sha='SHA2', snapshot_number=4,
140 build_target=common_pb2.BuildTarget(name=board),
141 location='LOCATION2', profile=common_pb2.Profile(name=profile))]
142
Alex Kleinda35fcf2019-03-07 16:01:15 -0700143 in_proto = self._InputProto(build_target=board, profile=profile,
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600144 replace=force, current=not upgrade_chroot,
145 package_indexes=package_indexes)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700146 out_proto = self._OutputProto()
Alex Klein231d2da2019-07-22 16:44:45 -0600147 sysroot_controller.Create(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700148
149 # Not default value checks.
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600150 rc_patch.assert_called_with(
151 force=force, package_indexes=[
152 binpkg.PackageIndexInfo.from_protobuf(x)
153 for x in package_indexes
154 ], upgrade_chroot=upgrade_chroot)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700155 self.assertEqual(board, out_proto.sysroot.build_target.name)
156 self.assertEqual(sysroot_path, out_proto.sysroot.path)
157
158
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700159class GenerateArchiveTest(cros_test_lib.MockTempDirTestCase,
160 api_config.ApiConfigMixin):
161 """GenerateArchive function tests."""
162
163 def setUp(self):
164 self.chroot_path = '/path/to/chroot'
165 self.board = 'board'
166
167 def _InputProto(self, build_target=None, chroot_path=None, pkg_list=None):
168 """Helper to build and input proto instance."""
169 # pkg_list will be a list of category/package strings such as
170 # ['virtual/target-fuzzers'].
171 if pkg_list:
172 package_list = []
173 for pkg in pkg_list:
174 pkg_string_parts = pkg.split('/')
Alex Klein18a60af2020-06-11 12:08:47 -0600175 package_info_msg = common_pb2.PackageInfo(
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700176 category=pkg_string_parts[0],
177 package_name=pkg_string_parts[1])
Alex Klein18a60af2020-06-11 12:08:47 -0600178 package_list.append(package_info_msg)
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700179 else:
180 package_list = []
181
182 return sysroot_pb2.SysrootGenerateArchiveRequest(
183 build_target={'name': build_target},
184 chroot={'path': chroot_path},
185 packages=package_list)
186
187 def _OutputProto(self):
188 """Helper to build output proto instance."""
189 return sysroot_pb2.SysrootGenerateArchiveResponse()
190
191 def testValidateOnly(self):
192 """Sanity check that a validate only call does not execute any logic."""
193 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
194
195 in_proto = self._InputProto(build_target=self.board,
196 chroot_path=self.chroot_path,
197 pkg_list=['virtual/target-fuzzers'])
198 sysroot_controller.GenerateArchive(in_proto, self._OutputProto(),
199 self.validate_only_config)
200 patch.assert_not_called()
201
202 def testMockCall(self):
203 """Sanity check that a mock call does not execute any logic."""
204 patch = self.PatchObject(sysroot_service, 'GenerateArchive')
205
206 in_proto = self._InputProto(build_target=self.board,
207 chroot_path=self.chroot_path,
208 pkg_list=['virtual/target-fuzzers'])
209 sysroot_controller.GenerateArchive(in_proto,
210 self._OutputProto(),
211 self.mock_call_config)
212 patch.assert_not_called()
213
214 def testArgumentValidation(self):
215 """Test the input argument validation."""
216 # Error when no build target provided.
217 in_proto = self._InputProto()
218 out_proto = self._OutputProto()
219 with self.assertRaises(cros_build_lib.DieSystemExit):
220 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
221
222 # Error when packages is not specified.
223 in_proto = self._InputProto(build_target='board',
224 chroot_path=self.chroot_path)
225 with self.assertRaises(cros_build_lib.DieSystemExit):
226 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
227
228 # Valid when board, chroot path, and package are specified.
229 patch = self.PatchObject(sysroot_service, 'GenerateArchive',
230 return_value='/path/to/sysroot/tar.bz')
231 in_proto = self._InputProto(build_target='board',
232 chroot_path=self.chroot_path,
233 pkg_list=['virtual/target-fuzzers'])
234 out_proto = self._OutputProto()
235 sysroot_controller.GenerateArchive(in_proto, out_proto, self.api_config)
236 patch.assert_called_once()
237
238
Alex Klein231d2da2019-07-22 16:44:45 -0600239class InstallToolchainTest(cros_test_lib.MockTempDirTestCase,
240 api_config.ApiConfigMixin):
Alex Kleinda35fcf2019-03-07 16:01:15 -0700241 """Install toolchain function tests."""
242
243 def setUp(self):
244 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600245 # Avoid running the portageq command.
246 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleinda35fcf2019-03-07 16:01:15 -0700247 self.board = 'board'
248 self.sysroot = os.path.join(self.tempdir, 'board')
249 self.invalid_sysroot = os.path.join(self.tempdir, 'invalid', 'sysroot')
250 osutils.SafeMakedirs(self.sysroot)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000251 # Set up portage log directory.
Lizzy Presland4c279832021-11-19 20:27:43 +0000252 self.target_sysroot = sysroot_lib.Sysroot(self.sysroot)
Alex Kleine1c4d4b2022-01-05 14:51:04 -0700253 self.portage_dir = os.path.join(self.tempdir, 'portage_logdir')
254 self.PatchObject(
255 sysroot_lib.Sysroot, 'portage_logdir', new=self.portage_dir)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000256 osutils.SafeMakedirs(self.portage_dir)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700257
258 def _InputProto(self, build_target=None, sysroot_path=None,
259 compile_source=False):
Alex Kleind4e1e422019-03-18 16:00:41 -0600260 """Helper to build an input proto instance."""
Alex Kleinda35fcf2019-03-07 16:01:15 -0700261 proto = sysroot_pb2.InstallToolchainRequest()
262 if build_target:
263 proto.sysroot.build_target.name = build_target
264 if sysroot_path:
265 proto.sysroot.path = sysroot_path
266 if compile_source:
267 proto.flags.compile_source = compile_source
268
269 return proto
270
271 def _OutputProto(self):
272 """Helper to build output proto instance."""
273 return sysroot_pb2.InstallToolchainResponse()
274
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000275 def _CreatePortageLogFile(self, log_path: Union[str, os.PathLike],
276 pkg_info: package_info.PackageInfo,
277 timestamp: datetime.datetime):
Lizzy Presland7e23a612021-11-09 21:49:42 +0000278 """Creates a log file for testing for individual packages built by Portage.
279
280 Args:
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000281 log_path: The PORTAGE_LOGDIR path.
282 pkg_info: Package name used to name the log file.
283 timestamp: Timestamp used to name the file.
Lizzy Presland7e23a612021-11-09 21:49:42 +0000284 """
Lizzy Presland4c279832021-11-19 20:27:43 +0000285 path = os.path.join(log_path,
Lizzy Presland77741782021-12-13 19:46:42 +0000286 f'{pkg_info.category}:{pkg_info.pvr}:' \
Lizzy Presland7e23a612021-11-09 21:49:42 +0000287 f'{timestamp.strftime("%Y%m%d-%H%M%S")}.log')
288 osutils.WriteFile(path,
289 f'Test log file for package {pkg_info.category}/'
290 f'{pkg_info.package} written to {path}')
291 return path
292
Alex Klein231d2da2019-07-22 16:44:45 -0600293 def testValidateOnly(self):
294 """Sanity check that a validate only call does not execute any logic."""
295 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
296
297 in_proto = self._InputProto(build_target=self.board,
298 sysroot_path=self.sysroot)
299 sysroot_controller.InstallToolchain(in_proto, self._OutputProto(),
300 self.validate_only_config)
301 patch.assert_not_called()
302
Alex Klein076841b2019-08-29 15:19:39 -0600303 def testMockCall(self):
304 """Sanity check that a mock call does not execute any logic."""
305 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
306 request = self._InputProto()
307 response = self._OutputProto()
308
309 rc = sysroot_controller.InstallToolchain(request, response,
310 self.mock_call_config)
311
312 patch.assert_not_called()
313 self.assertEqual(controller.RETURN_CODE_SUCCESS, rc)
314
315 def testMockError(self):
316 """Sanity check that a mock error does not execute any logic."""
317 patch = self.PatchObject(sysroot_service, 'InstallToolchain')
318 request = self._InputProto()
319 response = self._OutputProto()
320
321 rc = sysroot_controller.InstallToolchain(request, response,
322 self.mock_error_config)
323
324 patch.assert_not_called()
325 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Lizzy Presland239459a2022-05-05 22:03:19 +0000326 self.assertTrue(response.failed_package_data)
Alex Klein076841b2019-08-29 15:19:39 -0600327
Alex Kleinda35fcf2019-03-07 16:01:15 -0700328 def testArgumentValidation(self):
329 """Test the argument validation."""
330 # Test errors on missing inputs.
331 out_proto = self._OutputProto()
332 # Both missing.
333 in_proto = self._InputProto()
334 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600335 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700336
337 # Sysroot path missing.
338 in_proto = self._InputProto(build_target=self.board)
339 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600340 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700341
342 # Build target name missing.
343 in_proto = self._InputProto(sysroot_path=self.sysroot)
344 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600345 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700346
347 # Both provided, but invalid sysroot path.
348 in_proto = self._InputProto(build_target=self.board,
349 sysroot_path=self.invalid_sysroot)
350 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein231d2da2019-07-22 16:44:45 -0600351 sysroot_controller.InstallToolchain(in_proto, out_proto, self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700352
353 def testSuccessOutputHandling(self):
354 """Test the output is processed and recorded correctly."""
355 self.PatchObject(sysroot_service, 'InstallToolchain')
356 out_proto = self._OutputProto()
357 in_proto = self._InputProto(build_target=self.board,
358 sysroot_path=self.sysroot)
359
Alex Klein231d2da2019-07-22 16:44:45 -0600360 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
361 self.api_config)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700362 self.assertFalse(rc)
Lizzy Presland239459a2022-05-05 22:03:19 +0000363 self.assertFalse(out_proto.failed_package_data)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700364
365
366 def testErrorOutputHandling(self):
367 """Test the error output is processed and recorded correctly."""
368 out_proto = self._OutputProto()
369 in_proto = self._InputProto(build_target=self.board,
370 sysroot_path=self.sysroot)
371
Lizzy Presland77741782021-12-13 19:46:42 +0000372 err_pkgs = ['cat/pkg-1.0-r1', 'cat2/pkg2-1.0-r1']
Alex Kleinea0c89e2021-09-09 15:17:35 -0600373 err_cpvs = [package_info.parse(pkg) for pkg in err_pkgs]
Alex Kleinda35fcf2019-03-07 16:01:15 -0700374 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
Lizzy Presland7e23a612021-11-09 21:49:42 +0000375
376 new_logs = {}
377 for i, pkg in enumerate(err_pkgs):
Lizzy Presland4c279832021-11-19 20:27:43 +0000378 self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000379 datetime.datetime(2021, 6, 9, 13, 37, 0))
Lizzy Presland4c279832021-11-19 20:27:43 +0000380 new_logs[pkg] = self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000381 datetime.datetime(2021, 6, 9,
382 16, 20, 0)
383 )
384
Alex Kleinda35fcf2019-03-07 16:01:15 -0700385 err = sysroot_lib.ToolchainInstallError('Error',
386 cros_build_lib.CommandResult(),
387 tc_info=err_cpvs)
388 self.PatchObject(sysroot_service, 'InstallToolchain', side_effect=err)
389
Alex Klein231d2da2019-07-22 16:44:45 -0600390 rc = sysroot_controller.InstallToolchain(in_proto, out_proto,
391 self.api_config)
Alex Klein8cb365a2019-05-15 16:24:53 -0600392 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000393 self.assertTrue(out_proto.failed_package_data)
394 # This needs to return 2 to indicate the available error response.
395 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
396 for data in out_proto.failed_package_data:
397 package = controller_util.deserialize_package_info(data.name)
398 cat_pkg = (data.name.category, data.name.package_name)
399 self.assertIn(cat_pkg, expected)
Lizzy Presland77741782021-12-13 19:46:42 +0000400 self.assertEqual(data.log_path.path, new_logs[package.cpvr])
Lizzy Presland7e23a612021-11-09 21:49:42 +0000401
Alex Kleind4e1e422019-03-18 16:00:41 -0600402
Alex Klein231d2da2019-07-22 16:44:45 -0600403class InstallPackagesTest(cros_test_lib.MockTempDirTestCase,
404 api_config.ApiConfigMixin):
Alex Kleind4e1e422019-03-18 16:00:41 -0600405 """InstallPackages tests."""
406
407 def setUp(self):
408 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Kleina9d64602019-05-17 14:55:37 -0600409 # Avoid running the portageq command.
410 self.PatchObject(sysroot_controller, '_LogBinhost')
Alex Kleind4e1e422019-03-18 16:00:41 -0600411 self.build_target = 'board'
412 self.sysroot = os.path.join(self.tempdir, 'build', 'board')
413 osutils.SafeMakedirs(self.sysroot)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000414 # Set up portage log directory.
Lizzy Presland4c279832021-11-19 20:27:43 +0000415 self.target_sysroot = sysroot_lib.Sysroot(self.sysroot)
Alex Kleine1c4d4b2022-01-05 14:51:04 -0700416 self.portage_dir = os.path.join(self.tempdir, 'portage_logdir')
417 self.PatchObject(
418 sysroot_lib.Sysroot, 'portage_logdir', new=self.portage_dir)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000419 osutils.SafeMakedirs(self.portage_dir)
Michael Mortensen798ee192020-01-17 13:04:43 -0700420 # Set up goma directories.
421 self.goma_dir = os.path.join(self.tempdir, 'goma_dir')
422 osutils.SafeMakedirs(self.goma_dir)
423 self.goma_out_dir = os.path.join(self.tempdir, 'goma_out_dir')
424 osutils.SafeMakedirs(self.goma_out_dir)
Michael Mortensen4ccfb082020-01-22 16:24:03 -0700425 os.environ['GLOG_log_dir'] = self.goma_dir
Alex Kleind4e1e422019-03-18 16:00:41 -0600426
427 def _InputProto(self, build_target=None, sysroot_path=None,
Michael Mortensen798ee192020-01-17 13:04:43 -0700428 build_source=False, goma_dir=None, goma_log_dir=None,
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600429 goma_stats_file=None, goma_counterz_file=None,
Alex Kleinca572ee2020-09-03 10:47:14 -0600430 package_indexes=None, packages=None):
Alex Kleind4e1e422019-03-18 16:00:41 -0600431 """Helper to build an input proto instance."""
432 instance = sysroot_pb2.InstallPackagesRequest()
433
434 if build_target:
435 instance.sysroot.build_target.name = build_target
436 if sysroot_path:
437 instance.sysroot.path = sysroot_path
438 if build_source:
439 instance.flags.build_source = build_source
Michael Mortensen798ee192020-01-17 13:04:43 -0700440 if goma_dir:
441 instance.goma_config.goma_dir = goma_dir
442 if goma_log_dir:
443 instance.goma_config.log_dir.dir = goma_log_dir
444 if goma_stats_file:
445 instance.goma_config.stats_file = goma_stats_file
446 if goma_counterz_file:
447 instance.goma_config.counterz_file = goma_counterz_file
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600448 if package_indexes:
449 instance.package_indexes.extend(package_indexes)
Alex Kleinca572ee2020-09-03 10:47:14 -0600450 if packages:
451 for pkg in packages:
Alex Kleinb397b792021-09-09 15:55:45 -0600452 pkg_info = package_info.parse(pkg)
453 pkg_info_msg = instance.packages.add()
454 controller_util.serialize_package_info(pkg_info, pkg_info_msg)
Alex Kleind4e1e422019-03-18 16:00:41 -0600455 return instance
456
457 def _OutputProto(self):
458 """Helper to build an empty output proto instance."""
459 return sysroot_pb2.InstallPackagesResponse()
460
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000461 def _CreateGomaLogFile(self, goma_log_dir: Union[str, os.PathLike], name: str,
462 timestamp: datetime.datetime):
Michael Mortensen798ee192020-01-17 13:04:43 -0700463 """Creates a log file for testing.
464
465 Args:
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000466 goma_log_dir: Directory where the file will be created.
467 name: Log file 'base' name that is combined with the timestamp.
468 timestamp: Timestamp that is written to the file.
Michael Mortensen798ee192020-01-17 13:04:43 -0700469 """
470 path = os.path.join(
471 goma_log_dir,
472 '%s.host.log.INFO.%s' % (name, timestamp.strftime('%Y%m%d-%H%M%S.%f')))
473 osutils.WriteFile(
474 path,
475 timestamp.strftime('Goma log file created at: %Y/%m/%d %H:%M:%S'))
476
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000477 def _CreatePortageLogFile(self, log_path: Union[str, os.PathLike],
478 pkg_info: package_info.PackageInfo,
479 timestamp: datetime.datetime):
Lizzy Presland7e23a612021-11-09 21:49:42 +0000480 """Creates a log file for testing for individual packages built by Portage.
481
482 Args:
Lizzy Preslandfc1db002022-05-06 18:19:49 +0000483 log_path: The PORTAGE_LOGDIR path.
484 pkg_info: Package name used to name the log file.
485 timestamp: Timestamp used to name the file.
Lizzy Presland7e23a612021-11-09 21:49:42 +0000486 """
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)
Lizzy Presland239459a2022-05-05 22:03:19 +0000527 self.assertTrue(response.failed_package_data)
Alex Klein076841b2019-08-29 15:19:39 -0600528
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)
Lizzy Presland239459a2022-05-05 22:03:19 +0000582 self.assertFalse(out_proto.failed_package_data)
Alex Kleind4e1e422019-03-18 16:00:41 -0600583
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 use_any_chrome=False,
Alex Kleineb76da72021-03-19 10:43:09 -0600612 usepkg=True,
613 install_debug_symbols=True,
614 packages=[],
615 package_indexes=[
616 binpkg.PackageIndexInfo.from_protobuf(x) for x in package_indexes
617 ],
618 use_flags=[],
619 use_goma=False,
Joanna Wang1ec0c812021-11-17 17:41:27 -0800620 use_remoteexec=False,
Alex Kleineb76da72021-03-19 10:43:09 -0600621 incremental_build=False,
Navil Perez5766d1b2021-05-26 17:38:15 +0000622 dryrun=False)
LaMont Jonesc0343fa2020-08-12 18:58:31 -0600623
Michael Mortensen798ee192020-01-17 13:04:43 -0700624 def testSuccessWithGomaLogs(self):
625 """Test successful call with goma."""
626 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
627 datetime.datetime(2018, 9, 21, 12, 0, 0))
628 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
629 datetime.datetime(2018, 9, 21, 12, 1, 0))
630 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
631 datetime.datetime(2018, 9, 21, 12, 2, 0))
632
633 # Prevent argument validation error.
634 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
635 return_value=True)
636
637 in_proto = self._InputProto(build_target=self.build_target,
638 sysroot_path=self.sysroot,
639 goma_dir=self.goma_dir,
640 goma_log_dir=self.goma_out_dir)
641
642 out_proto = self._OutputProto()
643 self.PatchObject(sysroot_service, 'BuildPackages')
644
645 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
646 self.api_config)
647 self.assertFalse(rc)
Lizzy Presland239459a2022-05-05 22:03:19 +0000648 self.assertFalse(out_proto.failed_package_data)
Michael Mortensen798ee192020-01-17 13:04:43 -0700649 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
650 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
651 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
652 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
653
654 def testSuccessWithGomaLogsAndStatsCounterzFiles(self):
655 """Test successful call with goma including stats and counterz files."""
656 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
657 datetime.datetime(2018, 9, 21, 12, 0, 0))
658 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
659 datetime.datetime(2018, 9, 21, 12, 1, 0))
660 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
661 datetime.datetime(2018, 9, 21, 12, 2, 0))
662 # Create stats and counterz files.
663 osutils.WriteFile(os.path.join(self.goma_dir, 'stats.binaryproto'),
664 'File: stats.binaryproto')
665 osutils.WriteFile(os.path.join(self.goma_dir, 'counterz.binaryproto'),
666 'File: counterz.binaryproto')
667
668 # Prevent argument validation error.
669 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
670 return_value=True)
671
672 in_proto = self._InputProto(build_target=self.build_target,
673 sysroot_path=self.sysroot,
674 goma_dir=self.goma_dir,
675 goma_log_dir=self.goma_out_dir,
676 goma_stats_file='stats.binaryproto',
677 goma_counterz_file='counterz.binaryproto')
678
679 out_proto = self._OutputProto()
680 self.PatchObject(sysroot_service, 'BuildPackages')
681
682 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
683 self.api_config)
684 self.assertFalse(rc)
Lizzy Presland239459a2022-05-05 22:03:19 +0000685 self.assertFalse(out_proto.failed_package_data)
Michael Mortensen798ee192020-01-17 13:04:43 -0700686 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
687 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
688 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
689 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
690 # Verify that the output dir has 5 files -- since there should be 3 log
691 # files, the stats file, and the counterz file.
692 output_files = os.listdir(self.goma_out_dir)
693 self.assertCountEqual(output_files, [
694 'stats.binaryproto',
695 'counterz.binaryproto',
696 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
697 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
698 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
Michael Mortensen1c7439c2020-01-24 14:43:19 -0700699 self.assertEqual(out_proto.goma_artifacts.counterz_file,
700 'counterz.binaryproto')
701 self.assertEqual(out_proto.goma_artifacts.stats_file,
702 'stats.binaryproto')
Michael Mortensen798ee192020-01-17 13:04:43 -0700703
704 def testFailureMissingGomaStatsCounterzFiles(self):
705 """Test successful call with goma including stats and counterz files."""
706 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy',
707 datetime.datetime(2018, 9, 21, 12, 0, 0))
708 self._CreateGomaLogFile(self.goma_dir, 'compiler_proxy-subproc',
709 datetime.datetime(2018, 9, 21, 12, 1, 0))
710 self._CreateGomaLogFile(self.goma_dir, 'gomacc',
711 datetime.datetime(2018, 9, 21, 12, 2, 0))
712 # Note that stats and counterz files are not created, but are specified in
713 # the proto below.
714
715 # Prevent argument validation error.
716 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
717 return_value=True)
718
719 in_proto = self._InputProto(build_target=self.build_target,
720 sysroot_path=self.sysroot,
721 goma_dir=self.goma_dir,
722 goma_log_dir=self.goma_out_dir,
723 goma_stats_file='stats.binaryproto',
724 goma_counterz_file='counterz.binaryproto')
725
726 out_proto = self._OutputProto()
727 self.PatchObject(sysroot_service, 'BuildPackages')
728
Michael Mortensen1d6d5b02020-01-22 07:33:50 -0700729 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
730 self.api_config)
731 self.assertFalse(rc)
Lizzy Presland239459a2022-05-05 22:03:19 +0000732 self.assertFalse(out_proto.failed_package_data)
Michael Mortensen1d6d5b02020-01-22 07:33:50 -0700733 self.assertCountEqual(out_proto.goma_artifacts.log_files, [
734 'compiler_proxy-subproc.host.log.INFO.20180921-120100.000000.gz',
735 'compiler_proxy.host.log.INFO.20180921-120000.000000.gz',
736 'gomacc.host.log.INFO.20180921-120200.000000.tar.gz'])
737 self.assertFalse(out_proto.goma_artifacts.counterz_file)
738 self.assertFalse(out_proto.goma_artifacts.stats_file)
Michael Mortensen798ee192020-01-17 13:04:43 -0700739
Alex Kleind4e1e422019-03-18 16:00:41 -0600740 def testFailureOutputHandling(self):
741 """Test failed package handling."""
742 # Prevent argument validation error.
743 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
744 return_value=True)
745
746 in_proto = self._InputProto(build_target=self.build_target,
747 sysroot_path=self.sysroot)
748 out_proto = self._OutputProto()
749
750 # Failed package info and expected list for verification.
Lizzy Presland77741782021-12-13 19:46:42 +0000751 err_pkgs = ['cat/pkg-1.0-r3', 'cat2/pkg2-1.0-r1']
Lizzy Presland7e23a612021-11-09 21:49:42 +0000752 err_cpvs = [package_info.parse(cpv) for cpv in err_pkgs]
Alex Kleind4e1e422019-03-18 16:00:41 -0600753 expected = [('cat', 'pkg'), ('cat2', 'pkg2')]
754
Lizzy Presland7e23a612021-11-09 21:49:42 +0000755 new_logs = {}
756 for i, pkg in enumerate(err_pkgs):
Lizzy Presland4c279832021-11-19 20:27:43 +0000757 self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000758 datetime.datetime(2021, 6, 9, 13, 37, 0))
Lizzy Presland4c279832021-11-19 20:27:43 +0000759 new_logs[pkg] = self._CreatePortageLogFile(self.portage_dir, err_cpvs[i],
Lizzy Presland7e23a612021-11-09 21:49:42 +0000760 datetime.datetime(2021, 6, 9,
761 16, 20, 0)
762 )
Alex Kleind4e1e422019-03-18 16:00:41 -0600763 # Force error to be raised with the packages.
764 error = sysroot_lib.PackageInstallError('Error',
765 cros_build_lib.CommandResult(),
766 packages=err_cpvs)
767 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
768
Alex Klein231d2da2019-07-22 16:44:45 -0600769 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
770 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600771 # This needs to return 2 to indicate the available error response.
772 self.assertEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE, rc)
Lizzy Presland7e23a612021-11-09 21:49:42 +0000773 for data in out_proto.failed_package_data:
774 package = controller_util.deserialize_package_info(data.name)
775 cat_pkg = (data.name.category, data.name.package_name)
776 self.assertIn(cat_pkg, expected)
Lizzy Presland77741782021-12-13 19:46:42 +0000777 self.assertEqual(data.log_path.path, new_logs[package.cpvr])
Lizzy Presland7e23a612021-11-09 21:49:42 +0000778
Alex Klein2557b4f2019-07-11 14:34:00 -0600779 def testNoPackageFailureOutputHandling(self):
780 """Test failure handling without packages to report."""
781 # Prevent argument validation error.
782 self.PatchObject(sysroot_lib.Sysroot, 'IsToolchainInstalled',
783 return_value=True)
784
785 in_proto = self._InputProto(build_target=self.build_target,
786 sysroot_path=self.sysroot)
787 out_proto = self._OutputProto()
788
789 # Force error to be raised with no packages.
790 error = sysroot_lib.PackageInstallError('Error',
791 cros_build_lib.CommandResult(),
792 packages=[])
793 self.PatchObject(sysroot_service, 'BuildPackages', side_effect=error)
794
Alex Klein231d2da2019-07-22 16:44:45 -0600795 rc = sysroot_controller.InstallPackages(in_proto, out_proto,
796 self.api_config)
Alex Klein2557b4f2019-07-11 14:34:00 -0600797 # All we really care about is it's not 0 or 2 (response available), so
798 # test for that rather than a specific return code.
799 self.assertTrue(rc)
800 self.assertNotEqual(controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE,
801 rc)