Add test deptype (#5132)
* Add '--test=all' and '--test=root' options to test either the root or the root and all dependencies. * add a test dependency type that is only used when --test is enabled. * test dependencies are not added to the spec, but they are provided in the test environment.
This commit is contained in:
parent
3c0e799a51
commit
9e7faff6c9
@ -158,6 +158,9 @@
|
|||||||
build_jobs = _config.get('build_jobs', multiprocessing.cpu_count())
|
build_jobs = _config.get('build_jobs', multiprocessing.cpu_count())
|
||||||
|
|
||||||
|
|
||||||
|
package_testing = spack.package_prefs.PackageTesting()
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# When packages call 'from spack import *', this extra stuff is brought in.
|
# When packages call 'from spack import *', this extra stuff is brought in.
|
||||||
#
|
#
|
||||||
|
@ -191,9 +191,9 @@ def set_build_environment_variables(pkg, env, dirty):
|
|||||||
dirty (bool): Skip unsetting the user's environment settings
|
dirty (bool): Skip unsetting the user's environment settings
|
||||||
"""
|
"""
|
||||||
# Gather information about various types of dependencies
|
# Gather information about various types of dependencies
|
||||||
build_deps = pkg.spec.dependencies(deptype='build')
|
build_deps = pkg.spec.dependencies(deptype=('build', 'test'))
|
||||||
link_deps = pkg.spec.traverse(root=False, deptype=('link'))
|
link_deps = pkg.spec.traverse(root=False, deptype=('link'))
|
||||||
build_link_deps = pkg.spec.traverse(root=False, deptype=('build', 'link'))
|
build_link_deps = list(build_deps) + list(link_deps)
|
||||||
rpath_deps = get_rpath_deps(pkg)
|
rpath_deps = get_rpath_deps(pkg)
|
||||||
|
|
||||||
build_prefixes = [dep.prefix for dep in build_deps]
|
build_prefixes = [dep.prefix for dep in build_deps]
|
||||||
|
@ -97,9 +97,18 @@ def setup_parser(subparser):
|
|||||||
nargs=argparse.REMAINDER,
|
nargs=argparse.REMAINDER,
|
||||||
help="spec of the package to install"
|
help="spec of the package to install"
|
||||||
)
|
)
|
||||||
subparser.add_argument(
|
testing = subparser.add_mutually_exclusive_group()
|
||||||
|
testing.add_argument(
|
||||||
|
'--test', default=None,
|
||||||
|
choices=['root', 'all'],
|
||||||
|
help="""If 'root' is chosen, run package tests during
|
||||||
|
installation for top-level packages (but skip tests for dependencies).
|
||||||
|
if 'all' is chosen, run package tests during installation for all
|
||||||
|
packages. If neither are chosen, don't run tests for any packages."""
|
||||||
|
)
|
||||||
|
testing.add_argument(
|
||||||
'--run-tests', action='store_true',
|
'--run-tests', action='store_true',
|
||||||
help="run package level tests during installation"
|
help='run package tests during installation (same as --test=all)'
|
||||||
)
|
)
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'--log-format',
|
'--log-format',
|
||||||
@ -325,12 +334,21 @@ def install(parser, args, **kwargs):
|
|||||||
'install_source': args.install_source,
|
'install_source': args.install_source,
|
||||||
'install_deps': 'dependencies' in args.things_to_install,
|
'install_deps': 'dependencies' in args.things_to_install,
|
||||||
'make_jobs': args.jobs,
|
'make_jobs': args.jobs,
|
||||||
'run_tests': args.run_tests,
|
|
||||||
'verbose': args.verbose,
|
'verbose': args.verbose,
|
||||||
'fake': args.fake,
|
'fake': args.fake,
|
||||||
'dirty': args.dirty
|
'dirty': args.dirty
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if args.run_tests:
|
||||||
|
tty.warn("Deprecated option: --run-tests: use --test=all instead")
|
||||||
|
|
||||||
|
specs = spack.cmd.parse_specs(args.package)
|
||||||
|
if args.test == 'all' or args.run_tests:
|
||||||
|
spack.package_testing.test_all()
|
||||||
|
elif args.test == 'root':
|
||||||
|
for spec in specs:
|
||||||
|
spack.package_testing.test(spec.name)
|
||||||
|
|
||||||
# Spec from cli
|
# Spec from cli
|
||||||
specs = []
|
specs = []
|
||||||
if args.file:
|
if args.file:
|
||||||
|
@ -1210,7 +1210,6 @@ def do_install(self,
|
|||||||
skip_patch=False,
|
skip_patch=False,
|
||||||
verbose=False,
|
verbose=False,
|
||||||
make_jobs=None,
|
make_jobs=None,
|
||||||
run_tests=False,
|
|
||||||
fake=False,
|
fake=False,
|
||||||
explicit=False,
|
explicit=False,
|
||||||
dirty=None,
|
dirty=None,
|
||||||
@ -1235,7 +1234,6 @@ def do_install(self,
|
|||||||
suppresses it)
|
suppresses it)
|
||||||
make_jobs (int): Number of make jobs to use for install. Default
|
make_jobs (int): Number of make jobs to use for install. Default
|
||||||
is ncpus
|
is ncpus
|
||||||
run_tests (bool): Run tests within the package's install()
|
|
||||||
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).
|
||||||
@ -1281,7 +1279,6 @@ def do_install(self,
|
|||||||
skip_patch=skip_patch,
|
skip_patch=skip_patch,
|
||||||
verbose=verbose,
|
verbose=verbose,
|
||||||
make_jobs=make_jobs,
|
make_jobs=make_jobs,
|
||||||
run_tests=run_tests,
|
|
||||||
dirty=dirty,
|
dirty=dirty,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -1289,7 +1286,7 @@ def do_install(self,
|
|||||||
tty.msg('Installing %s' % self.name)
|
tty.msg('Installing %s' % self.name)
|
||||||
|
|
||||||
# Set run_tests flag before starting build.
|
# Set run_tests flag before starting build.
|
||||||
self.run_tests = run_tests
|
self.run_tests = spack.package_testing.check(self.name)
|
||||||
|
|
||||||
# Set parallelism before starting build.
|
# Set parallelism before starting build.
|
||||||
self.make_jobs = make_jobs
|
self.make_jobs = make_jobs
|
||||||
|
@ -204,6 +204,25 @@ 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."""
|
||||||
|
@ -197,7 +197,7 @@
|
|||||||
_any_version = VersionList([':'])
|
_any_version = VersionList([':'])
|
||||||
|
|
||||||
#: Types of dependencies that Spack understands.
|
#: Types of dependencies that Spack understands.
|
||||||
alldeps = ('build', 'link', 'run')
|
alldeps = ('build', 'link', 'run', 'test')
|
||||||
|
|
||||||
#: Max integer helps avoid passing too large a value to cyaml.
|
#: Max integer helps avoid passing too large a value to cyaml.
|
||||||
maxint = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1
|
maxint = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1
|
||||||
@ -2089,7 +2089,8 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
|
|||||||
pkg_dep = self._evaluate_dependency_conditions(dep_name)
|
pkg_dep = self._evaluate_dependency_conditions(dep_name)
|
||||||
deptypes = pkg.dependency_types[dep_name]
|
deptypes = pkg.dependency_types[dep_name]
|
||||||
# If pkg_dep is a dependency, merge it.
|
# If pkg_dep is a dependency, merge it.
|
||||||
if pkg_dep:
|
if pkg_dep and (spack.package_testing.check(self.name) or
|
||||||
|
set(deptypes) - set(['test'])):
|
||||||
changed |= self._merge_dependency(
|
changed |= self._merge_dependency(
|
||||||
pkg_dep, deptypes, visited, spec_deps, provider_index)
|
pkg_dep, deptypes, visited, spec_deps, provider_index)
|
||||||
any_change |= changed
|
any_change |= changed
|
||||||
|
@ -44,6 +44,15 @@ def parser():
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def noop_install(monkeypatch):
|
||||||
|
|
||||||
|
def noop(*args, **kwargs):
|
||||||
|
return
|
||||||
|
|
||||||
|
monkeypatch.setattr(spack.package.PackageBase, 'do_install', noop)
|
||||||
|
|
||||||
|
|
||||||
def test_install_package_and_dependency(
|
def test_install_package_and_dependency(
|
||||||
tmpdir, builtin_mock, mock_archive, mock_fetch, config,
|
tmpdir, builtin_mock, mock_archive, mock_fetch, config,
|
||||||
install_mockery):
|
install_mockery):
|
||||||
@ -64,6 +73,33 @@ def test_install_package_and_dependency(
|
|||||||
assert not spack.repo.get(s).stage.created
|
assert not spack.repo.get(s).stage.created
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('noop_install', 'builtin_mock', 'config')
|
||||||
|
def test_install_runtests():
|
||||||
|
assert not spack.package_testing._test_all
|
||||||
|
assert not spack.package_testing.packages_to_test
|
||||||
|
|
||||||
|
install('--test=root', 'dttop')
|
||||||
|
assert not spack.package_testing._test_all
|
||||||
|
assert spack.package_testing.packages_to_test == set(['dttop'])
|
||||||
|
|
||||||
|
spack.package_testing.clear()
|
||||||
|
|
||||||
|
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')
|
||||||
|
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(
|
||||||
tmpdir, builtin_mock, mock_archive, mock_fetch, config,
|
tmpdir, builtin_mock, mock_archive, mock_fetch, config,
|
||||||
install_mockery):
|
install_mockery):
|
||||||
|
@ -44,6 +44,8 @@
|
|||||||
import spack.util.pattern
|
import spack.util.pattern
|
||||||
from spack.package import PackageBase
|
from spack.package import PackageBase
|
||||||
from spack.fetch_strategy import *
|
from spack.fetch_strategy import *
|
||||||
|
from spack.spec import Spec
|
||||||
|
from spack.version import Version
|
||||||
|
|
||||||
|
|
||||||
##########
|
##########
|
||||||
@ -552,3 +554,58 @@ def get_rev():
|
|||||||
t = Bunch(checks=checks, url=url, hash=get_rev, path=str(repodir))
|
t = Bunch(checks=checks, url=url, hash=get_rev, path=str(repodir))
|
||||||
yield t
|
yield t
|
||||||
current.chdir()
|
current.chdir()
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Mock packages
|
||||||
|
##########
|
||||||
|
|
||||||
|
|
||||||
|
class MockPackage(object):
|
||||||
|
|
||||||
|
def __init__(self, name, dependencies, dependency_types, conditions=None,
|
||||||
|
versions=None):
|
||||||
|
self.name = name
|
||||||
|
self.spec = None
|
||||||
|
dep_to_conditions = ordereddict_backport.OrderedDict()
|
||||||
|
for dep in dependencies:
|
||||||
|
if not conditions or dep.name not in conditions:
|
||||||
|
dep_to_conditions[dep.name] = {name: dep.name}
|
||||||
|
else:
|
||||||
|
dep_to_conditions[dep.name] = conditions[dep.name]
|
||||||
|
self.dependencies = dep_to_conditions
|
||||||
|
self.dependency_types = dict(
|
||||||
|
(x.name, y) for x, y in zip(dependencies, dependency_types))
|
||||||
|
if versions:
|
||||||
|
self.versions = versions
|
||||||
|
else:
|
||||||
|
versions = list(Version(x) for x in [1, 2, 3])
|
||||||
|
self.versions = dict((x, {'preferred': False}) for x in versions)
|
||||||
|
self.variants = {}
|
||||||
|
self.provided = {}
|
||||||
|
self.conflicts = {}
|
||||||
|
|
||||||
|
|
||||||
|
class MockPackageMultiRepo(object):
|
||||||
|
|
||||||
|
def __init__(self, packages):
|
||||||
|
self.specToPkg = dict((x.name, x) for x in packages)
|
||||||
|
|
||||||
|
def get(self, spec):
|
||||||
|
if not isinstance(spec, spack.spec.Spec):
|
||||||
|
spec = Spec(spec)
|
||||||
|
return self.specToPkg[spec.name]
|
||||||
|
|
||||||
|
def get_pkg_class(self, name):
|
||||||
|
return self.specToPkg[name]
|
||||||
|
|
||||||
|
def exists(self, name):
|
||||||
|
return name in self.specToPkg
|
||||||
|
|
||||||
|
def is_virtual(self, name):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def repo_for_pkg(self, name):
|
||||||
|
import collections
|
||||||
|
Repo = collections.namedtuple('Repo', ['namespace'])
|
||||||
|
return Repo('mockrepo')
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
import spack.architecture
|
import spack.architecture
|
||||||
import spack.package
|
import spack.package
|
||||||
|
|
||||||
|
from spack.test.conftest import MockPackage, MockPackageMultiRepo
|
||||||
from spack.spec import Spec, canonical_deptype, alldeps
|
from spack.spec import Spec, canonical_deptype, alldeps
|
||||||
|
|
||||||
|
|
||||||
@ -69,6 +70,44 @@ def _mock(pkg_name, spec, deptypes=spack.alldeps):
|
|||||||
return _mock
|
return _mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('config')
|
||||||
|
def test_test_deptype():
|
||||||
|
"""Ensure that test-only dependencies are only included for specified
|
||||||
|
packages in the following spec DAG::
|
||||||
|
|
||||||
|
w
|
||||||
|
/|
|
||||||
|
x y
|
||||||
|
|
|
||||||
|
z
|
||||||
|
|
||||||
|
w->y deptypes are (link, build), w->x and y->z deptypes are (test)
|
||||||
|
|
||||||
|
"""
|
||||||
|
saved_repo = spack.repo
|
||||||
|
|
||||||
|
default = ('build', 'link')
|
||||||
|
test_only = ('test',)
|
||||||
|
|
||||||
|
x = MockPackage('x', [], [])
|
||||||
|
z = MockPackage('z', [], [])
|
||||||
|
y = MockPackage('y', [z], [test_only])
|
||||||
|
w = MockPackage('w', [x, y], [test_only, default])
|
||||||
|
|
||||||
|
mock_repo = MockPackageMultiRepo([w, x, y, z])
|
||||||
|
try:
|
||||||
|
spack.package_testing.test(w.name)
|
||||||
|
spack.repo = mock_repo
|
||||||
|
spec = Spec('w')
|
||||||
|
spec.concretize()
|
||||||
|
|
||||||
|
assert ('x' in spec)
|
||||||
|
assert ('z' not in spec)
|
||||||
|
finally:
|
||||||
|
spack.repo = saved_repo
|
||||||
|
spack.package_testing.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('refresh_builtin_mock')
|
@pytest.mark.usefixtures('refresh_builtin_mock')
|
||||||
class TestSpecDag(object):
|
class TestSpecDag(object):
|
||||||
|
|
||||||
|
@ -39,8 +39,7 @@ class Libpsl(AutotoolsPackage):
|
|||||||
depends_on('pkg-config@0.9.0:', type='build')
|
depends_on('pkg-config@0.9.0:', type='build')
|
||||||
depends_on('python@2.7:', type='build')
|
depends_on('python@2.7:', type='build')
|
||||||
|
|
||||||
# TODO: Add a 'test' deptype
|
depends_on('valgrind~mpi~boost', type='test')
|
||||||
# depends_on('valgrind', type='test')
|
|
||||||
|
|
||||||
def configure_args(self):
|
def configure_args(self):
|
||||||
spec = self.spec
|
spec = self.spec
|
||||||
|
Loading…
Reference in New Issue
Block a user