# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # 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.""" from __future__ import print_function import cStringIO import os from chromite.cbuildbot import constants from chromite.lib import cros_test_lib from chromite.lib import git from chromite.lib import osutils from chromite.lib import portage_util MANIFEST = git.ManifestCheckout.Cached(constants.SOURCE_ROOT) # pylint: disable=protected-access class _Package(object): """Package helper class.""" def __init__(self, package): self.package = package class _DummyCommandResult(object): """Create mock RunCommand results.""" def __init__(self, output): # Members other than 'output' are expected to be unused, so # we omit them here. # # All shell output will be newline terminated; we add the # newline here for convenience. self.output = output + '\n' 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() { }""" _SINGLE_LINE_TEST = 'src_test() { echo "foo" }' 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) return fake_ebuild def testParseEBuildPath(self): """Test with ebuild with revision number.""" basedir = os.path.join(self.tempdir, 'cat', 'test_package') fake_ebuild_path = os.path.join(basedir, 'test_package-0.0.1-r1.ebuild') fake_ebuild = self._MakeFakeEbuild(fake_ebuild_path) self.assertEquals(fake_ebuild.category, 'cat') self.assertEquals(fake_ebuild.pkgname, 'test_package') self.assertEquals(fake_ebuild.version_no_rev, '0.0.1') self.assertEquals(fake_ebuild.current_revision, 1) self.assertEquals(fake_ebuild.version, '0.0.1-r1') self.assertEquals(fake_ebuild.package, 'cat/test_package') self.assertEquals(fake_ebuild._ebuild_path_no_version, os.path.join(basedir, 'test_package')) self.assertEquals(fake_ebuild.ebuild_path_no_revision, os.path.join(basedir, 'test_package-0.0.1')) self.assertEquals(fake_ebuild._unstable_ebuild_path, os.path.join(basedir, 'test_package-9999.ebuild')) self.assertEquals(fake_ebuild.ebuild_path, fake_ebuild_path) def testParseEBuildPathNoRevisionNumber(self): """Test with ebuild without revision number.""" 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.assertEquals(fake_ebuild.category, 'cat') self.assertEquals(fake_ebuild.pkgname, 'test_package') self.assertEquals(fake_ebuild.version_no_rev, '9999') self.assertEquals(fake_ebuild.current_revision, 0) self.assertEquals(fake_ebuild.version, '9999') self.assertEquals(fake_ebuild.package, 'cat/test_package') self.assertEquals(fake_ebuild._ebuild_path_no_version, os.path.join(basedir, 'test_package')) self.assertEquals(fake_ebuild.ebuild_path_no_revision, os.path.join(basedir, 'test_package-9999')) self.assertEquals(fake_ebuild._unstable_ebuild_path, os.path.join(basedir, 'test_package-9999.ebuild')) self.assertEquals(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=_DummyCommandResult(fake_hash)) test_hash = fake_ebuild.GetCommitId(self.tempdir) self.assertEquals(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.assertEquals(fake_ebuild.is_stable, stable) def testEBuildBlacklisted(self): """Test blacklisted 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.assertEquals(fake_ebuild.is_blacklisted, False) fake_ebuild = self._MakeFakeEbuild( fake_ebuild_path, fake_ebuild_content=['CROS_WORKON_BLACKLIST="1"\n']) self.assertEquals(fake_ebuild.is_blacklisted, True) 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).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) class ProjectAndPathTest(cros_test_lib.MockTempDirTestCase): """Project and Path related tests.""" def _MockParseWorkonVariables(self, fake_projects, fake_srcpaths, fake_localnames, fake_subdirs, fake_ebuild_contents): """Mock the necessary calls, call GetSourcePath().""" 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, subdir in zip(fake_localnames, fake_subdirs): if path == os.path.join(self.tempdir, localname, subdir): return False elif path == os.path.join(self.tempdir, 'platform', localname, subdir): return True raise Exception('unhandled path: %s' % path) def _FindCheckoutFromPath(path): """Mock function for manifest.FindCheckoutFromPath""" for project, localname, subdir in zip(fake_projects, fake_localnames, fake_subdirs): if path == os.path.join(self.tempdir, 'platform', localname, subdir): return {'name': project} 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) return ebuild.GetSourcePath(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_subdir = 'bar' fake_ebuild_contents = """ CROS_WORKON_PROJECT=%s CROS_WORKON_LOCALNAME=%s CROS_WORKON_SUBDIR=%s """ % (fake_project, fake_localname, fake_subdir) project, subdir = self._MockParseWorkonVariables( [fake_project], [], [fake_localname], [fake_subdir], fake_ebuild_contents) self.assertEquals(project, [fake_project]) self.assertEquals(subdir, [os.path.join( self.tempdir, 'platform', '%s/%s' % (fake_localname, fake_subdir))]) 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'] fake_subdirs = ['sub1', 'sub2', 'sub3'] # 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_SUBDIR=%s """ % (portage_util.EBuild.FormatBashArray(fake_projects), portage_util.EBuild.FormatBashArray(fake_localnames), portage_util.EBuild.FormatBashArray(fake_subdirs)) projects, subdirs = self._MockParseWorkonVariables( fake_projects, [], fake_localnames, fake_subdirs, fake_ebuild_contents) self.assertEquals(projects, fake_projects) fake_paths = [ os.path.realpath(os.path.join( self.tempdir, 'platform', '%s/%s' % (fake_localnames[i], fake_subdirs[i]))) for i in range(0, len(fake_projects)) ] self.assertEquals(subdirs, 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'] fake_subdirs = ['sub1', 'sub2', 'sub3'] # 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 CROS_WORKON_SUBDIR=%s """ % (portage_util.EBuild.FormatBashArray(fake_projects), portage_util.EBuild.FormatBashArray(fake_srcpaths), portage_util.EBuild.FormatBashArray(fake_localnames), portage_util.EBuild.FormatBashArray(fake_subdirs)) projects, subdirs = self._MockParseWorkonVariables( fake_projects, fake_srcpaths, fake_localnames, fake_subdirs, fake_ebuild_contents) self.assertEquals(projects, fake_projects) fake_paths = [] for srcpath, localname, subdir in zip( fake_srcpaths, fake_localnames, fake_subdirs): if srcpath: path = os.path.realpath(os.path.join( self.tempdir, 'src', srcpath)) else: path = os.path.realpath(os.path.join( self.tempdir, 'platform', '%s/%s' % (localname, subdir))) fake_paths.append(path) self.assertEquals(subdirs, fake_paths) class StubEBuild(portage_util.EBuild): """Test helper to StubEBuild.""" def __init__(self, path): super(StubEBuild, self).__init__(path) self.is_workon = True self.is_stable = True def _ReadEBuild(self, path): pass def GetCommitId(self, srcpath): id_map = { 'p1_path': 'my_id', 'p1_path1': 'my_id1', 'p1_path2': 'my_id2' } if srcpath in id_map: return id_map[srcpath] 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', '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'] _revved_ebuild = ('EAPI=2\n' 'CROS_WORKON_COMMIT="my_id"\n' 'CROS_WORKON_TREE="treehash"\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=("treehash1" "treehash2")\n' 'KEYWORDS="x86 arm amd64"\n' 'src_unpack(){}\n') def setUp(self): self.overlay = os.path.join(self.tempdir, 'overlay') package_name = os.path.join(self.overlay, 'category/test_package/test_package-0.0.1') ebuild_path = package_name + '-r1.ebuild' self.m_ebuild = StubEBuild(ebuild_path) self.revved_ebuild_path = package_name + '-r2.ebuild' self._m_file = cStringIO.StringIO() 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': 'treehash1', 'p1_path2': 'treehash2', 'p1_path': 'treehash', }.get(path) def _RunGit(cwd, cmd): """Mock function for portage_util.EBuild._RunGit""" 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, 'GetSourcePath') if multi: source_mock.return_value = (['fake_project1', 'fake_project2'], ['p1_path1', 'p1_path2']) else: source_mock.return_value = (['fake_project1'], ['p1_path']) 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 RevWorkOnEBuild(self, *args, **kwargs): """Thin helper wrapper to call the function under test. Returns: (result, revved_ebuild) where result is the result from the called function, and revved_ebuild is the content of the revved ebuild. """ m_file = cStringIO.StringIO() kwargs['redirect_file'] = m_file result = self.m_ebuild.RevWorkOnEBuild(*args, **kwargs) return result, m_file.getvalue() def testRevWorkOnEBuild(self): """Test Uprev of a single project ebuild.""" self.createRevWorkOnMocks(self._mock_ebuild, rev=True) result, revved_ebuild = self.RevWorkOnEBuild(self.tempdir, MANIFEST) self.assertEqual(result, 'category/test_package-0.0.1-r2') self.assertEqual(self._revved_ebuild, revved_ebuild) self.assertExists(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, revved_ebuild = self.RevWorkOnEBuild(self.tempdir, MANIFEST) self.assertEqual(result, 'category/test_package-0.0.1-r2') self.assertEqual(self._revved_ebuild_multi, revved_ebuild) self.assertExists(self.revved_ebuild_path) def testRevUnchangedEBuild(self): self.createRevWorkOnMocks(self._mock_ebuild, rev=False) self.PatchObject(portage_util.filecmp, 'cmp', return_value=True) result, revved_ebuild = self.RevWorkOnEBuild(self.tempdir, MANIFEST) self.assertEqual(result, None) self.assertEqual(self._revved_ebuild, revved_ebuild) 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, revved_ebuild = self.RevWorkOnEBuild(self.tempdir, MANIFEST) self.assertEqual(result, 'category/test_package-0.0.1-r1') self.assertEqual(self._revved_ebuild, revved_ebuild) self.assertExists(self.revved_ebuild_path) 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', 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, 'GetSourcePath', return_value=(None, [])) self.PatchObject(portage_util.EBuild, '_RunCommand', return_value='1122') 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, 'GetSourcePath', return_value=(None, [])) run = self.PatchObject(portage_util.EBuild, '_RunCommand') # Reject no output. run.return_value = '' self.assertRaises(SystemExit, self.m_ebuild.GetVersion, None, None, '1234') # Sanity check. self.assertEqual(exists.call_count, 1) exists.reset_mock() # Reject simple output. run.return_value = '\n' self.assertRaises(SystemExit, 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, 'GetSourcePath', return_value=(None, [])) self.PatchObject(portage_util.EBuild, '_RunCommand', return_value='999999') 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, 'GetSourcePath', return_value=(None, [])) self.PatchObject(portage_util.EBuild, '_RunCommand', return_value='abcd') self.assertRaises(ValueError, self.m_ebuild.GetVersion, None, None, '1234') # Sanity check. self.assertEqual(exists.call_count, 1) def testUpdateEBuildRecovery(self): """Make sure 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, {'VAR': 'a'}, redirect_file=1234) assert False, 'this should have thrown an exception ...' except Exception: pass # Second run: it should pass normally. portage_util.EBuild.UpdateEBuild(ebuild, {'VAR': 'b'}) 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.MissingOverlayException, 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, BRICK = ( 'fake!board', 'pub-priv-board', 'pub-priv-board_variant', 'pub-only-board', 'pub2-only-board', 'priv-only-board', 'brick', ) 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), )), )), D('projects', ( D(self.BRICK, ( D('packages', overlay_files), 'config.json', )), )), ) 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: settings['masters'] += board.split('_')[0] osutils.WriteFile(conf_path % settings, conf_data % settings) # Seed the brick, with PUB_ONLY overlay as its primary overlay. osutils.WriteFile(os.path.join(self.tempdir, 'projects', self.BRICK, 'packages', 'metadata', 'layout.conf'), 'repo-name = %s\nmasters = %s' % (self.BRICK, self.PUB_ONLY)) 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: 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.BRICK): 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.MissingOverlayException: d[o] = [] self._no_overlays = not bool(any(d.values())) def testMissingPrimaryOverlay(self): """Test what happens when a primary overlay is missing. If the overlay doesn't exist, FindOverlays should throw a MissingOverlayException. """ self.assertRaises(portage_util.MissingOverlayException, portage_util.FindPrimaryOverlay, self.BOTH, self.FAKE, self.tempdir) def testDuplicates(self): """Verify that no duplicate overlays are returned.""" for d in self.overlays.itervalues(): for overlays in d.itervalues(): self.assertEqual(len(overlays), len(set(overlays))) def testOverlaysExist(self): """Verify that all overlays returned actually exist on disk.""" for d in self.overlays.itervalues(): for overlays in d.itervalues(): 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 or b == self.BRICK: 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.itervalues())) 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 testPrimaryOverlays(self): """Verify that boards have a primary overlay. Further, the only difference between public boards are the primary overlay which should be listed last. """ primary = portage_util.FindPrimaryOverlay( self.BOTH, self.PUB_ONLY, self.tempdir) self.assertIn(primary, self.overlays[self.PUB_ONLY][self.BOTH]) self.assertNotIn(primary, self.overlays[self.PUB2_ONLY][self.BOTH]) self.assertEqual(primary, self.overlays[self.PUB_ONLY][self.PUBLIC][-1]) self.assertEqual(self.overlays[self.PUB_ONLY][self.PUBLIC][:-1], self.overlays[self.PUB2_ONLY][self.PUBLIC][:-1]) self.assertNotEqual(self.overlays[self.PUB_ONLY][self.PUBLIC][-1], self.overlays[self.PUB2_ONLY][self.PUBLIC][-1]) def testBrickPrimaryOverlay(self): """Verify that a brick's stacking correctly picks up its primary overlay.""" primary = portage_util.FindPrimaryOverlay( self.BOTH, self.BRICK, self.tempdir) self.assertIn(primary, self.overlays[self.PUB_ONLY][self.BOTH]) self.assertEqual(primary, self.overlays[self.PUB_ONLY][self.PUBLIC][-1]) 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 = [x for x in 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!') class BuildEBuildDictionaryTest(cros_test_lib.MockTempDirTestCase): """Tests of the EBuild Dictionary.""" def setUp(self): self.overlay = self.tempdir self.uprev_candidate_mock = self.PatchObject( portage_util, '_FindUprevCandidates', side_effect=BuildEBuildDictionaryTest._FindUprevCandidateMock) self.overlays = {self.overlay: []} def _CreatePackage(self, name, blacklisted=False): """Helper that creates an ebuild.""" package_path = os.path.join(self.overlay, name, 'test_package-0.0.1.ebuild') content = 'CROS_WORKON_BLACKLIST=1' if blacklisted else '' osutils.WriteFile(package_path, content, makedirs=True) @staticmethod def _FindUprevCandidateMock(files, allow_blacklisted=False): """Mock for the FindUprevCandidateMock function. Simplified implementation of FindUprevCandidate: consider an ebuild worthy of uprev if |allow_blacklisted| is set or the ebuild is not blacklisted. """ for f in files: if (f.endswith('.ebuild') and (not 'CROS_WORKON_BLACKLIST=1' in osutils.ReadFile(f) or allow_blacklisted)): 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, packages): """Succeeds iff the packages discovered were packages.""" self.assertEquals(len(self.overlays), 1) self.assertEquals([p.package for p in self.overlays[self.overlay]], packages) def testWantedPackage(self): """Test that we can find a specific package.""" package_name = 'chromeos-base/mypackage' self._CreatePackage(package_name) portage_util.BuildEBuildDictionary(self.overlays, False, [package_name]) self._assertFoundPackages([package_name]) def testUnwantedPackage(self): """Test that we find only the packages we want.""" portage_util.BuildEBuildDictionary(self.overlays, False, []) self._assertFoundPackages([]) 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) portage_util.BuildEBuildDictionary(self.overlays, True, []) self._assertFoundPackages([package_name]) def testUnknownPackage(self): """Test that _FindUprevCandidates is only called if the CP matches.""" self._CreatePackage('chromeos-base/package_name') portage_util.BuildEBuildDictionary(self.overlays, False, ['chromeos-base/other_package']) self.assertFalse(self.uprev_candidate_mock.called) self._assertFoundPackages([]) def testBlacklistedPackagesIgnoredByDefault(self): """Test that blacklisted packages are ignored by default.""" package_name = 'chromeos-base/blacklisted_package' self._CreatePackage(package_name, blacklisted=True) portage_util.BuildEBuildDictionary(self.overlays, False, [package_name]) self._assertFoundPackages([]) def testBlacklistedPackagesAllowed(self): """Test that we can find blacklisted packages with |allow_blacklisted|.""" package_name = 'chromeos-base/blacklisted_package' self._CreatePackage(package_name, blacklisted=True) portage_util.BuildEBuildDictionary(self.overlays, False, [package_name], allow_blacklisted=True) self._assertFoundPackages([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.assertEquals(components, portage_util.SplitEbuildPath(path)) def testSplitPV(self): """Test splitting PVs into package and version components.""" pv = 'bar-1.2.3_rc1-r5' package, version_no_rev, rev = tuple(pv.split('-')) split_pv = portage_util.SplitPV(pv) self.assertEquals(split_pv.pv, pv) self.assertEquals(split_pv.package, package) self.assertEquals(split_pv.version_no_rev, version_no_rev) self.assertEquals(split_pv.rev, rev) self.assertEquals(split_pv.version, '%s-%s' % (version_no_rev, rev)) def testSplitCPV(self): """Test splitting CPV into components.""" cpv = 'foo/bar-4.5.6_alpha-r6' cat, pv = cpv.split('/', 1) split_pv = portage_util.SplitPV(pv) split_cpv = portage_util.SplitCPV(cpv) self.assertEquals(split_cpv.category, cat) for k, v in split_pv._asdict().iteritems(): self.assertEquals(getattr(split_cpv, k), v) def testFindWorkonProjects(self): """Test if we can find the list of workon projects.""" ply_image = 'media-gfx/ply-image' ply_image_project = 'chromiumos/third_party/ply-image' this = 'chromeos-base/chromite' this_project = 'chromiumos/chromite' matches = [ ([ply_image], set([ply_image_project])), ([this], set([this_project])), ([ply_image, this], set([ply_image_project, this_project])) ] if portage_util.FindOverlays(constants.BOTH_OVERLAYS): for packages, projects in matches: self.assertEquals(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, 'var/db/pkg') os.makedirs(fake_pkgdb_path) for cat, pkgs in self.fake_pkgdb.iteritems(): 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 = portage_util.SplitPV(pkg) key = '%s/%s' % (cat, pv.package) self.fake_packages.append((key, pv.version)) # 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.assertEquals(self.fake_packages, packages) 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.assertEquals(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.assertEquals(fake_files, lst) class InstalledPackageTest(cros_test_lib.TempDirTestCase): """InstalledPackage class tests outside a PortageDB.""" def setUp(self): osutils.WriteFile(os.path.join(self.tempdir, 'package-1.ebuild'), 'EAPI=1') osutils.WriteFile(os.path.join(self.tempdir, 'PF'), 'package-1') osutils.WriteFile(os.path.join(self.tempdir, 'CATEGORY'), 'category-1') def testOutOfDBPackage(self): """Tests an InstalledPackage instance can be created without a PortageDB.""" pkg = portage_util.InstalledPackage(None, self.tempdir) self.assertEquals('package-1', pkg.pf) self.assertEquals('category-1', pkg.category) def testIncompletePackage(self): """Tests an incomplete or otherwise invalid package raises an exception.""" # No package name is provided. os.unlink(os.path.join(self.tempdir, 'PF')) self.assertRaises(portage_util.PortageDBException, 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.assertEquals('package-1', pkg.pf)