init: remove package_testing global
- refactor the way test dependencies are passed to the concretizer - remove global state - update tests
This commit is contained in:
parent
bc9f5f084f
commit
47dc96224d
@ -43,11 +43,6 @@
|
|||||||
tty.die('while initializing Spack RepoPath:', e.message)
|
tty.die('while initializing Spack RepoPath:', e.message)
|
||||||
|
|
||||||
|
|
||||||
#: Needed for test dependencies
|
|
||||||
from spack.package_prefs import PackageTesting
|
|
||||||
package_testing = PackageTesting()
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# When packages call 'from spack import *', we import a set of things that
|
# When packages call 'from spack import *', we import a set of things that
|
||||||
# should be useful for builds.
|
# should be useful for builds.
|
||||||
|
@ -139,14 +139,15 @@ def parse_specs(args, **kwargs):
|
|||||||
"""
|
"""
|
||||||
concretize = kwargs.get('concretize', False)
|
concretize = kwargs.get('concretize', False)
|
||||||
normalize = kwargs.get('normalize', False)
|
normalize = kwargs.get('normalize', False)
|
||||||
|
tests = kwargs.get('tests', False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
specs = spack.spec.parse(args)
|
specs = spack.spec.parse(args)
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
if concretize:
|
if concretize:
|
||||||
spec.concretize() # implies normalize
|
spec.concretize(tests=tests) # implies normalize
|
||||||
elif normalize:
|
elif normalize:
|
||||||
spec.normalize()
|
spec.normalize(tests=tests)
|
||||||
|
|
||||||
return specs
|
return specs
|
||||||
|
|
||||||
|
@ -50,11 +50,6 @@ def setup_parser(subparser):
|
|||||||
cd_group = subparser.add_mutually_exclusive_group()
|
cd_group = subparser.add_mutually_exclusive_group()
|
||||||
arguments.add_common_arguments(cd_group, ['clean', 'dirty'])
|
arguments.add_common_arguments(cd_group, ['clean', 'dirty'])
|
||||||
|
|
||||||
subparser.add_argument(
|
|
||||||
'--run-tests', action='store_true', dest='run_tests',
|
|
||||||
help="run package level tests during installation"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def bootstrap(parser, args, **kwargs):
|
def bootstrap(parser, args, **kwargs):
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
@ -62,7 +57,6 @@ def bootstrap(parser, args, **kwargs):
|
|||||||
'keep_stage': args.keep_stage,
|
'keep_stage': args.keep_stage,
|
||||||
'install_deps': 'dependencies',
|
'install_deps': 'dependencies',
|
||||||
'make_jobs': args.jobs,
|
'make_jobs': args.jobs,
|
||||||
'run_tests': args.run_tests,
|
|
||||||
'verbose': args.verbose,
|
'verbose': args.verbose,
|
||||||
'dirty': args.dirty
|
'dirty': args.dirty
|
||||||
})
|
})
|
||||||
|
@ -202,14 +202,16 @@ def install(parser, args, **kwargs):
|
|||||||
reporter.filename = args.log_file
|
reporter.filename = args.log_file
|
||||||
|
|
||||||
specs = spack.cmd.parse_specs(args.package)
|
specs = spack.cmd.parse_specs(args.package)
|
||||||
|
tests = False
|
||||||
if args.test == 'all' or args.run_tests:
|
if args.test == 'all' or args.run_tests:
|
||||||
spack.package_testing.test_all()
|
tests = True
|
||||||
elif args.test == 'root':
|
elif args.test == 'root':
|
||||||
for spec in specs:
|
tests = [spec.name for spec in specs]
|
||||||
spack.package_testing.test(spec.name)
|
kwargs['tests'] = tests
|
||||||
|
|
||||||
try:
|
try:
|
||||||
specs = spack.cmd.parse_specs(args.package, concretize=True)
|
specs = spack.cmd.parse_specs(
|
||||||
|
args.package, concretize=True, tests=tests)
|
||||||
except SpackError as e:
|
except SpackError as e:
|
||||||
reporter.concretization_report(e.message)
|
reporter.concretization_report(e.message)
|
||||||
raise
|
raise
|
||||||
|
@ -1380,6 +1380,7 @@ def do_install(self,
|
|||||||
make_jobs=None,
|
make_jobs=None,
|
||||||
fake=False,
|
fake=False,
|
||||||
explicit=False,
|
explicit=False,
|
||||||
|
tests=False,
|
||||||
dirty=None,
|
dirty=None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""Called by commands to install a package and its dependencies.
|
"""Called by commands to install a package and its dependencies.
|
||||||
@ -1405,6 +1406,8 @@ def do_install(self,
|
|||||||
fake (bool): Don't really build; install fake stub files instead.
|
fake (bool): Don't really build; install fake stub files instead.
|
||||||
explicit (bool): True if package was explicitly installed, False
|
explicit (bool): True if package was explicitly installed, False
|
||||||
if package was implicitly installed (as a dependency).
|
if package was implicitly installed (as a dependency).
|
||||||
|
tests (bool or list or set): False to run no tests, True to test
|
||||||
|
all packages, or a list of package names to run tests for some
|
||||||
dirty (bool): Don't clean the build environment before installing.
|
dirty (bool): Don't clean the build environment before installing.
|
||||||
force (bool): Install again, even if already installed.
|
force (bool): Install again, even if already installed.
|
||||||
"""
|
"""
|
||||||
@ -1435,7 +1438,6 @@ def do_install(self,
|
|||||||
# is installed
|
# is installed
|
||||||
if keep_stage is False:
|
if keep_stage is False:
|
||||||
self.stage.destroy()
|
self.stage.destroy()
|
||||||
|
|
||||||
return self._update_explicit_entry_in_db(rec, explicit)
|
return self._update_explicit_entry_in_db(rec, explicit)
|
||||||
|
|
||||||
self._do_install_pop_kwargs(kwargs)
|
self._do_install_pop_kwargs(kwargs)
|
||||||
@ -1454,6 +1456,7 @@ def do_install(self,
|
|||||||
skip_patch=skip_patch,
|
skip_patch=skip_patch,
|
||||||
verbose=verbose,
|
verbose=verbose,
|
||||||
make_jobs=make_jobs,
|
make_jobs=make_jobs,
|
||||||
|
tests=tests,
|
||||||
dirty=dirty,
|
dirty=dirty,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
@ -1470,8 +1473,9 @@ def do_install(self,
|
|||||||
tty.msg('No binary for %s found: installing from source'
|
tty.msg('No binary for %s found: installing from source'
|
||||||
% self.name)
|
% self.name)
|
||||||
|
|
||||||
# Set run_tests flag before starting build.
|
# Set run_tests flag before starting build
|
||||||
self.run_tests = spack.package_testing.check(self.name)
|
self.run_tests = (tests is True or
|
||||||
|
tests and self.name in tests)
|
||||||
|
|
||||||
# Set parallelism before starting build.
|
# Set parallelism before starting build.
|
||||||
self.make_jobs = make_jobs
|
self.make_jobs = make_jobs
|
||||||
@ -1555,6 +1559,11 @@ def build_process():
|
|||||||
# preserve verbosity across runs
|
# preserve verbosity across runs
|
||||||
return echo
|
return echo
|
||||||
|
|
||||||
|
# hook that allow tests to inspect this Package before installation
|
||||||
|
# see unit_test_check() docs.
|
||||||
|
if not self.unit_test_check():
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create the install prefix and fork the build process.
|
# Create the install prefix and fork the build process.
|
||||||
if not os.path.exists(self.prefix):
|
if not os.path.exists(self.prefix):
|
||||||
@ -1594,6 +1603,23 @@ def build_process():
|
|||||||
# check the filesystem for it.
|
# check the filesystem for it.
|
||||||
self.stage.created = False
|
self.stage.created = False
|
||||||
|
|
||||||
|
def unit_test_check(self):
|
||||||
|
"""Hook for unit tests to assert things about package internals.
|
||||||
|
|
||||||
|
Unit tests can override this function to perform checks after
|
||||||
|
``Package.install`` and all post-install hooks run, but before
|
||||||
|
the database is updated.
|
||||||
|
|
||||||
|
The overridden function may indicate that the install procedure
|
||||||
|
should terminate early (before updating the database) by
|
||||||
|
returning ``False`` (or any value such that ``bool(result)`` is
|
||||||
|
``False``).
|
||||||
|
|
||||||
|
Return:
|
||||||
|
(bool): ``True`` to continue, ``False`` to skip ``install()``
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def check_for_unfinished_installation(
|
def check_for_unfinished_installation(
|
||||||
self, keep_prefix=False, restage=False):
|
self, keep_prefix=False, restage=False):
|
||||||
"""Check for leftover files from partially-completed prior install to
|
"""Check for leftover files from partially-completed prior install to
|
||||||
|
@ -204,25 +204,6 @@ def preferred_variants(cls, pkg_name):
|
|||||||
if name in pkg.variants)
|
if name in pkg.variants)
|
||||||
|
|
||||||
|
|
||||||
class PackageTesting(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.packages_to_test = set()
|
|
||||||
self._test_all = False
|
|
||||||
|
|
||||||
def test(self, package_name):
|
|
||||||
self.packages_to_test.add(package_name)
|
|
||||||
|
|
||||||
def test_all(self):
|
|
||||||
self._test_all = True
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self._test_all = False
|
|
||||||
self.packages_to_test.clear()
|
|
||||||
|
|
||||||
def check(self, package_name):
|
|
||||||
return self._test_all or (package_name in self.packages_to_test)
|
|
||||||
|
|
||||||
|
|
||||||
def spec_externals(spec):
|
def spec_externals(spec):
|
||||||
"""Return a list of external specs (w/external directory path filled in),
|
"""Return a list of external specs (w/external directory path filled in),
|
||||||
one for each known external installation."""
|
one for each known external installation."""
|
||||||
|
@ -1808,10 +1808,14 @@ def feq(cfield, sfield):
|
|||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def concretize(self):
|
def concretize(self, tests=False):
|
||||||
"""A spec is concrete if it describes one build of a package uniquely.
|
"""A spec is concrete if it describes one build of a package uniquely.
|
||||||
This will ensure that this spec is concrete.
|
This will ensure that this spec is concrete.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tests (list or bool): list of packages that will need test
|
||||||
|
dependencies, or True/False for test all/none
|
||||||
|
|
||||||
If this spec could describe more than one version, variant, or build
|
If this spec could describe more than one version, variant, or build
|
||||||
of a package, this will add constraints to make it concrete.
|
of a package, this will add constraints to make it concrete.
|
||||||
|
|
||||||
@ -1830,7 +1834,7 @@ def concretize(self):
|
|||||||
force = False
|
force = False
|
||||||
|
|
||||||
while changed:
|
while changed:
|
||||||
changes = (self.normalize(force),
|
changes = (self.normalize(force, tests),
|
||||||
self._expand_virtual_packages(),
|
self._expand_virtual_packages(),
|
||||||
self._concretize_helper())
|
self._concretize_helper())
|
||||||
changed = any(changes)
|
changed = any(changes)
|
||||||
@ -2050,7 +2054,7 @@ def _find_provider(self, vdep, provider_index):
|
|||||||
raise UnsatisfiableProviderSpecError(required[0], vdep)
|
raise UnsatisfiableProviderSpecError(required[0], vdep)
|
||||||
|
|
||||||
def _merge_dependency(
|
def _merge_dependency(
|
||||||
self, dependency, visited, spec_deps, provider_index):
|
self, dependency, visited, spec_deps, provider_index, tests):
|
||||||
"""Merge dependency information from a Package into this Spec.
|
"""Merge dependency information from a Package into this Spec.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -2146,10 +2150,10 @@ def _merge_dependency(
|
|||||||
self._add_dependency(spec_dependency, dependency.type)
|
self._add_dependency(spec_dependency, dependency.type)
|
||||||
|
|
||||||
changed |= spec_dependency._normalize_helper(
|
changed |= spec_dependency._normalize_helper(
|
||||||
visited, spec_deps, provider_index)
|
visited, spec_deps, provider_index, tests)
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def _normalize_helper(self, visited, spec_deps, provider_index):
|
def _normalize_helper(self, visited, spec_deps, provider_index, tests):
|
||||||
"""Recursive helper function for _normalize."""
|
"""Recursive helper function for _normalize."""
|
||||||
if self.name in visited:
|
if self.name in visited:
|
||||||
return False
|
return False
|
||||||
@ -2170,16 +2174,23 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
|
|||||||
for dep_name in self.package_class.dependencies:
|
for dep_name in self.package_class.dependencies:
|
||||||
# Do we depend on dep_name? If so pkg_dep is not None.
|
# Do we depend on dep_name? If so pkg_dep is not None.
|
||||||
dep = self._evaluate_dependency_conditions(dep_name)
|
dep = self._evaluate_dependency_conditions(dep_name)
|
||||||
|
|
||||||
# If dep is a needed dependency, merge it.
|
# If dep is a needed dependency, merge it.
|
||||||
if dep and (spack.package_testing.check(self.name) or
|
if dep:
|
||||||
set(dep.type) - set(['test'])):
|
merge = (
|
||||||
changed |= self._merge_dependency(
|
# caller requested test dependencies
|
||||||
dep, visited, spec_deps, provider_index)
|
tests is True or (tests and self.name in tests) or
|
||||||
|
# this is not a test-only dependency
|
||||||
|
dep.type - set(['test']))
|
||||||
|
|
||||||
|
if merge:
|
||||||
|
changed |= self._merge_dependency(
|
||||||
|
dep, visited, spec_deps, provider_index, tests)
|
||||||
any_change |= changed
|
any_change |= changed
|
||||||
|
|
||||||
return any_change
|
return any_change
|
||||||
|
|
||||||
def normalize(self, force=False):
|
def normalize(self, force=False, tests=False):
|
||||||
"""When specs are parsed, any dependencies specified are hanging off
|
"""When specs are parsed, any dependencies specified are hanging off
|
||||||
the root, and ONLY the ones that were explicitly provided are there.
|
the root, and ONLY the ones that were explicitly provided are there.
|
||||||
Normalization turns a partial flat spec into a DAG, where:
|
Normalization turns a partial flat spec into a DAG, where:
|
||||||
@ -2220,7 +2231,8 @@ def normalize(self, force=False):
|
|||||||
# to package files & their 'when' specs
|
# to package files & their 'when' specs
|
||||||
visited = set()
|
visited = set()
|
||||||
|
|
||||||
any_change = self._normalize_helper(visited, spec_deps, provider_index)
|
any_change = self._normalize_helper(
|
||||||
|
visited, spec_deps, provider_index, tests)
|
||||||
|
|
||||||
# If there are deps specified but not visited, they're not
|
# If there are deps specified but not visited, they're not
|
||||||
# actually deps of this package. Raise an error.
|
# actually deps of this package. Raise an error.
|
||||||
|
@ -53,10 +53,8 @@ def parser():
|
|||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def noop_install(monkeypatch):
|
def noop_install(monkeypatch):
|
||||||
|
|
||||||
def noop(*args, **kwargs):
|
def noop(*args, **kwargs):
|
||||||
return
|
pass
|
||||||
|
|
||||||
monkeypatch.setattr(spack.package.PackageBase, 'do_install', noop)
|
monkeypatch.setattr(spack.package.PackageBase, 'do_install', noop)
|
||||||
|
|
||||||
|
|
||||||
@ -77,31 +75,31 @@ def test_install_package_and_dependency(
|
|||||||
assert 'errors="0"' in content
|
assert 'errors="0"' in content
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('noop_install', 'builtin_mock', 'config')
|
@pytest.mark.disable_clean_stage_check
|
||||||
def test_install_runtests():
|
def test_install_runtests_notests(monkeypatch, builtin_mock, install_mockery):
|
||||||
assert not spack.package_testing._test_all
|
def check(pkg):
|
||||||
assert not spack.package_testing.packages_to_test
|
assert not pkg.run_tests
|
||||||
|
monkeypatch.setattr(spack.package.PackageBase, 'unit_test_check', check)
|
||||||
|
install('-v', 'dttop')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.disable_clean_stage_check
|
||||||
|
def test_install_runtests_root(monkeypatch, builtin_mock, install_mockery):
|
||||||
|
def check(pkg):
|
||||||
|
assert pkg.run_tests == (pkg.name == 'dttop')
|
||||||
|
|
||||||
|
monkeypatch.setattr(spack.package.PackageBase, 'unit_test_check', check)
|
||||||
install('--test=root', 'dttop')
|
install('--test=root', 'dttop')
|
||||||
assert not spack.package_testing._test_all
|
|
||||||
assert spack.package_testing.packages_to_test == set(['dttop'])
|
|
||||||
|
|
||||||
spack.package_testing.clear()
|
|
||||||
|
|
||||||
|
@pytest.mark.disable_clean_stage_check
|
||||||
|
def test_install_runtests_all(monkeypatch, builtin_mock, install_mockery):
|
||||||
|
def check(pkg):
|
||||||
|
assert pkg.run_tests
|
||||||
|
|
||||||
|
monkeypatch.setattr(spack.package.PackageBase, 'unit_test_check', check)
|
||||||
install('--test=all', 'a')
|
install('--test=all', 'a')
|
||||||
assert spack.package_testing._test_all
|
|
||||||
assert not spack.package_testing.packages_to_test
|
|
||||||
|
|
||||||
spack.package_testing.clear()
|
|
||||||
|
|
||||||
install('--run-tests', 'a')
|
install('--run-tests', 'a')
|
||||||
assert spack.package_testing._test_all
|
|
||||||
assert not spack.package_testing.packages_to_test
|
|
||||||
|
|
||||||
spack.package_testing.clear()
|
|
||||||
|
|
||||||
assert not spack.package_testing._test_all
|
|
||||||
assert not spack.package_testing.packages_to_test
|
|
||||||
|
|
||||||
|
|
||||||
def test_install_package_already_installed(
|
def test_install_package_already_installed(
|
||||||
|
@ -98,16 +98,14 @@ def test_test_deptype():
|
|||||||
|
|
||||||
mock_repo = MockPackageMultiRepo([w, x, y, z])
|
mock_repo = MockPackageMultiRepo([w, x, y, z])
|
||||||
try:
|
try:
|
||||||
spack.package_testing.test(w.name)
|
|
||||||
spack.repo = mock_repo
|
spack.repo = mock_repo
|
||||||
spec = Spec('w')
|
spec = Spec('w')
|
||||||
spec.concretize()
|
spec.concretize(tests=(w.name,))
|
||||||
|
|
||||||
assert ('x' in spec)
|
assert ('x' in spec)
|
||||||
assert ('z' not in spec)
|
assert ('z' not in spec)
|
||||||
finally:
|
finally:
|
||||||
spack.repo = saved_repo
|
spack.repo = saved_repo
|
||||||
spack.package_testing.clear()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('refresh_builtin_mock')
|
@pytest.mark.usefixtures('refresh_builtin_mock')
|
||||||
|
Loading…
Reference in New Issue
Block a user