portage_testables: Let test Packages define arbitrary variables
Allow users of the Package class to specify arbitrary keys and string
values as **kwargs that will be written out to the ebuild on disk in
the synthetic test overlay. The intended use for this is to be able to
specify CROS_WORKON_* style variables to exercise those code paths in
tests.
BUG=chromium:1139412, chromium:1071391, chromium:1078251
TEST=`run_pytest`
Change-Id: I58d10290ab8628ea22973d5a6ddad38cb8a062e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2481001
Tested-by: Chris McDonald <cjmcdonald@chromium.org>
Reviewed-by: Michael Mortensen <mmortensen@google.com>
Commit-Queue: Chris McDonald <cjmcdonald@chromium.org>
diff --git a/test/portage_testables.py b/test/portage_testables.py
index a2c0a5b..7bd1789 100644
--- a/test/portage_testables.py
+++ b/test/portage_testables.py
@@ -11,6 +11,8 @@
import itertools
import os
import pathlib # pylint: disable=import-error
+import shlex
+from typing import Dict, Iterable, Tuple, Union
from chromite.lib import constants
from chromite.lib import cros_build_lib
@@ -35,8 +37,8 @@
def _dict_to_ebuild(dictionary):
"""Helper to format a dictionary into an ebuild file."""
output = []
- for key in sorted(dictionary.keys()):
- output.append('%s="%s"' % (key, dictionary[key]))
+ for key in dictionary.keys():
+ output.append(f'{key}={shlex.quote(dictionary[key])}')
output.append('\n')
return '\n'.join(output)
@@ -58,11 +60,16 @@
self._write_layout_conf()
- def __contains__(self, item: package_info.CPV):
- if not isinstance(item, package_info.CPV):
+ def __contains__(self, item: Union[package_info.CPV,
+ package_info.PackageInfo]):
+ if not isinstance(item, (package_info.CPV, package_info.PackageInfo)):
raise TypeError(f'Expected a CPV but received a {type(item)}')
- ebuild_path = self.path / item.category / item.package / f'{item.pv}.ebuild'
+ if isinstance(item, package_info.CPV):
+ ebuild_path = (
+ self.path / item.category / item.package / f'{item.pv}.ebuild')
+ else:
+ ebuild_path = self.path / item.relative_path
return ebuild_path.is_file()
@@ -71,7 +78,7 @@
layout_conf_path = self.path / 'metadata' / 'layout.conf'
master_names = ' '.join(m.name for m in self.masters or [])
conf = {
- 'masters': master_names,
+ 'masters': 'portage-stable chromiumos eclass-overlay' + master_names,
'profile-formats': 'portage-2 profile-default-eapi',
'profile_eapi_when_unspecified': '5-progress',
'repo-name': str(self.name),
@@ -97,7 +104,7 @@
mode='a',
makedirs=True)
- def _write_ebuild(self, pkg):
+ def _write_ebuild(self, pkg: 'Package'):
"""Write a Package object out to an ebuild file in this Overlay."""
ebuild_path = self.path / pkg.category / pkg.package / (
pkg.package + '-' + pkg.version + '.ebuild')
@@ -112,6 +119,14 @@
osutils.WriteFile(ebuild_path, _dict_to_ebuild(base_conf), makedirs=True)
+ # Write additional miscellaneous variables declared in the Package object.
+ for k, v in pkg.variables.items():
+ osutils.WriteFile(ebuild_path, f'{k}="{v}"\n', mode='a')
+
+ # Write an eclass inheritance line, if needed.
+ if pkg.format_eclass_line():
+ osutils.WriteFile(ebuild_path, pkg.format_eclass_line(), mode='a')
+
extra_conf = {
'DEPEND': pkg.depend,
'RDEPEND': pkg.rdepend,
@@ -235,8 +250,7 @@
extra_env.update(kwargs.pop('extra_env', {}))
kwargs.setdefault('encoding', 'utf-8')
- return cros_build_lib.run(
- cmd, extra_env=extra_env, **kwargs)
+ return cros_build_lib.run(cmd, extra_env=extra_env, **kwargs)
class Profile(object):
@@ -253,6 +267,9 @@
class Package(object):
"""Portage package, lives in an overlay."""
+ inherit: Tuple[str]
+ variables: Dict[str, str]
+
def __init__(self,
category,
package,
@@ -261,7 +278,9 @@
keywords='*',
slot='0',
depend='',
- rdepend=''):
+ rdepend='',
+ inherit: Union[Iterable[str], str] = tuple(),
+ **kwargs):
self.category = category
self.package = package
self.version = version
@@ -270,6 +289,8 @@
self.slot = slot
self.depend = depend
self.rdepend = rdepend
+ self.inherit = (inherit,) if isinstance(inherit, str) else tuple(inherit)
+ self.variables = kwargs
@classmethod
def from_cpv(cls, pkg_str: str):
@@ -282,3 +303,12 @@
"""Returns a CPV object constructed from this package's metadata."""
return package_info.SplitCPV(self.category + '/' + self.package + '-' +
self.version)
+
+ def format_eclass_line(self) -> str:
+ """Returns a string containing this package's eclass inheritance line."""
+ if self.inherit and isinstance(self.inherit, str):
+ return f'inherit {self.inherit}\n'
+ elif self.inherit:
+ return f'inherit {" ".join(self.inherit)}\n'
+ else:
+ return ''