blob: 8b3271e069a03bf42c5012c35ad811c4f3428811 [file] [log] [blame]
# 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())