| # Copyright 2015 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. |
| |
| """Tests for workon_helper.""" |
| |
| from __future__ import print_function |
| |
| import collections |
| import os |
| |
| from chromite.lib import cros_test_lib |
| from chromite.lib import git |
| from chromite.lib import portage_util |
| from chromite.lib import sysroot_lib |
| from chromite.lib import osutils |
| from chromite.lib import workon_helper |
| |
| |
| BOARD = 'this_is_a_board_name' |
| |
| WORKON_ONLY_ATOM = 'sys-apps/my-package' |
| VERSIONED_WORKON_ATOM = 'sys-apps/versioned-package' |
| NOT_WORKON_ATOM = 'sys-apps/not-workon-package' |
| |
| HOST_ATOM = 'host-apps/my-package' |
| |
| WORKED_ON_PATTERN = '=%s-9999' |
| MASKED_PATTERN = '<%s-9999' |
| |
| OVERLAY_ROOT_DIR = 'overlays' |
| BOARD_OVERLAY_DIR = 'overlay-' + BOARD |
| HOST_OVERLAY_DIR = 'overlay-host' |
| |
| |
| InstalledPackageMock = collections.namedtuple('InstalledPackage', |
| ('category', 'package')) |
| |
| |
| class WorkonHelperTest(cros_test_lib.MockTempDirTestCase): |
| """Tests for chromite.lib.workon_helper.""" |
| |
| def _MakeFakeEbuild(self, overlay, atom, version, is_workon=True): |
| """Makes fake ebuilds with minimal real content. |
| |
| Args: |
| overlay: overlay to put this ebuild in. |
| atom: 'category/package' string in the familiar portage sense. |
| version: version suffix for the ebuild (e.g. '9999'). |
| is_workon: True iff this should be a workon-able package |
| (i.e. inherits cros-workon). |
| """ |
| category, package = atom.split('/', 1) |
| ebuild_path = os.path.join(self._mock_srcdir, OVERLAY_ROOT_DIR, overlay, |
| category, package, |
| '%s-%s.ebuild' % (package, version)) |
| content = 'KEYWORDS="~*"\n' |
| if is_workon: |
| content += 'inherit cros-workon\n' |
| osutils.WriteFile(ebuild_path, content, makedirs=True) |
| if atom not in self._valid_atoms: |
| self._valid_atoms[atom] = ebuild_path |
| |
| def _MockFindOverlays(self, sysroot): |
| """Mocked out version of portage_util.FindOverlays(). |
| |
| Args: |
| sysroot: path to sysroot. |
| |
| Returns: |
| List of paths to overlays. |
| """ |
| if sysroot == '/': |
| return [os.path.join(self._overlay_root, HOST_OVERLAY_DIR)] |
| return [os.path.join(self._overlay_root, BOARD_OVERLAY_DIR)] |
| |
| def _MockFindEbuildForPackage(self, package, _board=None, **_kwargs): |
| """Mocked out version of portage_util.FindEbuildForPackage(). |
| |
| Args: |
| package: complete atom string. |
| _board: ignored, see documentation in portage_util. We intentionally |
| create atoms with different names for hosts/boards so that we can |
| ignore this distinction here. |
| _kwargs: ignored, see documentation in portage_util. |
| |
| Returns: |
| An ebuild if we have previously created this atom. |
| """ |
| return self._valid_atoms.get(package, None) |
| |
| def setUp(self): |
| """Set up a test environment.""" |
| self._valid_atoms = dict() |
| self._mock_srcdir = os.path.join(self.tempdir, 'src') |
| workon_dir = workon_helper.GetWorkonPath(source_root=self._mock_srcdir) |
| self._sysroot = os.path.join(self.tempdir, 'sysroot') |
| osutils.SafeMakedirs(self._sysroot) |
| osutils.SafeMakedirs(self._mock_srcdir) |
| for system in ('host', BOARD): |
| osutils.Touch(os.path.join(workon_dir, system), makedirs=True) |
| osutils.Touch(os.path.join(workon_dir, system + '.mask'), makedirs=True) |
| self._overlay_root = os.path.join(self._mock_srcdir, OVERLAY_ROOT_DIR) |
| # Make a bunch of packages to work on. |
| self._MakeFakeEbuild(BOARD_OVERLAY_DIR, WORKON_ONLY_ATOM, '9999') |
| self._MakeFakeEbuild(BOARD_OVERLAY_DIR, VERSIONED_WORKON_ATOM, '9999') |
| self._MakeFakeEbuild(BOARD_OVERLAY_DIR, VERSIONED_WORKON_ATOM, '0.0.1-r1') |
| self._MakeFakeEbuild(BOARD_OVERLAY_DIR, NOT_WORKON_ATOM, '0.0.1-r1', |
| is_workon=False) |
| self._MakeFakeEbuild(HOST_OVERLAY_DIR, HOST_ATOM, '9999') |
| # Patch the modules interfaces to the rest of the world. |
| self.PatchObject(portage_util, 'FindEbuildForPackage', |
| self._MockFindEbuildForPackage) |
| |
| # Assume only versioned-packages is installed. |
| self.PatchObject( |
| portage_util.PortageDB, 'InstalledPackages', |
| return_value=[InstalledPackageMock('sys-apps', 'versioned-package')]) |
| # This basically turns off behavior related to adding repositories to |
| # minilayouts. |
| self.PatchObject(git.ManifestCheckout, 'IsFullManifest', return_value=True) |
| self.PatchObject( |
| portage_util, 'GetRepositoryForEbuild', return_value=( |
| portage_util.RepositoryInfoTuple(srcdir=self._mock_srcdir, |
| project='workon-project'), |
| ) |
| ) |
| # We do a lot of work as root. Pretend to be root so that we never have to |
| # call sudo. |
| self.PatchObject(os, 'getuid', return_value=0) |
| |
| def CreateHelper(self, host=False): |
| """Creates and returns a WorkonHelper object. |
| |
| Args: |
| host: If True, create the WorkonHelper for the host. |
| """ |
| if host: |
| overlay = os.path.join(self._overlay_root, HOST_OVERLAY_DIR) |
| name = 'host' |
| else: |
| overlay = os.path.join(self._overlay_root, BOARD_OVERLAY_DIR) |
| name = BOARD |
| |
| # Setup the sysroots. |
| sysroot_lib.Sysroot(self._sysroot).WriteConfig( |
| 'ARCH="amd64"\nPORTDIR_OVERLAY="%s"' % overlay) |
| |
| # Create helpers for the host or board. |
| return workon_helper.WorkonHelper( |
| self._sysroot, name, src_root=self._mock_srcdir) |
| |
| def assertWorkingOn(self, atoms, system=BOARD): |
| """Assert that the workon/mask files mention the given atoms. |
| |
| Args: |
| atoms: list of atom strings (e.g. ['sys-apps/dbus', 'foo-cat/bar']). |
| system: string system to consider (either 'host' or a board name). |
| """ |
| workon_path = workon_helper.GetWorkonPath( |
| source_root=self._mock_srcdir, sub_path=system) |
| self.assertEqual(sorted([WORKED_ON_PATTERN % atom for atom in atoms]), |
| sorted(osutils.ReadFile(workon_path).splitlines())) |
| mask_path = workon_path + '.mask' |
| self.assertEqual(sorted([MASKED_PATTERN % atom for atom in atoms]), |
| sorted(osutils.ReadFile(mask_path).splitlines())) |
| |
| def testShouldDetectBoardNotSetUp(self): |
| """Check that we complain if a board has not been previously setup.""" |
| with self.assertRaises(workon_helper.WorkonError): |
| workon_helper.WorkonHelper(os.path.join(self.tempdir, 'nonexistent'), |
| 'this-board-is-not-setup.', |
| src_root=self._mock_srcdir) |
| |
| def testShouldRegenerateSymlinks(self): |
| """Check that the symlinks are regenerated when using a new sysroot.""" |
| # pylint: disable=protected-access |
| helper = self.CreateHelper() |
| workon_link = helper._unmasked_symlink |
| |
| # The link exists after starting a package. |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertTrue(os.path.exists(workon_link)) |
| |
| # The link exists after recreating a sysroot. |
| osutils.RmDir(self._sysroot) |
| osutils.SafeMakedirs(self._sysroot) |
| helper = self.CreateHelper() |
| self.assertTrue(os.path.exists(workon_link)) |
| |
| # The link exists when no packages are worked on. |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertTrue(os.path.exists(workon_link)) |
| |
| def testCanStartSingleAtom(self): |
| """Check that we can mark a single atom as being worked on.""" |
| helper = self.CreateHelper() |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertWorkingOn([WORKON_ONLY_ATOM]) |
| |
| def testCanStartMultipleAtoms(self): |
| """Check that we can mark a multiple atoms as being worked on.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM) |
| helper.StartWorkingOnPackages(expected_atoms) |
| self.assertWorkingOn(expected_atoms) |
| |
| def testCanStartAtomsWithAll(self): |
| """Check that we can mark all possible workon atoms as started.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM) |
| helper.StartWorkingOnPackages([], use_all=True) |
| self.assertWorkingOn(expected_atoms) |
| |
| def testCanStartAtomsWithWorkonOnly(self): |
| """Check that we can start atoms that have only a cros-workon ebuild.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM,) |
| helper.StartWorkingOnPackages([], use_workon_only=True) |
| self.assertWorkingOn(expected_atoms) |
| |
| def testCannotStartAtomTwice(self): |
| """Check that starting an atom twice has no effect.""" |
| helper = self.CreateHelper() |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertWorkingOn([WORKON_ONLY_ATOM]) |
| |
| def testCanStopSingleAtom(self): |
| """Check that we can stop a previously started atom.""" |
| helper = self.CreateHelper() |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertWorkingOn([WORKON_ONLY_ATOM]) |
| helper.StopWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertWorkingOn([]) |
| |
| def testCanStopMultipleAtoms(self): |
| """Check that we can stop multiple previously worked on atoms.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM) |
| helper.StartWorkingOnPackages(expected_atoms) |
| self.assertWorkingOn(expected_atoms) |
| helper.StopWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertWorkingOn([VERSIONED_WORKON_ATOM]) |
| helper.StopWorkingOnPackages([VERSIONED_WORKON_ATOM]) |
| self.assertWorkingOn([]) |
| # Now do it all at once. |
| helper.StartWorkingOnPackages(expected_atoms) |
| self.assertWorkingOn(expected_atoms) |
| helper.StopWorkingOnPackages(expected_atoms) |
| self.assertWorkingOn([]) |
| |
| def testCanStopAtomsWithAll(self): |
| """Check that we can stop all worked on atoms.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM) |
| helper.StartWorkingOnPackages(expected_atoms) |
| helper.StopWorkingOnPackages([], use_all=True) |
| self.assertWorkingOn([]) |
| |
| def testCanStopAtomsWithWorkonOnly(self): |
| """Check that we can stop all workon only atoms.""" |
| helper = self.CreateHelper() |
| expected_atoms = (WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM) |
| helper.StartWorkingOnPackages(expected_atoms) |
| helper.StopWorkingOnPackages([], use_workon_only=True) |
| self.assertWorkingOn([VERSIONED_WORKON_ATOM]) |
| |
| def testShouldDetectUnknownAtom(self): |
| """Check that we reject requests to work on unknown atoms.""" |
| with self.assertRaises(workon_helper.WorkonError): |
| helper = self.CreateHelper() |
| helper.StopWorkingOnPackages(['sys-apps/not-a-thing']) |
| |
| def testCanListAllWorkedOnAtoms(self): |
| """Check that we can list all worked on atoms across boards.""" |
| helper = self.CreateHelper() |
| self.assertEqual(dict(), |
| workon_helper.ListAllWorkedOnAtoms( |
| src_root=self._mock_srcdir)) |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertEqual({BOARD: [WORKON_ONLY_ATOM]}, |
| workon_helper.ListAllWorkedOnAtoms( |
| src_root=self._mock_srcdir)) |
| host_helper = self.CreateHelper(host=True) |
| host_helper.StartWorkingOnPackages([HOST_ATOM]) |
| self.assertEqual({BOARD: [WORKON_ONLY_ATOM], 'host': [HOST_ATOM]}, |
| workon_helper.ListAllWorkedOnAtoms( |
| src_root=self._mock_srcdir)) |
| |
| def testCanListWorkedOnAtoms(self): |
| """Check that we can list the atoms we're currently working on.""" |
| helper = self.CreateHelper() |
| self.assertEqual(helper.ListAtoms(), []) |
| helper.StartWorkingOnPackages([WORKON_ONLY_ATOM]) |
| self.assertEqual(helper.ListAtoms(), [WORKON_ONLY_ATOM]) |
| |
| def testCanListAtomsWithAll(self): |
| """Check that we can list all possible atoms to work on.""" |
| helper = self.CreateHelper() |
| self.assertEqual(sorted(helper.ListAtoms(use_all=True)), |
| sorted([WORKON_ONLY_ATOM, VERSIONED_WORKON_ATOM])) |
| |
| def testCanListAtomsWithWorkonOnly(self): |
| """Check that we can list all workon only atoms.""" |
| helper = self.CreateHelper() |
| self.assertEqual(helper.ListAtoms(use_workon_only=True), |
| [WORKON_ONLY_ATOM]) |
| |
| def testCanRunCommand(self): |
| """Test that we can run a command in package source directories.""" |
| helper = self.CreateHelper() |
| file_name = 'foo' |
| file_path = os.path.join(self._mock_srcdir, file_name) |
| self.assertNotExists(file_path) |
| helper.RunCommandInPackages([WORKON_ONLY_ATOM], 'touch %s' % file_name) |
| self.assertExists(file_path) |
| |
| def testInstalledWorkonAtoms(self): |
| """Test that we can list all the cros workon atoms that are installed.""" |
| helper = self.CreateHelper() |
| self.assertEqual(set([VERSIONED_WORKON_ATOM]), |
| helper.InstalledWorkonAtoms()) |