| # 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"]) |