blob: 1b2db70ca7e468784f5c72e2806620b8feeaa8e4 [file] [log] [blame]
# Copyright 2012 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for portage_util.py."""
import json
import os
import chromite as cr
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import failures_lib
from chromite.lib import git
from chromite.lib import osutils
from chromite.lib import partial_mock
from chromite.lib import portage_util
from chromite.lib.parser import package_info
MANIFEST = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
# pylint: disable=protected-access
class _Package:
"""Package helper class."""
def __init__(self, package):
self.package = package
class EBuildTest(cros_test_lib.MockTempDirTestCase):
"""Ebuild related tests."""
_MULTILINE_WITH_TEST = """
hello
src_test() {
}"""
_MULTILINE_NO_TEST = """
hello
src_compile() {
}"""
_MULTILINE_COMMENTED = """
#src_test() {
# notactive
# }"""
_MULTILINE_PLATFORM = """
platform_pkg_test() {
}"""
_AUTOTEST_NORMAL = (
"\n\t+tests_fake_Test1\n\t+tests_fake_Test2\n",
(
"fake_Test1",
"fake_Test2",
),
)
_AUTOTEST_EXTRA_PLUS = ("\n\t++++tests_fake_Test1\n", ("fake_Test1",))
_AUTOTEST_EXTRA_TAB = (
"\t\t\n\t\n\t+tests_fake_Test1\tfoo\n",
("fake_Test1",),
)
_SINGLE_LINE_TEST = 'src_test() { echo "foo" }'
_INHERIT_CROS_GO = "inherit cros-workon cros-go"
_INHERIT_TAST_BUNDLE = "inherit tast-bundle"
_INHERIT_CROS_DEBUG = "inherit cros-debug"
_EBUILD_BASE = """
CROS_WORKON_COMMIT=commit1
CROS_WORKON_TREE=("tree1" "tree2")
inherit cros-workon
"""
_EBUILD_DIFFERENT_COMMIT = """
CROS_WORKON_COMMIT=commit9
CROS_WORKON_TREE=("tree1" "tree2")
inherit cros-workon
"""
_EBUILD_DIFFERENT_TREE = """
CROS_WORKON_COMMIT=commit1
CROS_WORKON_TREE=("tree9" "tree2")
inherit cros-workon
"""
_EBUILD_DIFFERENT_CONTENT = """
CROS_WORKON_COMMIT=commit1
CROS_WORKON_TREE=("tree1" "tree2")
inherit cros-workon superpower
"""
def _MakeFakeEbuild(self, fake_ebuild_path, fake_ebuild_content=""):
osutils.WriteFile(fake_ebuild_path, fake_ebuild_content, makedirs=True)
fake_ebuild = portage_util.EBuild(fake_ebuild_path, False)
return fake_ebuild
def testParseEBuildPath(self):
"""Test with ebuild with revision number."""
basedir = self.tempdir / "cat" / "test_package"
fake_ebuild_path = basedir / "test_package-0.0.1-r1.ebuild"
fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path)
self.assertEqual(fake_ebuild.category, "cat")
self.assertEqual(fake_ebuild.pkgname, "test_package")
self.assertEqual(fake_ebuild.version_no_rev, "0.0.1")
self.assertEqual(fake_ebuild.current_revision, 1)
self.assertEqual(fake_ebuild.version, "0.0.1-r1")
self.assertEqual(fake_ebuild.package, "cat/test_package")
self.assertEqual(
fake_ebuild._ebuild_path_no_version,
os.path.join(basedir, "test_package"),
)
self.assertEqual(
fake_ebuild.ebuild_path_no_revision,
os.path.join(basedir, "test_package-0.0.1"),
)
self.assertEqual(
fake_ebuild._unstable_ebuild_path,
os.path.join(basedir, "test_package-9999.ebuild"),
)
self.assertEqual(fake_ebuild.ebuild_path, fake_ebuild_path)
def testParseEBuildPathNoRevisionNumber(self):
"""Test with ebuild without revision number."""
basedir = self.tempdir / "cat" / "test_package"
fake_ebuild_path = basedir / "test_package-9999.ebuild"
fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path)
self.assertEqual(fake_ebuild.category, "cat")
self.assertEqual(fake_ebuild.pkgname, "test_package")
self.assertEqual(fake_ebuild.version_no_rev, "9999")
self.assertEqual(fake_ebuild.current_revision, 0)
self.assertEqual(fake_ebuild.version, "9999")
self.assertEqual(fake_ebuild.package, "cat/test_package")
self.assertEqual(
fake_ebuild._ebuild_path_no_version,
os.path.join(basedir, "test_package"),
)
self.assertEqual(
fake_ebuild.ebuild_path_no_revision,
os.path.join(basedir, "test_package-9999"),
)
self.assertEqual(
fake_ebuild._unstable_ebuild_path,
os.path.join(basedir, "test_package-9999.ebuild"),
)
self.assertEqual(fake_ebuild.ebuild_path, fake_ebuild_path)
def testGetCommitId(self):
fake_hash = "24ab3c9f6d6b5c744382dba2ca8fb444b9808e9f"
basedir = os.path.join(self.tempdir, "cat", "test_package")
fake_ebuild_path = os.path.join(basedir, "test_package-9999.ebuild")
fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path)
# git rev-parse HEAD
self.PatchObject(
git,
"RunGit",
return_value=cros_build_lib.CompletedProcess(
stdout=fake_hash + "\n"
),
)
test_hash = fake_ebuild.GetCommitId(self.tempdir)
self.assertEqual(test_hash, fake_hash)
def testEBuildStable(self):
"""Test ebuild w/keyword variations"""
basedir = os.path.join(self.tempdir, "cat", "test_package")
fake_ebuild_path = os.path.join(basedir, "test_package-9999.ebuild")
datasets = (
("~amd64", False),
("amd64", True),
("~amd64 ~arm ~x86", False),
("~amd64 arm ~x86", True),
("-* ~arm", False),
("-* x86", True),
)
for keywords, stable in datasets:
fake_ebuild = self._MakeFakeEbuild(
fake_ebuild_path,
fake_ebuild_content=['KEYWORDS="%s"\n' % keywords],
)
self.assertEqual(fake_ebuild.is_stable, stable)
def testEBuildManuallyUpreved(self):
"""Test manually uprevved ebuild"""
basedir = os.path.join(self.tempdir, "cat", "test_package")
fake_ebuild_path = os.path.join(basedir, "test_package-9999.ebuild")
fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path)
self.assertEqual(fake_ebuild.is_manually_uprevved, False)
PATTERNS = (
"CROS_WORKON_BLACKLIST=1",
'CROS_WORKON_BLACKLIST="1"',
"CROS_WORKON_BLACKLIST='1'",
"CROS_WORKON_MANUAL_UPREV=1",
'CROS_WORKON_MANUAL_UPREV="1"',
"CROS_WORKON_MANUAL_UPREV='1'",
)
for pattern in PATTERNS:
fake_ebuild = self._MakeFakeEbuild(
fake_ebuild_path, fake_ebuild_content=[pattern + "\n"]
)
self.assertTrue(fake_ebuild.is_manually_uprevved, msg=pattern)
def testEBuildAutoUprev(self):
"""Test auto uprev ebuild"""
basedir = os.path.join(self.tempdir, "cat", "test_package")
fake_ebuild_path = os.path.join(basedir, "test_package-9999.ebuild")
fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path)
self.assertEqual(fake_ebuild.is_manually_uprevved, False)
PATTERNS = (
"CROS_WORKON_MANUAL_UPREV=",
"CROS_WORKON_MANUAL_UPREV=0",
)
for pattern in PATTERNS:
fake_ebuild = self._MakeFakeEbuild(
fake_ebuild_path, fake_ebuild_content=[pattern + "\n"]
)
self.assertFalse(fake_ebuild.is_manually_uprevved, msg=pattern)
def testHasTest(self):
"""Tests that we detect test stanzas correctly."""
def run_case(content, expected):
with osutils.TempDir() as temp:
ebuild = os.path.join(
temp, "overlay", "app-misc", "foo-0.0.1-r1.ebuild"
)
osutils.WriteFile(ebuild, content, makedirs=True)
self.assertEqual(
expected, portage_util.EBuild(ebuild, False).has_test
)
run_case(self._MULTILINE_WITH_TEST, True)
run_case(self._MULTILINE_NO_TEST, False)
run_case(self._MULTILINE_COMMENTED, False)
run_case(self._MULTILINE_PLATFORM, True)
run_case(self._SINGLE_LINE_TEST, True)
run_case(self._INHERIT_CROS_GO, True)
run_case(self._INHERIT_TAST_BUNDLE, True)
run_case(self._INHERIT_CROS_DEBUG, False)
def testCheckHasTestWithoutEbuild(self):
"""Test CheckHasTest on a package without ebuild config file"""
package_name = "chromeos-base/temp_mypackage"
package_path = os.path.join(self.tempdir, package_name)
os.makedirs(package_path)
with self.assertRaises(failures_lib.PackageBuildFailure):
portage_util._CheckHasTest(package_name, self.tempdir)
def testEBuildGetAutotestTests(self):
"""Test extraction of test names from IUSE_TESTS variable.
Used for autotest ebuilds.
"""
def run_case(tests_str, results):
settings = {"IUSE_TESTS": tests_str}
self.assertEqual(
portage_util.EBuild._GetAutotestTestsFromSettings(settings),
results,
)
run_case(self._AUTOTEST_NORMAL[0], list(self._AUTOTEST_NORMAL[1]))
run_case(
self._AUTOTEST_EXTRA_PLUS[0], list(self._AUTOTEST_EXTRA_PLUS[1])
)
run_case(self._AUTOTEST_EXTRA_TAB[0], list(self._AUTOTEST_EXTRA_TAB[1]))
def testAlmostSameEBuilds(self):
"""Test _AlmostSameEBuilds()."""
def AlmostSameEBuilds(ebuild1_contents, ebuild2_contents):
ebuild1_path = os.path.join(self.tempdir, "a.ebuild")
ebuild2_path = os.path.join(self.tempdir, "b.ebuild")
osutils.WriteFile(ebuild1_path, ebuild1_contents)
osutils.WriteFile(ebuild2_path, ebuild2_contents)
return portage_util.EBuild._AlmostSameEBuilds(
ebuild1_path, ebuild2_path
)
self.assertTrue(AlmostSameEBuilds(self._EBUILD_BASE, self._EBUILD_BASE))
self.assertTrue(
AlmostSameEBuilds(self._EBUILD_BASE, self._EBUILD_DIFFERENT_COMMIT)
)
self.assertFalse(
AlmostSameEBuilds(self._EBUILD_BASE, self._EBUILD_DIFFERENT_TREE)
)
self.assertFalse(
AlmostSameEBuilds(self._EBUILD_BASE, self._EBUILD_DIFFERENT_CONTENT)
)
def testClassifySimple(self):
"""Test Classify on a simple ebuild."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
osutils.WriteFile(ebuild_path, "")
attrs = portage_util.EBuild.Classify(ebuild_path)
self.assertFalse(attrs.is_workon)
self.assertFalse(attrs.is_stable)
self.assertFalse(attrs.is_manually_uprevved)
self.assertFalse(attrs.has_test)
def testClassifyUnstable(self):
"""Test Classify handling of non-stable KEYWORDS."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
TESTS = (
"KEYWORDS=",
"KEYWORDS= # Yep.",
'KEYWORDS="-*"',
'KEYWORDS="-* ~arm"',
'KEYWORDS="~*"',
)
for keywords in TESTS:
osutils.WriteFile(ebuild_path, keywords)
attrs = portage_util.EBuild.Classify(ebuild_path)
self.assertFalse(attrs.is_stable, msg="Failing: %s" % (keywords,))
def testClassifyStable(self):
"""Test Classify handling of stable KEYWORDS."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
TESTS = (
'KEYWORDS="*"',
'KEYWORDS="*" # Yep.',
'KEYWORDS="-* arm"',
)
for keywords in TESTS:
osutils.WriteFile(ebuild_path, keywords)
attrs = portage_util.EBuild.Classify(ebuild_path)
self.assertTrue(attrs.is_stable, msg="Failing: %s" % (keywords,))
def testClassifyEncodingASCII(self):
"""Test Classify with ASCII file encodings."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
# Generate a valid shell script with all possible ASCII values.
osutils.WriteFile(
ebuild_path,
"cat <<\\EOF\n%s\nEOF\n"
% ("".join(chr(x) for x in range(0, 128)),),
)
# Just check that we don't throw an exception.
portage_util.EBuild.Classify(ebuild_path)
def testClassifyEncodingUTF8(self):
"""Test Classify with UTF-8 file encodings."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
osutils.WriteFile(ebuild_path, "# FöÖßbäłł")
# Just check that we don't throw an exception.
portage_util.EBuild.Classify(ebuild_path)
def testClassifyEncodingLatin1(self):
"""Test Classify with ISO 8859-1 file encodings."""
ebuild_path = os.path.join(self.tempdir, "foo-1.ebuild")
osutils.WriteFile(ebuild_path, b"# This is \xa0 bad UTF-8", mode="wb")
with self.assertRaises(UnicodeDecodeError):
portage_util.EBuild.Classify(ebuild_path)
class ProjectAndPathTest(cros_test_lib.MockTempDirTestCase):
"""Project and Path related tests."""
def _MockParseWorkonVariables(
self,
fake_projects,
fake_srcpaths,
fake_localnames,
fake_ebuild_contents,
):
"""Mock the necessary calls, call GetSourceInfo()."""
def _isdir(path):
"""Mock function for os.path.isdir"""
if any(fake_srcpaths):
if path == os.path.join(self.tempdir, "src"):
return True
for srcpath in fake_srcpaths:
if srcpath:
if path == os.path.join(self.tempdir, "src", srcpath):
return True
else:
for localname in fake_localnames:
if path == os.path.join(self.tempdir, localname):
return False
elif path == os.path.join(
self.tempdir, "platform", localname
):
return True
raise Exception("unhandled path: %s" % path)
def _FindCheckoutFromPath(
path, strict=True
): # pylint: disable=unused-argument
"""Mock function for manifest.FindCheckoutFromPath"""
for project, localname in zip(fake_projects, fake_localnames):
if path == os.path.join(self.tempdir, "platform", localname):
return {"name": project, "local_path": localname}
return {}
self.PatchObject(os.path, "isdir", side_effect=_isdir)
self.PatchObject(
MANIFEST, "FindCheckoutFromPath", side_effect=_FindCheckoutFromPath
)
if not fake_srcpaths:
fake_srcpaths = [""] * len(fake_projects)
if not fake_projects:
fake_projects = [""] * len(fake_srcpaths)
# We need 'chromeos-base' here because it controls default _SUBDIR
# values.
ebuild_path = os.path.join(
self.tempdir,
"packages",
"chromeos-base",
"package",
"package-9999.ebuild",
)
osutils.WriteFile(ebuild_path, fake_ebuild_contents, makedirs=True)
ebuild = portage_util.EBuild(ebuild_path, False)
return ebuild.GetSourceInfo(self.tempdir, MANIFEST)
def testParseLegacyWorkonVariables(self):
"""Tests if ebuilds in a single item format are correctly parsed."""
fake_project = "my_project1"
fake_localname = "foo"
fake_ebuild_contents = """
CROS_WORKON_PROJECT=%s
CROS_WORKON_LOCALNAME=%s
""" % (
fake_project,
fake_localname,
)
info = self._MockParseWorkonVariables(
[fake_project], [], [fake_localname], fake_ebuild_contents
)
self.assertEqual(info.projects, [fake_project])
self.assertEqual(
info.srcdirs,
[os.path.join(self.tempdir, "platform", fake_localname)],
)
self.assertEqual(
info.subtrees,
[os.path.join(self.tempdir, "platform", fake_localname)],
)
def testParseAlwaysLive(self):
"""Tests if an ebuild which is always live is correctly handled."""
fake_project = "my_project1"
fake_localname = "foo"
fake_ebuild_contents = """
CROS_WORKON_PROJECT=%s
CROS_WORKON_LOCALNAME=%s
CROS_WORKON_ALWAYS_LIVE=1
""" % (
fake_project,
fake_localname,
)
info = self._MockParseWorkonVariables(
[fake_project], [], [fake_localname], fake_ebuild_contents
)
self.assertEqual(info.projects, [])
self.assertEqual(info.srcdirs, [])
self.assertEqual(info.srcdirs, [])
self.assertEqual(info.subtrees, [])
def testParseArrayWorkonVariables(self):
"""Tests if ebuilds in an array format are correctly parsed."""
fake_projects = ["my_project1", "my_project2", "my_project3"]
fake_localnames = ["foo", "bar", "bas"]
# The test content is formatted using the same function that
# formats ebuild output, ensuring that we can parse our own
# products.
fake_ebuild_contents = """
CROS_WORKON_PROJECT=%s
CROS_WORKON_LOCALNAME=%s
""" % (
portage_util.EBuild.FormatBashArray(fake_projects),
portage_util.EBuild.FormatBashArray(fake_localnames),
)
info = self._MockParseWorkonVariables(
fake_projects, [], fake_localnames, fake_ebuild_contents
)
self.assertEqual(info.projects, fake_projects)
fake_paths = [
os.path.realpath(os.path.join(self.tempdir, "platform", x))
for x in fake_localnames
]
self.assertEqual(info.srcdirs, fake_paths)
self.assertEqual(info.subtrees, fake_paths)
def testParseArrayWorkonVariablesWithSrcpaths(self):
"""Tests if ebuilds with CROS_WORKON_SRCPATH are handled correctly."""
fake_projects = ["my_project1", "", ""]
fake_srcpaths = ["", "path/to/src", "path/to/other/src"]
fake_localnames = ["foo", "bar", "bas"]
# The test content is formatted using the same function that
# formats ebuild output, ensuring that we can parse our own
# products.
fake_ebuild_contents = """
CROS_WORKON_PROJECT=%s
CROS_WORKON_SRCPATH=%s
CROS_WORKON_LOCALNAME=%s
""" % (
portage_util.EBuild.FormatBashArray(fake_projects),
portage_util.EBuild.FormatBashArray(fake_srcpaths),
portage_util.EBuild.FormatBashArray(fake_localnames),
)
info = self._MockParseWorkonVariables(
fake_projects, fake_srcpaths, fake_localnames, fake_ebuild_contents
)
self.assertEqual(info.projects, fake_projects)
fake_paths = []
for srcpath, localname in zip(fake_srcpaths, fake_localnames):
if srcpath:
path = os.path.realpath(
os.path.join(self.tempdir, "src", srcpath)
)
else:
path = os.path.realpath(
os.path.join(self.tempdir, "platform", localname)
)
fake_paths.append(path)
self.assertEqual(info.srcdirs, fake_paths)
self.assertEqual(info.subtrees, fake_paths)
def testParseArrayWorkonVariablesWithSubtrees(self):
"""Tests if ebuilds with CROS_WORKON_SUBTREE are handled correctly."""
fake_project = "my_project1"
fake_localname = "foo/bar"
fake_subtrees = "test baz/quz"
# The test content is formatted using the same function that
# formats ebuild output, ensuring that we can parse our own
# products.
fake_ebuild_contents = """
CROS_WORKON_PROJECT=%s
CROS_WORKON_LOCALNAME=%s
CROS_WORKON_SUBTREE="%s"
""" % (
fake_project,
fake_localname,
fake_subtrees,
)
info = self._MockParseWorkonVariables(
[fake_project], [], [fake_localname], fake_ebuild_contents
)
self.assertEqual(info.projects, [fake_project])
self.assertEqual(
info.srcdirs,
[os.path.join(self.tempdir, "platform", fake_localname)],
)
self.assertEqual(
info.subtrees,
[
os.path.join(self.tempdir, "platform", "foo/bar/test"),
os.path.join(self.tempdir, "platform", "foo/bar/baz/quz"),
],
)
class StubEBuild(portage_util.EBuild):
"""Test helper to StubEBuild."""
def __init__(self, path, subdir_support):
super().__init__(path, subdir_support)
self.is_workon = True
self.is_stable = True
def _ReadEBuild(self, path):
pass
def GetCommitId(self, srcdir, ref: str = "HEAD"):
id_map = {"p1_path1": "my_id1", "p1_path2": "my_id2"}
if srcdir in id_map:
return id_map[srcdir]
else:
return "you_lose"
class EBuildRevWorkonTest(cros_test_lib.MockTempDirTestCase):
"""Tests for EBuildRevWorkon."""
# Lines that we will feed as fake ebuild contents to
# EBuild.MarAsStable(). This is the minimum content needed
# to test the various branches in the function's main processing
# loop.
_mock_ebuild = [
"EAPI=2\n",
"CROS_WORKON_COMMIT=old_id\n",
"CROS_WORKON_PROJECT=test_package\n",
'KEYWORDS="~x86 ~arm ~amd64"\n',
"src_unpack(){}\n",
]
_mock_ebuild_multi = [
"EAPI=2\n",
'CROS_WORKON_COMMIT=("old_id1","old_id2")\n',
'KEYWORDS="~x86 ~arm ~amd64"\n',
"src_unpack(){}\n",
]
_mock_ebuild_subdir = [
"EAPI=5\n",
"CROS_WORKON_COMMIT=old_id\n",
"CROS_WORKON_PROJECT=test_package\n",
'CROS_WORKON_SUBDIRS_TO_REV=( foo )\nKEYWORDS="~x86 ~arm ~amd64"\n',
"src_unpack(){}\n",
]
_revved_ebuild = (
"EAPI=2\n"
'CROS_WORKON_COMMIT="my_id1"\n'
'CROS_WORKON_TREE=("treehash1a" "treehash1b")\n'
"CROS_WORKON_PROJECT=test_package\n"
'KEYWORDS="x86 arm amd64"\n'
"src_unpack(){}\n"
)
_revved_ebuild_multi = (
"EAPI=2\n"
'CROS_WORKON_COMMIT=("my_id1" "my_id2")\n'
'CROS_WORKON_TREE=("treehash1a" "treehash1b" "treehash2")\n'
'KEYWORDS="x86 arm amd64"\n'
"src_unpack(){}\n"
)
_revved_ebuild_subdir = (
"EAPI=5\n"
'CROS_WORKON_COMMIT="my_id1"\n'
'CROS_WORKON_TREE=("treehash1a" "treehash1b")\n'
"CROS_WORKON_PROJECT=test_package\n"
"CROS_WORKON_SUBDIRS_TO_REV=( foo )\n"
'KEYWORDS="x86 arm amd64"\n'
"src_unpack(){}\n"
)
unstable_ebuild_changed = False
def setUp(self):
self.overlay = self.tempdir / "overlay"
package_name_no_version = os.path.join(
self.overlay, "category/test_package/test_package"
)
package_name_version = package_name_no_version + "-0.0.1"
package_name_forced_version = package_name_no_version + "-777.0.0"
ebuild_path = package_name_version + "-r1.ebuild"
self.m_ebuild = StubEBuild(ebuild_path, False)
self.revved_ebuild_path = package_name_version + "-r2.ebuild"
self.revved_ebuild_path_forced_version = (
package_name_forced_version + "-r1.ebuild"
)
self.git_files_changed = []
def createRevWorkOnMocks(self, ebuild_content, rev, multi=False):
"""Creates a mock environment to run RevWorkOnEBuild.
Args:
ebuild_content: The content of the ebuild that will be revved.
rev: Tell _RunGit whether this is attempt an attempt to rev an
ebuild.
multi: Whether there are multiple projects to uprev.
"""
def _GetTreeId(path):
"""Mock function for portage_util.EBuild.GetTreeId"""
return {
"p1_path1/a": "treehash1a",
"p1_path1/b": "treehash1b",
"p1_path2": "treehash2",
}.get(path)
def _RunGit(cwd, cmd):
"""Mock function for portage_util.EBuild._RunGit"""
if cmd[0] == "log":
# special case for git log in the overlay:
# report if -9999.ebuild supposedly changed
if cwd == self.overlay and self.unstable_ebuild_changed:
return "someebuild-9999.ebuild"
file_list = cmd[cmd.index("--") + 1 :]
# Just get the last path component, so we can specify the
# file_list without concerning ourselves with tempdir
file_list = [os.path.split(f)[1] for f in file_list]
# Return a stub file if we have changes in any of the listed
# files.
if set(self.git_files_changed).intersection(file_list):
return "somefile"
return ""
self.assertEqual(cwd, self.overlay)
self.assertTrue(rev, msg="git should not be run when not revving")
if cmd[0] == "add":
self.assertEqual(cmd, ["add", self.revved_ebuild_path])
else:
self.assertTrue(self.m_ebuild.is_stable)
self.assertEqual(cmd, ["rm", "-f", self.m_ebuild.ebuild_path])
source_mock = self.PatchObject(portage_util.EBuild, "GetSourceInfo")
if multi:
source_mock.return_value = portage_util.SourceInfo(
projects=["fake_project1", "fake_project2"],
srcdirs=["p1_path1", "p1_path2"],
subdirs=["files", None],
subtrees=["p1_path1/a", "p1_path1/b", "p1_path2"],
)
else:
source_mock.return_value = portage_util.SourceInfo(
projects=["fake_project1"],
srcdirs=["p1_path1"],
subdirs=["files", None],
subtrees=["p1_path1/a", "p1_path1/b"],
)
self.PatchObject(
portage_util.EBuild, "GetTreeId", side_effect=_GetTreeId
)
self.PatchObject(portage_util.EBuild, "_RunGit", side_effect=_RunGit)
osutils.WriteFile(
self.m_ebuild._unstable_ebuild_path, ebuild_content, makedirs=True
)
osutils.WriteFile(
self.m_ebuild.ebuild_path, ebuild_content, makedirs=True
)
def testRevWorkOnEBuild(self):
"""Test Uprev of a single project ebuild."""
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild, osutils.ReadFile(self.revved_ebuild_path)
)
def testRevUnchangedEBuildSubdirsNoChange(self):
"""Uprev of a single-project ebuild with CROS_WORKON_SUBDIRS_TO_REV.
No files changed in git, so this should not uprev.
"""
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=False)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertIsNone(result)
self.assertNotExists(self.revved_ebuild_path)
def testRevUnchangedEBuildSubdirsChange(self):
"""Uprev of a single-project ebuild with CROS_WORKON_SUBDIRS_TO_REV.
The 'foo' directory is changed in git, and this directory is mentioned
in CROS_WORKON_SUBDIRS_TO_REV, so this should uprev.
"""
self.git_files_changed = ["foo"]
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=True)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild_subdir,
osutils.ReadFile(self.revved_ebuild_path),
)
def testRevChangedEBuildFilesChanged(self):
"""Test Uprev of a single-project ebuild with files/ content change.
The 'files' directory is changed in git and some other directory is
mentioned in CROS_WORKON_SUBDIRS_TO_REV. files/ should always force
uprev.
"""
self.git_files_changed = ["files"]
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=True)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild_subdir,
osutils.ReadFile(self.revved_ebuild_path),
)
def testRevUnchangedEBuildFilesChanged(self):
"""Test Uprev of a single-project ebuild with files/ content change.
The 'files' directory is changed in git and some other directory is
mentioned in CROS_WORKON_SUBDIRS_TO_REV. files/ should always force
uprev.
"""
self.git_files_changed = ["files"]
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=False)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild_subdir,
osutils.ReadFile(self.revved_ebuild_path),
)
def testRevUnchangedEBuildOtherSubdirChange(self):
"""Uprev an other subdir with no CROS_WORKON_SUBDIRS_TO_REV.
The 'other' directory is changed in git, but there is no
CROS_WORKON_SUBDIRS_TO_REV in the build, so any change causes an uprev.
"""
self.git_files_changed = ["other"]
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild, osutils.ReadFile(self.revved_ebuild_path)
)
def testNoRevUnchangedEBuildOtherSubdirChange(self):
"""Uprev an other subdir with no CROS_WORKON_SUBDIRS_TO_REV.
The 'other' directory is changed in git, but CROS_WORKON_SUBDIRS_TO_REV
is empty, so this should not uprev.
"""
self.git_files_changed = ["other"]
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=False)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertIsNone(result)
self.assertNotExists(self.revved_ebuild_path)
def testRevChangedEBuildNoSubdirChange(self):
"""Uprev a changed ebuild with CROS_WORKON_SUBDIRS_TO_REV.
Any change to the 9999 ebuild should cause an uprev, even if
CROS_WORKON_SUBDIRS_TO_REV is set and no files in that list are changed
in git.
"""
self.unstable_ebuild_changed = True
self.createRevWorkOnMocks(self._mock_ebuild_subdir, rev=True)
self.m_ebuild.cros_workon_vars = portage_util.EBuild.GetCrosWorkonVars(
self.m_ebuild.ebuild_path, "test-package"
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild_subdir,
osutils.ReadFile(self.revved_ebuild_path),
)
def testRevWorkOnMultiEBuild(self):
"""Test Uprev of a multi-project (array) ebuild."""
self.createRevWorkOnMocks(self._mock_ebuild_multi, rev=True, multi=True)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild_multi, osutils.ReadFile(self.revved_ebuild_path)
)
def testRevUnchangedEBuild(self):
self.createRevWorkOnMocks(self._mock_ebuild, rev=False)
self.PatchObject(
portage_util.EBuild, "_AlmostSameEBuilds", return_value=True
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertIsNone(result)
self.assertNotExists(self.revved_ebuild_path)
def testRevMissingEBuild(self):
self.revved_ebuild_path = self.m_ebuild.ebuild_path
self.m_ebuild.ebuild_path = self.m_ebuild._unstable_ebuild_path
self.m_ebuild.current_revision = 0
self.m_ebuild.is_stable = False
self.createRevWorkOnMocks(
self._mock_ebuild[0:1] + self._mock_ebuild[2:], rev=True
)
result = self.m_ebuild.RevWorkOnEBuild(self.tempdir, MANIFEST)
self.assertEqual(result[0], "category/test_package-0.0.1-r1")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild, osutils.ReadFile(self.revved_ebuild_path)
)
def testRevForceStableVersionEBuild(self):
"""Test force stable version uprev of a ebuild."""
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
result = self.m_ebuild.RevWorkOnEBuild(
self.tempdir, MANIFEST, new_version="777.0.0"
)
self.assertEqual(result[0], "category/test_package-777.0.0-r1")
self.assertExists(self.revved_ebuild_path_forced_version)
self.assertEqual(
self._revved_ebuild,
osutils.ReadFile(self.revved_ebuild_path_forced_version),
)
def testRevForceStableVersionSameVersionEBuild(self):
"""Test force stable version uprev of a ebuild with same version."""
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
result = self.m_ebuild.RevWorkOnEBuild(
self.tempdir, MANIFEST, new_version="0.0.1"
)
self.assertEqual(result[0], "category/test_package-0.0.1-r2")
self.assertExists(self.revved_ebuild_path)
self.assertEqual(
self._revved_ebuild, osutils.ReadFile(self.revved_ebuild_path)
)
def testRevInvalidVersionWithRevisionEBuild(self):
"""Test force stable version uprev of a ebuild with same version."""
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
with self.assertRaises(ValueError):
self.m_ebuild.RevWorkOnEBuild(
self.tempdir, MANIFEST, new_version="0.0.1-r777"
)
def testRevInvalidGibberishVersionEBuild(self):
"""Test force stable version uprev of a ebuild with same version."""
self.createRevWorkOnMocks(self._mock_ebuild, rev=True)
with self.assertRaises(ValueError):
self.m_ebuild.RevWorkOnEBuild(
self.tempdir, MANIFEST, new_version="gibberish-version-0000"
)
def testCommitChange(self):
m = self.PatchObject(portage_util.EBuild, "_RunGit", return_value="")
mock_message = "Commitme"
self.m_ebuild.CommitChange(mock_message, ".")
m.assert_called_once_with(".", ["commit", "-a", "-m", "Commitme"])
def testGitRepoHasChanges(self):
"""Tests that GitRepoHasChanges works correctly."""
git.RunGit(
self.tempdir,
[
"clone",
"--depth=1",
f"file://{constants.CHROMITE_DIR}",
self.tempdir,
],
)
# No changes yet as we just cloned the repo.
self.assertFalse(portage_util.EBuild.GitRepoHasChanges(self.tempdir))
# Update metadata but no real changes.
osutils.Touch(os.path.join(self.tempdir, "LICENSE"))
self.assertFalse(portage_util.EBuild.GitRepoHasChanges(self.tempdir))
# A real change.
osutils.WriteFile(os.path.join(self.tempdir, "LICENSE"), "hi")
self.assertTrue(portage_util.EBuild.GitRepoHasChanges(self.tempdir))
def testNoVersionScript(self):
"""Verify default behavior with no chromeos-version.sh script."""
self.assertEqual("1234", self.m_ebuild.GetVersion(None, None, "1234"))
def testValidVersionScript(self):
"""Verify normal behavior with a chromeos-version.sh script."""
exists = self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(
portage_util.EBuild,
"GetSourceInfo",
return_value=portage_util.SourceInfo(
projects=None, srcdirs=[], subdirs=[], subtrees=[]
),
)
rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
rc_mock.SetDefaultCmdResult(stdout="1122", stderr="STDERR")
self.assertEqual("1122", self.m_ebuild.GetVersion(None, None, "1234"))
# Sanity check.
self.assertEqual(exists.call_count, 1)
def testVersionScriptNoOutput(self):
"""Reject scripts that output nothing."""
exists = self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(
portage_util.EBuild,
"GetSourceInfo",
return_value=portage_util.SourceInfo(
projects=None, srcdirs=[], subdirs=[], subtrees=[]
),
)
rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
# Reject no output.
rc_mock.SetDefaultCmdResult(stdout="", stderr="STDERR")
self.assertRaises(
portage_util.Error, self.m_ebuild.GetVersion, None, None, "1234"
)
# Sanity check.
self.assertEqual(exists.call_count, 1)
exists.reset_mock()
# Reject simple output.
rc_mock.SetDefaultCmdResult(stdout="\n", stderr="STDERR")
self.assertRaises(
portage_util.Error, self.m_ebuild.GetVersion, None, None, "1234"
)
# Sanity check.
self.assertEqual(exists.call_count, 1)
exists.reset_mock()
# Reject error.
rc_mock.SetDefaultCmdResult(
returncode=1, stdout="FAIL\n", stderr="STDERR"
)
self.assertRaises(
portage_util.Error, self.m_ebuild.GetVersion, None, None, "1234"
)
# Sanity check.
self.assertEqual(exists.call_count, 1)
def testVersionScriptTooHighVersion(self):
"""Reject scripts that output high version numbers."""
exists = self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(
portage_util.EBuild,
"GetSourceInfo",
return_value=portage_util.SourceInfo(
projects=None, srcdirs=[], subdirs=[], subtrees=[]
),
)
rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
rc_mock.SetDefaultCmdResult(stdout="999999", stderr="STDERR")
self.assertRaises(
ValueError, self.m_ebuild.GetVersion, None, None, "1234"
)
# Sanity check.
self.assertEqual(exists.call_count, 1)
def testVersionScriptInvalidVersion(self):
"""Reject scripts that output bad version numbers."""
exists = self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(
portage_util.EBuild,
"GetSourceInfo",
return_value=portage_util.SourceInfo(
projects=None, srcdirs=[], subdirs=[], subtrees=[]
),
)
rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
rc_mock.SetDefaultCmdResult(stdout="abcd", stderr="STDERR")
self.assertRaises(
ValueError, self.m_ebuild.GetVersion, None, None, "1234"
)
# Sanity check.
self.assertEqual(exists.call_count, 1)
def testVersionScriptInvalidVersionPostfix(self):
"""Reject scripts that output bad version numbers."""
exists = self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(
portage_util.EBuild,
"GetSourceInfo",
return_value=portage_util.SourceInfo(
projects=None, srcdirs=[], subdirs=[], subtrees=[]
),
)
rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
rc_mock.SetDefaultCmdResult(stdout="4.4.21_baseline", stderr="STDERR")
with self.assertRaises(portage_util.EbuildVersionError):
self.m_ebuild.GetVersion(None, None, "1234")
# Sanity check.
self.assertEqual(exists.call_count, 1)
def testUpdateEBuildRecovery(self):
"""Verify UpdateEBuild can be called more than once even w/failures."""
ebuild = os.path.join(self.tempdir, "test.ebuild")
content = "# Some data\nVAR=val\n"
osutils.WriteFile(ebuild, content)
# First run: pass in an invalid redirect file to trigger an exception.
try:
portage_util.EBuild.UpdateEBuild(ebuild, [])
self.fail("this should have thrown an exception")
except Exception:
pass
self.assertEqual(content, osutils.ReadFile(ebuild))
# Second run: it should pass normally.
portage_util.EBuild.UpdateEBuild(ebuild, {"VAR": "b"})
def testUpdateEBuildSpacing(self):
"""Verify UpdateEBuild does not edit marked variables."""
ebuild = os.path.join(self.tempdir, "test.ebuild")
content = (
"# Some data\n"
"VAR=a # portage_util: no edit\n"
"VAR=b\n"
"\tVAR=c\n"
)
expected_content = "# Some data\nVAR=d\nVAR=a # portage_util: no edit\n"
osutils.WriteFile(ebuild, content)
# Check that all VARs are removed except the one with no edit.
portage_util.EBuild.UpdateEBuild(ebuild, {"VAR": "d"})
self.assertEqual(expected_content, osutils.ReadFile(ebuild))
# And check idempotency.
portage_util.EBuild.UpdateEBuild(ebuild, {"VAR": "d"})
self.assertEqual(expected_content, osutils.ReadFile(ebuild))
class ListOverlaysTest(cros_test_lib.TempDirTestCase):
"""Tests related to listing overlays."""
def testMissingOverlays(self):
"""Tests that exceptions are raised when an overlay is missing."""
self.assertRaises(
portage_util.MissingOverlayError,
portage_util._ListOverlays,
board="foo",
buildroot=self.tempdir,
)
class FindOverlaysTest(cros_test_lib.MockTempDirTestCase):
"""Tests related to finding overlays."""
FAKE, PUB_PRIV, PUB_PRIV_VARIANT, PUB_ONLY, PUB2_ONLY, PRIV_ONLY = (
"fake!board",
"pub-priv-board",
"pub-priv-board_variant",
"pub-only-board",
"pub2-only-board",
"priv-only-board",
)
PRIVATE = constants.PRIVATE_OVERLAYS
PUBLIC = constants.PUBLIC_OVERLAYS
BOTH = constants.BOTH_OVERLAYS
def setUp(self):
# Create an overlay tree to run tests against and isolate ourselves from
# changes in the main tree.
D = cros_test_lib.Directory
overlay_files = (D("metadata", ("layout.conf",)),)
board_overlay_files = overlay_files + (
"make.conf",
"toolchain.conf",
)
file_layout = (
D(
"src",
(
D(
"overlays",
(
D(
"overlay-%s" % self.PUB_ONLY,
board_overlay_files,
),
D(
"overlay-%s" % self.PUB2_ONLY,
board_overlay_files,
),
D(
"overlay-%s" % self.PUB_PRIV,
board_overlay_files,
),
D(
"overlay-%s" % self.PUB_PRIV_VARIANT,
board_overlay_files,
),
),
),
D(
"private-overlays",
(
D(
"overlay-%s" % self.PUB_PRIV,
board_overlay_files,
),
D(
"overlay-%s" % self.PUB_PRIV_VARIANT,
board_overlay_files,
),
D(
"overlay-%s" % self.PRIV_ONLY,
board_overlay_files,
),
),
),
D(
"third_party",
(
D("chromiumos-overlay", overlay_files),
D("portage-stable", overlay_files),
),
),
),
),
)
cros_test_lib.CreateOnDiskHierarchy(self.tempdir, file_layout)
# Seed the board overlays.
conf_data = "repo-name = %(repo-name)s\nmasters = %(masters)s"
conf_path = os.path.join(
self.tempdir,
"src",
"%(private)soverlays",
"overlay-%(board)s",
"metadata",
"layout.conf",
)
for board in (
self.PUB_PRIV,
self.PUB_PRIV_VARIANT,
self.PUB_ONLY,
self.PUB2_ONLY,
):
settings = {
"board": board,
"masters": "portage-stable ",
"private": "",
"repo-name": board,
}
if "_" in board:
# TODO(b/236161656): Fix.
# pylint: disable-next=use-maxsplit-arg
settings["masters"] += board.split("_")[0]
osutils.WriteFile(conf_path % settings, conf_data % settings)
for board in (self.PUB_PRIV, self.PUB_PRIV_VARIANT, self.PRIV_ONLY):
settings = {
"board": board,
"masters": "portage-stable ",
"private": "private-",
"repo-name": "%s-private" % board,
}
if "_" in board:
# TODO(b/236161656): Fix.
# pylint: disable-next=use-maxsplit-arg
settings["masters"] += board.split("_")[0]
osutils.WriteFile(conf_path % settings, conf_data % settings)
# Seed the common overlays.
conf_path = os.path.join(
self.tempdir,
"src",
"third_party",
"%(overlay)s",
"metadata",
"layout.conf",
)
osutils.WriteFile(
conf_path % {"overlay": "chromiumos-overlay"},
conf_data % {"repo-name": "chromiumos", "masters": ""},
)
osutils.WriteFile(
conf_path % {"overlay": "portage-stable"},
conf_data % {"repo-name": "portage-stable", "masters": ""},
)
# Now build up the list of overlays that we'll use in tests below.
self.overlays = {}
for b in (
None,
self.FAKE,
self.PUB_PRIV,
self.PUB_PRIV_VARIANT,
self.PUB_ONLY,
self.PUB2_ONLY,
self.PRIV_ONLY,
):
self.overlays[b] = d = {}
for o in (self.PRIVATE, self.PUBLIC, self.BOTH, None):
try:
d[o] = portage_util.FindOverlays(o, b, self.tempdir)
except portage_util.MissingOverlayError:
d[o] = []
self._no_overlays = not bool(any(d.values()))
def testDuplicates(self):
"""Verify that no duplicate overlays are returned."""
for d in self.overlays.values():
for overlays in d.values():
self.assertEqual(len(overlays), len(set(overlays)))
def testOverlaysExist(self):
"""Verify that all overlays returned actually exist on disk."""
for d in self.overlays.values():
for overlays in d.values():
self.assertTrue(all(os.path.isdir(x) for x in overlays))
def testPrivatePublicOverlayTypes(self):
"""Verify public/private filtering.
If we ask for results from 'both overlays', we should
find all public and all private overlays.
"""
for b, d in self.overlays.items():
if b == self.FAKE:
continue
self.assertGreaterEqual(set(d[self.BOTH]), set(d[self.PUBLIC]))
self.assertGreater(set(d[self.BOTH]), set(d[self.PRIVATE]))
self.assertTrue(set(d[self.PUBLIC]).isdisjoint(d[self.PRIVATE]))
def testNoOverlayType(self):
"""If we specify overlay_type=None, no results should be returned."""
self.assertTrue(all(d[None] == [] for d in self.overlays.values()))
def testNonExistentBoard(self):
"""Test what happens when a non-existent board is supplied.
If we specify a non-existent board to FindOverlays, only generic
overlays should be returned.
"""
for o in (self.PUBLIC, self.BOTH):
self.assertLess(
set(self.overlays[self.FAKE][o]),
set(self.overlays[self.PUB_PRIV][o]),
)
def testAllBoards(self):
"""If we specify board=None, all overlays should be returned."""
for o in (self.PUBLIC, self.BOTH):
for b in (self.FAKE, self.PUB_PRIV):
self.assertLess(
set(self.overlays[b][o]), set(self.overlays[None][o])
)
def testReadOverlayFileOrder(self):
"""Verify that the boards are examined in the right order."""
m = self.PatchObject(os.path, "isfile", return_value=False)
portage_util.ReadOverlayFile(
"test", self.PUBLIC, self.PUB_PRIV, self.tempdir
)
read_overlays = [x[0][0][:-5] for x in m.call_args_list]
overlays = list(reversed(self.overlays[self.PUB_PRIV][self.PUBLIC]))
self.assertEqual(read_overlays, overlays)
def testFindOverlayFile(self):
"""Verify that the first file found is returned."""
file_to_find = "something_special"
full_path = os.path.join(
self.tempdir,
"src",
"private-overlays",
"overlay-%s" % self.PUB_PRIV,
file_to_find,
)
osutils.Touch(full_path)
self.assertEqual(
full_path,
portage_util.FindOverlayFile(
file_to_find, self.BOTH, self.PUB_PRIV_VARIANT, self.tempdir
),
)
def testFoundPrivateOverlays(self):
"""Verify that private boards had their overlays located."""
for b in (self.PUB_PRIV, self.PUB_PRIV_VARIANT, self.PRIV_ONLY):
self.assertNotEqual(self.overlays[b][self.PRIVATE], [])
self.assertNotEqual(
self.overlays[self.PUB_PRIV][self.BOTH],
self.overlays[self.PUB_PRIV][self.PRIVATE],
)
self.assertNotEqual(
self.overlays[self.PUB_PRIV_VARIANT][self.BOTH],
self.overlays[self.PUB_PRIV_VARIANT][self.PRIVATE],
)
def testFoundPublicOverlays(self):
"""Verify that public boards had their overlays located."""
for b in (
self.PUB_PRIV,
self.PUB_PRIV_VARIANT,
self.PUB_ONLY,
self.PUB2_ONLY,
):
self.assertNotEqual(self.overlays[b][self.PUBLIC], [])
self.assertNotEqual(
self.overlays[self.PUB_PRIV][self.BOTH],
self.overlays[self.PUB_PRIV][self.PUBLIC],
)
self.assertNotEqual(
self.overlays[self.PUB_PRIV_VARIANT][self.BOTH],
self.overlays[self.PUB_PRIV_VARIANT][self.PUBLIC],
)
def testFoundParentOverlays(self):
"""Verify that the overlays for a parent board are found."""
for d in self.PUBLIC, self.PRIVATE:
self.assertLess(
set(self.overlays[self.PUB_PRIV][d]),
set(self.overlays[self.PUB_PRIV_VARIANT][d]),
)
class UtilFuncsTest(cros_test_lib.TempDirTestCase):
"""Basic tests for utility functions"""
def _CreateProfilesRepoName(self, name):
"""Write |name| to profiles/repo_name"""
profiles = os.path.join(self.tempdir, "profiles")
osutils.SafeMakedirs(profiles)
repo_name = os.path.join(profiles, "repo_name")
osutils.WriteFile(repo_name, name)
def testGetOverlayNameNone(self):
"""If the overlay has no name, it should be fine"""
self.assertEqual(portage_util.GetOverlayName(self.tempdir), None)
def testGetOverlayNameProfilesRepoName(self):
"""Verify profiles/repo_name can be read"""
self._CreateProfilesRepoName("hi!")
self.assertEqual(portage_util.GetOverlayName(self.tempdir), "hi!")
def testGetOverlayNameProfilesLayoutConf(self):
"""Verify metadata/layout.conf is read before profiles/repo_name"""
self._CreateProfilesRepoName("hi!")
metadata = os.path.join(self.tempdir, "metadata")
osutils.SafeMakedirs(metadata)
layout_conf = os.path.join(metadata, "layout.conf")
osutils.WriteFile(layout_conf, "repo-name = bye")
self.assertEqual(portage_util.GetOverlayName(self.tempdir), "bye")
def testGetOverlayNameProfilesLayoutConfNoRepoName(self):
"""Verify metadata/layout.conf w/out repo-name is ignored"""
self._CreateProfilesRepoName("hi!")
metadata = os.path.join(self.tempdir, "metadata")
osutils.SafeMakedirs(metadata)
layout_conf = os.path.join(metadata, "layout.conf")
osutils.WriteFile(layout_conf, "here = we go")
self.assertEqual(portage_util.GetOverlayName(self.tempdir), "hi!")
def testGetRepositoryFromEbuildInfo(self):
"""Verify GetRepositoryFromEbuildInfo handles data from ebuild info."""
def _runTestGetRepositoryFromEbuildInfo(fake_projects, fake_srcdirs):
"""Generate the output from ebuild info"""
# ebuild info always put () around the result, even for single
# element array.
fake_ebuild_contents = """
CROS_WORKON_PROJECT=("%s")
CROS_WORKON_SRCDIR=("%s")
""" % (
'" "'.join(fake_projects),
'" "'.join(fake_srcdirs),
)
result = portage_util.GetRepositoryFromEbuildInfo(
fake_ebuild_contents
)
result_srcdirs, result_projects = zip(*result)
self.assertEqual(fake_projects, list(result_projects))
self.assertEqual(fake_srcdirs, list(result_srcdirs))
_runTestGetRepositoryFromEbuildInfo(["a", "b"], ["src_a", "src_b"])
_runTestGetRepositoryFromEbuildInfo(["a"], ["src_a"])
class GetOverlayEBuildsTest(cros_test_lib.MockTempDirTestCase):
"""Tests for GetOverlayEBuilds."""
def setUp(self):
self.overlay = self.tempdir
self.uprev_candidate_mock = self.PatchObject(
portage_util,
"_FindUprevCandidates",
side_effect=GetOverlayEBuildsTest._FindUprevCandidateMock,
)
def _CreatePackage(self, name, manually_uprevved=False):
"""Helper that creates an ebuild."""
package_path = os.path.join(
self.overlay, name, "test_package-0.0.1.ebuild"
)
content = "CROS_WORKON_MANUAL_UPREV=1" if manually_uprevved else ""
osutils.WriteFile(package_path, content, makedirs=True)
@staticmethod
def _FindUprevCandidateMock(files, allow_manual_uprev, _subdir_support):
"""Mock for the FindUprevCandidateMock function.
Simplified implementation of FindUprevCandidate: consider an ebuild
worthy of uprev if |allow_manual_uprev| is set or the ebuild is not
manually uprevved.
"""
for f in files:
if f.endswith(".ebuild") and (
"CROS_WORKON_MANUAL_UPREV=1" not in osutils.ReadFile(f)
or allow_manual_uprev
):
pkgdir = os.path.dirname(f)
return _Package(
os.path.join(
os.path.basename(os.path.dirname(pkgdir)),
os.path.basename(pkgdir),
)
)
return None
def _assertFoundPackages(self, ebuilds, packages):
"""Succeeds iff the packages discovered were packages."""
self.assertEqual([e.package for e in ebuilds], packages)
def testWantedPackage(self):
"""Test that we can find a specific package."""
package_name = "chromeos-base/mypackage"
self._CreatePackage(package_name)
ebuilds = portage_util.GetOverlayEBuilds(
self.overlay, False, [package_name]
)
self._assertFoundPackages(ebuilds, [package_name])
def testUnwantedPackage(self):
"""Test that we find only the packages we want."""
ebuilds = portage_util.GetOverlayEBuilds(self.overlay, False, [])
self._assertFoundPackages(ebuilds, [])
def testAnyPackage(self):
"""Test that we return all packages available if use_all is set."""
package_name = "chromeos-base/package_name"
self._CreatePackage(package_name)
ebuilds = portage_util.GetOverlayEBuilds(self.overlay, True, [])
self._assertFoundPackages(ebuilds, [package_name])
def testUnknownPackage(self):
"""Test that _FindUprevCandidates is only called if the CP matches."""
self._CreatePackage("chromeos-base/package_name")
ebuilds = portage_util.GetOverlayEBuilds(
self.overlay, False, ["chromeos-base/other_package"]
)
self.assertFalse(self.uprev_candidate_mock.called)
self._assertFoundPackages(ebuilds, [])
def testManuallyUprevedPackagesIgnoredByDefault(self):
"""Test that manually uprevved packages are ignored by default."""
package_name = "chromeos-base/manuallyuprevved_package"
self._CreatePackage(package_name, manually_uprevved=True)
ebuilds = portage_util.GetOverlayEBuilds(
self.overlay, False, [package_name]
)
self._assertFoundPackages(ebuilds, [])
def testManuallyUprevedPackagesAllowed(self):
"""Test that we can find manually uprevved packages.
When we specify the |allow_manual_uprev| parameter.
"""
package_name = "chromeos-base/manuallyuprevved_package"
self._CreatePackage(package_name, manually_uprevved=True)
ebuilds = portage_util.GetOverlayEBuilds(
self.overlay, False, [package_name], allow_manual_uprev=True
)
self._assertFoundPackages(ebuilds, [package_name])
class ProjectMappingTest(cros_test_lib.TestCase):
"""Tests related to Proejct Mapping."""
def testSplitEbuildPath(self):
"""Test if we can split an ebuild path into its components."""
ebuild_path = "chromeos-base/platform2/platform2-9999.ebuild"
components = ["chromeos-base", "platform2", "platform2-9999"]
for path in (ebuild_path, "./" + ebuild_path, "foo.bar/" + ebuild_path):
self.assertEqual(components, portage_util.SplitEbuildPath(path))
def testFindWorkonProjects(self):
"""Test if we can find the list of workon projects."""
frecon = "sys-apps/frecon"
frecon_project = "chromiumos/platform/frecon"
dev_install = "chromeos-base/dev-install"
dev_install_project = "chromiumos/platform2"
matches = [
([frecon], {frecon_project}),
([dev_install], {dev_install_project}),
([frecon, dev_install], {frecon_project, dev_install_project}),
]
if portage_util.FindOverlays(constants.BOTH_OVERLAYS):
for packages, projects in matches:
self.assertEqual(
projects, portage_util.FindWorkonProjects(packages)
)
class PortageDBTest(cros_test_lib.TempDirTestCase):
"""Portage package Database related tests."""
fake_pkgdb = {
"category1": ["package-1", "package-2"],
"category2": ["package-3", "package-4"],
"category3": ["invalid", "semi-invalid"],
"with": ["files-1"],
"dash-category": ["package-5"],
"-invalid": ["package-6"],
"invalid": [],
}
fake_packages = []
build_root = None
fake_chroot = None
fake_files = [
("dir", "/lib64"),
(
"obj",
"/lib64/libext2fs.so.2.4",
"a6723f44cf82f1979e9731043f820d8c",
"1390848093",
),
("dir", "/dir with spaces"),
(
"obj",
"/dir with spaces/file with spaces",
"cd4865bbf122da11fca97a04dfcac258",
"1390848093",
),
("sym", "/lib64/libe2p.so.2", "->", "libe2p.so.2.3", "1390850489"),
"foo",
]
def setUp(self):
self.build_root = self.tempdir
self.fake_packages = []
# Prepare a fake chroot.
self.fake_chroot = os.path.join(
self.build_root, "chroot/build/amd64-host"
)
fake_pkgdb_path = os.path.join(self.fake_chroot, portage_util.VDB_PATH)
os.makedirs(fake_pkgdb_path)
for cat, pkgs in self.fake_pkgdb.items():
catpath = os.path.join(fake_pkgdb_path, cat)
if cat == "invalid":
# Invalid category is a file. Should not be delved into.
osutils.Touch(catpath)
continue
os.makedirs(catpath)
for pkg in pkgs:
pkgpath = os.path.join(catpath, pkg)
if pkg == "invalid":
# Invalid package is a file instead of a directory/
osutils.Touch(pkgpath)
continue
os.makedirs(pkgpath)
if pkg.endswith("-invalid"):
# Invalid package does not meet existence of "%s/%s.ebuild"
# file.
osutils.Touch(os.path.join(pkgpath, "whatever"))
continue
# Create the package.
osutils.Touch(os.path.join(pkgpath, pkg + ".ebuild"))
if cat.startswith("-"):
# Invalid category.
continue
# Correct pkg.
pv = package_info.parse(pkg)
key = "%s/%s" % (cat, pv.package)
self.fake_packages.append((key, pv.vr))
# Add contents to with/files-1.
osutils.WriteFile(
os.path.join(fake_pkgdb_path, "with", "files-1", "CONTENTS"),
"".join(" ".join(entry) + "\n" for entry in self.fake_files),
)
def testListInstalledPackages(self):
"""Test if listing packages installed into a root works."""
packages = portage_util.ListInstalledPackages(self.fake_chroot)
# Sort the lists, because the filesystem might reorder the entries for
# us.
packages.sort()
self.fake_packages.sort()
self.assertEqual(self.fake_packages, packages)
def testCalculatePackageSizes_ApparentSize(self):
"""Test if calculating disk usage of installed packages works."""
fake_data = "FAKE DATA"
expected_size = 0
for fake_file in self.fake_files:
if fake_file[0] == "obj":
fake_filename = os.path.join(
self.fake_chroot, os.path.relpath(fake_file[1], "/")
)
osutils.WriteFile(fake_filename, fake_data, makedirs=True)
expected_size += len(fake_data)
portage_db = portage_util.PortageDB(self.fake_chroot)
installed_packages = portage_db.InstalledPackages()
# Only one package in fake portage db has files associated with it.
total_size = 0
for p in installed_packages:
sizes = portage_util.CalculatePackageSize(
p.ListContents(), self.fake_chroot
)
total_size += sizes.apparent_size
self.assertEqual(total_size, expected_size)
def testCalculatePackageSizes_DiskUsage(self):
"""Test if calculating disk usage of installed packages works."""
fake_data = "FAKE DATA"
expected_size = 0
for fake_file in self.fake_files:
if fake_file[0] == "obj":
fake_filename = os.path.join(
self.fake_chroot, os.path.relpath(fake_file[1], "/")
)
osutils.WriteFile(fake_filename, fake_data, makedirs=True)
# Filesystems allocate 4096 bytes on disk for new files.
expected_size += 8 * 512
portage_db = portage_util.PortageDB(self.fake_chroot)
installed_packages = portage_db.InstalledPackages()
# Only one package in fake portage db has files associated with it.
total_size = 0
for p in installed_packages:
sizes = portage_util.CalculatePackageSize(
p.ListContents(), self.fake_chroot
)
total_size += sizes.disk_utilization_size
self.assertEqual(total_size, expected_size)
def testGeneratePackageSizes(self):
"""Test if calculating installed package sizes works."""
fake_data = "FAKE DATA"
expected_size = 0
for fake_file in self.fake_files:
if fake_file[0] == "obj":
fake_filename = os.path.join(
self.fake_chroot, os.path.relpath(fake_file[1], "/")
)
osutils.WriteFile(fake_filename, fake_data, makedirs=True)
expected_size += len(fake_data)
portage_db = portage_util.PortageDB(self.fake_chroot)
installed_packages = portage_db.InstalledPackages()
package_size_pairs = portage_util.GeneratePackageSizes(
portage_db, "fake_chroot", installed_packages
)
total_size = sum(x for _, x in package_size_pairs)
self.assertEqual(total_size, expected_size)
def testIsPackageInstalled(self):
"""Test if checking the existence of an installed package works."""
self.assertTrue(
portage_util.IsPackageInstalled(
"category1/package", sysroot=self.fake_chroot
)
)
self.assertFalse(
portage_util.IsPackageInstalled(
"category1/foo", sysroot=self.fake_chroot
)
)
def testListContents(self):
"""Test if the list of installed files is properly parsed."""
pdb = portage_util.PortageDB(self.fake_chroot)
pkg = pdb.GetInstalledPackage("with", "files-1")
self.assertTrue(pkg)
lst = pkg.ListContents()
# Check ListContents filters out the garbage we added to the list of
# files.
fake_files = [
f for f in self.fake_files if f[0] in ("sym", "obj", "dir")
]
self.assertEqual(len(fake_files), len(lst))
# Check the paths are all relative.
self.assertTrue(all(not f[1].startswith("/") for f in lst))
# Check all the files are present. We only consider file type and path,
# and convert the path to a relative path.
fake_files = [(f[0], f[1].lstrip("/")) for f in fake_files]
self.assertEqual(fake_files, lst)
def testPackageInfo(self):
"""Verify construction and self consistency of the PackageInfo."""
portage_db = portage_util.PortageDB(self.fake_chroot)
for pkg in portage_db.InstalledPackages():
self.assertEqual(pkg.category, pkg.package_info.category)
self.assertEqual(pkg.pf, pkg.package_info.pvr)
class InstalledPackageTest(cros_test_lib.TempDirTestCase):
"""InstalledPackage class tests outside a PortageDB."""
def setUp(self):
content = (
("package-1.ebuild", "EAPI=1"),
("CATEGORY", "category-1\n"),
("HOMEPAGE", "http://example.com\n"),
("LICENSE", "GPL-2\n"),
("NEEDED", "/usr/sbin/bootlockboxd libmetrics.so,libhwsec.so\n"),
("PF", "package-1\n"),
("PROVIDES", "x86_64: libsystem_api.so\n"),
("repository", "portage-stable\n"),
("REQUIRES", "x86_64: libc++.so.1 libc++abi.so.1 libc.so.6\n"),
("SIZE", "123\n"),
)
for path, data in content:
osutils.WriteFile(os.path.join(self.tempdir, path), data)
def testOutOfDBPackage(self):
"""Verify InstalledPackage instance can be created w/o a PortageDB."""
pkg = portage_util.InstalledPackage(None, self.tempdir)
self.assertEqual("category-1", pkg.category)
self.assertEqual("http://example.com", pkg.homepage)
self.assertEqual("GPL-2", pkg.license)
self.assertEqual(
{"/usr/sbin/bootlockboxd": ["libmetrics.so", "libhwsec.so"]},
pkg.needed,
)
self.assertEqual("package-1", pkg.pf)
self.assertEqual("portage-stable", pkg.repository)
self.assertEqual(
"x86_64: libc++.so.1 libc++abi.so.1 libc.so.6", pkg.requires
)
self.assertEqual("123", pkg.size)
def testIncompletePackage(self):
"""Tests an incomplete or invalid package raises an exception."""
# No package name is provided.
os.unlink(os.path.join(self.tempdir, "PF"))
self.assertRaises(
portage_util.PortageDBError,
portage_util.InstalledPackage,
None,
self.tempdir,
)
# Check that doesn't fail when the package name is provided.
pkg = portage_util.InstalledPackage(None, self.tempdir, pf="package-1")
self.assertEqual("package-1", pkg.pf)
class HasPrebuiltTest(cros_test_lib.RunCommandTestCase):
"""HasPrebuilt tests."""
def setUp(self):
self.atom = constants.CHROME_CP
def testHasPrebuilt(self):
"""Test a package with a matching prebuilt."""
self.rc.SetDefaultCmdResult(returncode=0)
self.PatchObject(
osutils, "ReadFile", return_value=json.dumps({self.atom: True})
)
self.assertTrue(portage_util.HasPrebuilt(self.atom))
def testNoPrebuilt(self):
"""Test a package without a matching prebuilt."""
self.rc.SetDefaultCmdResult(returncode=0)
self.PatchObject(
osutils, "ReadFile", return_value=json.dumps({self.atom: False})
)
self.assertFalse(portage_util.HasPrebuilt(self.atom))
def testScriptFailure(self):
"""Test the script failure fail safe returns false."""
self.rc.SetDefaultCmdResult(returncode=1)
self.assertFalse(portage_util.HasPrebuilt(self.atom))
class PortageqBestVisibleTest(cros_test_lib.MockTestCase):
"""PortageqBestVisible tests."""
def testValidPackage(self):
"""Test valid outputs."""
expected = package_info.PackageInfo("cat", "pkg", "1.0")
result = cros_build_lib.CompletedProcess(
stdout=expected.cpvr, returncode=0
)
self.PatchObject(portage_util, "_Portageq", return_value=result)
result = portage_util.PortageqBestVisible("cat/pkg")
self.assertEqual(expected, result)
class PortageqEnvvarTest(cros_test_lib.MockTestCase):
"""PortageqEnvvar[s] tests."""
def testValidEnvvar(self):
"""Test valid variables."""
result = cros_build_lib.CompletedProcess(
stdout="TEST=value\n", returncode=0
)
self.PatchObject(portage_util, "_Portageq", return_value=result)
envvar1 = portage_util.PortageqEnvvar("TEST")
envvars1 = portage_util.PortageqEnvvars(["TEST"])
self.assertEqual("value", envvar1)
self.assertEqual(envvar1, envvars1["TEST"])
def testUndefinedEnvvars(self):
"""Test undefined variable handling."""
# The variable exists in the command output even when not actually
# defined.
result = cros_build_lib.CompletedProcess(
stdout="DOES_NOT_EXIST=\n", returncode=1
)
success_error = cros_build_lib.RunCommandError("", result)
self.PatchObject(portage_util, "_Portageq", side_effect=success_error)
# Test ignoring error when just from undefined variable.
envv = portage_util.PortageqEnvvar(
"DOES_NOT_EXIST", allow_undefined=True
)
envvs = portage_util.PortageqEnvvars(
["DOES_NOT_EXIST"], allow_undefined=True
)
self.assertEqual("", envv)
self.assertEqual(envv, envvs["DOES_NOT_EXIST"])
# Test raising the error when just from undefined variable.
with self.assertRaises(portage_util.PortageqError):
portage_util.PortageqEnvvar("DOES_NOT_EXIST")
with self.assertRaises(portage_util.PortageqError):
portage_util.PortageqEnvvars(["DOES_NOT_EXIST"])
run_error = cros_build_lib.CompletedProcess(stdout="\n", returncode=2)
failure_error = cros_build_lib.RunCommandError("", run_error)
self.PatchObject(portage_util, "_Portageq", side_effect=failure_error)
# Test re-raising the error when the command did not run successfully.
with self.assertRaises(cros_build_lib.RunCommandError):
portage_util.PortageqEnvvar("DOES_NOT_EXIST")
with self.assertRaises(cros_build_lib.RunCommandError):
portage_util.PortageqEnvvars(["DOES_NOT_EXIST"])
def testInvalidEnvvars(self):
"""Test invalid variables handling."""
# Envvar tests.
with self.assertRaises(TypeError):
portage_util.PortageqEnvvar([])
with self.assertRaises(ValueError):
portage_util.PortageqEnvvar("")
# Envvars tests.
self.assertEqual({}, portage_util.PortageqEnvvars([]))
with self.assertRaises(TypeError):
portage_util.PortageqEnvvars("")
# Raised when extending the command list. This is currently expected,
# and ints should not be accepted, but more formal handling can be
# added.
with self.assertRaises(TypeError):
portage_util.PortageqEnvvars(1)
class PortageqHasVersionTest(cros_test_lib.MockTestCase):
"""PortageqHasVersion tests."""
def testPortageqHasVersion(self):
"""Test HasVersion."""
result_true = cros_build_lib.CompletedProcess(returncode=0)
result_false = cros_build_lib.CompletedProcess(returncode=1)
result_error = cros_build_lib.CompletedProcess(returncode=2)
# Test has version.
self.PatchObject(portage_util, "_Portageq", return_value=result_true)
self.assertTrue(portage_util.PortageqHasVersion("cat/pkg"))
# Test not has version.
self.PatchObject(portage_util, "_Portageq", return_value=result_false)
self.assertFalse(portage_util.PortageqHasVersion("cat/pkg"))
# Test run error.
self.PatchObject(portage_util, "_Portageq", return_value=result_error)
self.assertFalse(portage_util.PortageqHasVersion("cat/pkg"))
class PortageqMatchTest(cros_test_lib.MockTestCase):
"""PortageqMatch tests."""
def testMultiError(self):
"""Test unspecific query results in error.
The method currently isn't setup to support multiple values in the
output. It is instead interpreted as a cpv format error by SplitCPV.
This isn't a hard requirement, just the current expected behavior.
"""
output_str = "cat-1/pkg-one-1.0\ncat-2/pkg-two-2.1.3-r45\n"
result = cros_build_lib.CompletedProcess(
returncode=0, stdout=output_str
)
self.PatchObject(portage_util, "_Portageq", return_value=result)
with self.assertRaises(ValueError):
portage_util.PortageqMatch("*/*")
def testValidPackage(self):
"""Test valid package produces the corresponding PackageInfo."""
cpvr = "cat/pkg-1.0-r1"
result = cros_build_lib.CompletedProcess(returncode=0, stdout=cpvr)
self.PatchObject(portage_util, "_Portageq", return_value=result)
pkg = portage_util.PortageqMatch("cat/pkg")
self.assertIsInstance(pkg, package_info.PackageInfo)
assert pkg == cpvr
class FindEbuildTest(cros_test_lib.RunCommandTestCase):
"""Tests for FindEbuildsForPackages and FindEbuildsForPackages."""
def testFindEbuildsForPackagesReturnResultsSimple(self):
equery_output = (
"/chromeos-overlay/misc/foo/foo.ebuild\n"
"/chromeos-overlay/misc/bar/bar.ebuild\n"
)
self.rc.AddCmdResult(
[
"equery",
"--no-color",
"--no-pipe",
"which",
"misc/foo",
"misc/bar",
],
stdout=equery_output,
)
self.assertEqual(
portage_util.FindEbuildsForPackages(
["misc/foo", "misc/bar"], sysroot="/build/nami"
),
{
"misc/bar": "/chromeos-overlay/misc/bar/bar.ebuild",
"misc/foo": "/chromeos-overlay/misc/foo/foo.ebuild",
},
)
def testFindEbuildsForPackagesWithoutCategoryReturnResults(self):
equery_output = (
"/chromeos-overlay/misc/foo/foo.ebuild\n"
"/chromeos-overlay/misc/bar/bar.ebuild\n"
)
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which", "misc/foo", "bar"],
stdout=equery_output,
)
self.assertEqual(
portage_util.FindEbuildsForPackages(
["misc/foo", "bar"], sysroot="/build/nami"
),
{
"bar": "/chromeos-overlay/misc/bar/bar.ebuild",
"misc/foo": "/chromeos-overlay/misc/foo/foo.ebuild",
},
)
def testFindEbuildsForPackagesReturnResultsComplexPackages(self):
ebuild_path = (
"/portage-stable/sys-libs/timezone-data/timezone-data-2018i.ebuild"
)
equery_output = "\n".join([ebuild_path] * 4)
packages = [
# CATEGORY/PN
"sys-libs/timezone-data",
# CATEGORY/P
"sys-libs/timezone-data-2018i",
# CATEGORY/PN:SLOT
"sys-libs/timezone-data:0",
# CATEGORY/P:SLOT
"sys-libs/timezone-data-2018i:0",
]
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which"] + packages,
stdout=equery_output,
)
self.assertEqual(
portage_util.FindEbuildsForPackages(
packages, sysroot="/build/nami"
),
{
"sys-libs/timezone-data": ebuild_path,
"sys-libs/timezone-data-2018i:0": ebuild_path,
"sys-libs/timezone-data:0": ebuild_path,
"sys-libs/timezone-data-2018i": ebuild_path,
},
)
def testFindEbuildsForPackagesReturnNone(self):
# Result for package 'bar' is missing.
equery_output = "/chromeos-overlay/bar/bar.ebuild\n"
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which", "foo", "bar"],
stdout=equery_output,
returncode=1,
)
self.assertEqual(
portage_util.FindEbuildsForPackages(
["foo", "bar"], sysroot="/build/nami"
),
{},
)
def testFindEbuildsForPackagesInvalidEbuildsOrder(self):
equery_output = (
"/chromeos-overlay/bar/bar.ebuild\n"
"/chromeos-overlay/foo/foo.ebuild\n"
)
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which", "foo", "bar"],
stdout=equery_output,
)
with self.assertRaises(AssertionError):
portage_util.FindEbuildsForPackages(
["foo", "bar"], sysroot="/build/nami"
)
def testFindEbuildForPackageReturnResults(self):
equery_output = "/chromeos-overlay/misc/foo/foo-9999.ebuild\n"
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which", "misc/foo"],
stdout=equery_output,
)
self.assertEqual(
portage_util.FindEbuildForPackage(
"misc/foo", sysroot="/build/nami"
),
"/chromeos-overlay/misc/foo/foo-9999.ebuild",
)
def testFindEbuildForPackageReturnNone(self):
equery_output = "Cannot find ebuild for package 'foo'\n"
self.rc.AddCmdResult(
["equery", "--no-color", "--no-pipe", "which", "foo"],
stdout=equery_output,
returncode=1,
)
self.assertEqual(
portage_util.FindEbuildForPackage("foo", sysroot="/build/nami"),
None,
)
class FindPackageNamesForFilesTest(cros_test_lib.RunCommandTestCase):
"""Tests for FindPackageNamesForFiles."""
belongs_cmd = ["equery", "--no-color", "--no-pipe", "--quiet", "belongs"]
def testFindPackageNamesForFilesSimple(self):
self.rc.AddCmdResult(
self.belongs_cmd + ["/some/file"],
stdout="some-category/some-package-0.2-r2\n",
)
packages = portage_util.FindPackageNamesForFiles("/some/file")
expected = package_info.PackageInfo(
"some-category", "some-package", "0.2", 2
)
self.assertEqual(packages, [expected])
def testFindPackageNamesForFilesNoResults(self):
self.rc.AddCmdResult(self.belongs_cmd + ["/some/file"], stdout="")
packages = portage_util.FindPackageNamesForFiles("/some/file")
self.assertEqual(packages, [])
_EQUERY_OUTPUT_CORPUS = """
virtual/editor-0:
[ 0] virtual/editor-0
[ 1] app-editors/nano-4.2
[ 1] app-editors/emacs-26.1-r3
[ 1] app-editors/qemacs-0.4.1_pre20170225
[ 1] app-editors/vim-8.1.1486
[ 1] app-misc/mc-4.8.10
[ 1] sys-apps/busybox-1.29.3
[ 1] sys-apps/ed-1.14.2
"""
class DepTreeTest(cros_test_lib.TestCase):
"""Tests for GetDepTreeForPackage & parsing"""
def testParseDepTreeOutput(self):
expected = [
"virtual/editor-0",
"app-editors/nano-4.2",
"app-editors/emacs-26.1-r3",
"app-editors/qemacs-0.4.1_pre20170225",
"app-editors/vim-8.1.1486",
"app-misc/mc-4.8.10",
"sys-apps/busybox-1.29.3",
"sys-apps/ed-1.14.2",
]
self.assertEqual(
expected, portage_util._ParseDepTreeOutput(_EQUERY_OUTPUT_CORPUS)
)
_EMERGE_PRETEND_SDK_OUTPUT_CORPUS = """\
N f sys-devel/binutils 2.27.0-r23 to /var/empty/
R dev-python/sphinxcontrib-jsmath 1.0.1-r1
R virtual/perl-JSON-PP 2.273.0-r4
R sys-libs/readline 6.3_p8-r2
N sys-libs/readline 6.3_p8-r2 to /var/empty/
N app-shells/bash 4.3_p48-r3 to /var/empty/
N sys-libs/glibc 2.27-r18 to /var/empty/
"""
class PackageDependenciesTest(cros_test_lib.RunCommandTestCase):
"""Tests for GetPackageDependencies & parsing"""
def testParseDepTreeOutput(self):
expected = [
"sys-devel/binutils-2.27.0-r23",
"dev-python/sphinxcontrib-jsmath-1.0.1-r1",
"virtual/perl-JSON-PP-2.273.0-r4",
"sys-libs/readline-6.3_p8-r2",
"sys-libs/readline-6.3_p8-r2",
"app-shells/bash-4.3_p48-r3",
"sys-libs/glibc-2.27-r18",
]
self.rc.AddCmdResult(
partial_mock.Ignore(), stdout=_EMERGE_PRETEND_SDK_OUTPUT_CORPUS
)
self.assertEqual(
expected,
portage_util.GetPackageDependencies("target-chromium-os-sdk"),
)
class FindEbuildsForOverlaysTest(cros_test_lib.MockTempDirTestCase):
"""Tests for FindEbuildsForOverlays."""
def setUp(self):
file_layout = (
cros_test_lib.Directory("package1/bar1", ["bar1-1.0.ebuild"]),
cros_test_lib.Directory("package2/bar2", ["bar2-2.0.ebuild"]),
)
cros_test_lib.CreateOnDiskHierarchy(self.tempdir, file_layout)
def testFindEbuildsForOverlaysOutput(self):
mock_overlay_paths = [
self.tempdir / "package1" / "bar1",
self.tempdir / "package2" / "bar2",
]
expected_ebuilds = [
self.tempdir / "package1" / "bar1" / "bar1-1.0.ebuild",
self.tempdir / "package2" / "bar2" / "bar2-2.0.ebuild",
]
ebuilds = yield from portage_util.FindEbuildsForOverlays(
mock_overlay_paths
)
self.assertEqual(expected_ebuilds, ebuilds)
_EQUERY_DEPENDS_OUTPUT_CORPUS = """\
app-admin/perl-cleaner-2.20
app-shells/bash-completion-2.8-r1
chromeos-base/chromeos-base-1-r11
sys-apps/portage-3.0.21-r73
sys-devel/crossdev-20211027-r1
virtual/target-chromium-os-sdk-1-r219
"""
class GetReverseDependenciesTest(cros_test_lib.RunCommandTestCase):
"""Tests for GetReverseDependencies."""
def testGetReverseDependencies(self):
expected = [
package_info.parse("app-admin/perl-cleaner-2.20"),
package_info.parse("app-shells/bash-completion-2.8-r1"),
package_info.parse("chromeos-base/chromeos-base-1-r11"),
package_info.parse("sys-apps/portage-3.0.21-r73"),
package_info.parse("sys-devel/crossdev-20211027-r1"),
package_info.parse("virtual/target-chromium-os-sdk-1-r219"),
]
self.rc.AddCmdResult(
partial_mock.Ignore(), stdout=_EQUERY_DEPENDS_OUTPUT_CORPUS
)
result = portage_util.GetReverseDependencies(["app-shells/bash"])
self.assertEqual(expected, result)
def testGetReverseDependenciesNone(self):
self.rc.AddCmdResult(partial_mock.Ignore(), stdout="\n")
result = portage_util.GetReverseDependencies(["fake/package"])
self.assertEqual([], result)
def testGetReverseDependenciesValueError(self):
with self.assertRaises(ValueError) as e:
portage_util.GetReverseDependencies([])
self.assertEqual("Must provide at least one package.", e)
class RegenCacheTest(cros_test_lib.MockTempDirTestCase):
"""Tests for RegenCache."""
def testRegenCacheGenerateConfig(self):
overlay1 = cr.test.Overlay(f"{self.tempdir}/src/overlays/foo", "foo")
overlay2 = cr.test.Overlay(f"{self.tempdir}/src/overlays/bar", "bar")
overlay3 = cr.test.Overlay(f"{self.tempdir}/src/overlays/baz", "baz")
overlays = [overlay1.path, overlay2.path, overlay3.path]
self.PatchObject(portage_util, "FindOverlays", return_value=overlays)
mock_output = portage_util.generate_repositories_configuration()
correct_output = (
f"[foo]\nlocation = {self.tempdir}/src/overlays/foo\n"
f"[bar]\nlocation = {self.tempdir}/src/overlays/bar\n"
f"[baz]\nlocation = {self.tempdir}/src/overlays/baz\n"
)
self.assertEqual(mock_output, correct_output)
class RegenDependencyCacheTest(
cros_test_lib.RunCommandTestCase, cros_test_lib.LoggingTestCase
):
"""Tests for RegenDependencyCache."""
def testRegenDependencyCache(self):
with cros_test_lib.LoggingCapturer() as logs:
portage_util.RegenDependencyCache()
self.AssertLogsContain(logs, "Rebuilding Portage dependency cache.")
self.assertCommandContains(["parallel_emerge", "--regen", "--quiet"])
def testRegenDependencyCacheBoard(self):
portage_util.RegenDependencyCache(board="eve")
self.assertCommandContains(["--board=eve"])
def testRegenDependencyCacheSysroot(self):
portage_util.RegenDependencyCache(sysroot="/build/eve")
self.assertCommandContains(["--sysroot=/build/eve"])
def testRegenDependencyCacheJobs(self):
portage_util.RegenDependencyCache(jobs=10)
self.assertCommandContains(["--jobs=10"])