diff --git a/lib/spack/spack/test/cmd/ci.py b/lib/spack/spack/test/cmd/ci.py index 89ed471868..36236b0cc1 100644 --- a/lib/spack/spack/test/cmd/ci.py +++ b/lib/spack/spack/test/cmd/ci.py @@ -18,7 +18,7 @@ import spack.paths as spack_paths import spack.repo as repo from spack.spec import Spec -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo import spack.util.executable as exe import spack.util.spack_yaml as syaml import spack.util.gpg @@ -101,15 +101,14 @@ def test_specs_staging(config): """ default = ('build', 'link') - g = MockPackage('g', [], []) - f = MockPackage('f', [], []) - e = MockPackage('e', [], []) - d = MockPackage('d', [f, g], [default, default]) - c = MockPackage('c', [], []) - b = MockPackage('b', [d, e], [default, default]) - a = MockPackage('a', [b, c], [default, default]) - - mock_repo = MockPackageMultiRepo([a, b, c, d, e, f, g]) + mock_repo = MockPackageMultiRepo() + g = mock_repo.add_package('g', [], []) + f = mock_repo.add_package('f', [], []) + e = mock_repo.add_package('e', [], []) + d = mock_repo.add_package('d', [f, g], [default, default]) + c = mock_repo.add_package('c', [], []) + b = mock_repo.add_package('b', [d, e], [default, default]) + mock_repo.add_package('a', [b, c], [default, default]) with repo.swap(mock_repo): spec_a = Spec('a') diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 4f3abb4438..f35b0a4bc1 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -20,7 +20,7 @@ from spack.stage import stage_prefix from spack.spec_list import SpecListError -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo import spack.util.spack_json as sjson @@ -733,10 +733,10 @@ def create_v1_lockfile_dict(roots, all_specs): def test_read_old_lock_and_write_new(tmpdir): build_only = ('build',) - y = MockPackage('y', [], []) - x = MockPackage('x', [y], [build_only]) + mock_repo = MockPackageMultiRepo() + y = mock_repo.add_package('y', [], []) + mock_repo.add_package('x', [y], [build_only]) - mock_repo = MockPackageMultiRepo([x, y]) with spack.repo.swap(mock_repo): x = Spec('x') x.concretize() @@ -765,9 +765,9 @@ def test_read_old_lock_creates_backup(tmpdir): """When reading a version-1 lockfile, make sure that a backup of that file is created. """ - y = MockPackage('y', [], []) + mock_repo = MockPackageMultiRepo() + y = mock_repo.add_package('y', [], []) - mock_repo = MockPackageMultiRepo([y]) with spack.repo.swap(mock_repo): y = Spec('y') y.concretize() @@ -796,11 +796,10 @@ def test_indirect_build_dep(): default = ('build', 'link') build_only = ('build',) - z = MockPackage('z', [], []) - y = MockPackage('y', [z], [build_only]) - x = MockPackage('x', [y], [default]) - - mock_repo = MockPackageMultiRepo([x, y, z]) + mock_repo = MockPackageMultiRepo() + z = mock_repo.add_package('z', [], []) + y = mock_repo.add_package('y', [z], [build_only]) + mock_repo.add_package('x', [y], [default]) def noop(*args): pass @@ -838,11 +837,10 @@ def test_store_different_build_deps(): default = ('build', 'link') build_only = ('build',) - z = MockPackage('z', [], []) - y = MockPackage('y', [z], [build_only]) - x = MockPackage('x', [y, z], [default, build_only]) - - mock_repo = MockPackageMultiRepo([x, y, z]) + mock_repo = MockPackageMultiRepo() + z = mock_repo.add_package('z', [], []) + y = mock_repo.add_package('y', [z], [build_only]) + mock_repo.add_package('x', [y, z], [default, build_only]) def noop(*args): pass diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 9024c99cad..232dfaeb4b 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -15,7 +15,7 @@ from spack.package_prefs import PackagePrefs from spack.spec import Spec, CompilerSpec, ConflictsInSpecError from spack.version import ver -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo import spack.compilers import spack.platforms.test @@ -235,10 +235,10 @@ def test_architecture_deep_inheritance(self): """ default_dep = ('link', 'build') - bazpkg = MockPackage('bazpkg', [], []) - barpkg = MockPackage('barpkg', [bazpkg], [default_dep]) - foopkg = MockPackage('foopkg', [barpkg], [default_dep]) - mock_repo = MockPackageMultiRepo([foopkg, barpkg, bazpkg]) + mock_repo = MockPackageMultiRepo() + bazpkg = mock_repo.add_package('bazpkg', [], []) + barpkg = mock_repo.add_package('barpkg', [bazpkg], [default_dep]) + mock_repo.add_package('foopkg', [barpkg], [default_dep]) with spack.repo.swap(mock_repo): spec = Spec('foopkg %clang@3.3 os=CNL target=footar' + diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py index 8912c0219b..736c5a5563 100644 --- a/lib/spack/spack/test/conftest.py +++ b/lib/spack/spack/test/conftest.py @@ -14,7 +14,6 @@ import tempfile import xml.etree.ElementTree -import ordereddict_backport import py import pytest import ruamel.yaml as yaml @@ -38,11 +37,8 @@ import spack.util.gpg from spack.util.pattern import Bunch -from spack.dependency import Dependency from spack.fetch_strategy import FetchStrategyComposite, URLFetchStrategy from spack.fetch_strategy import FetchError -from spack.spec import Spec -from spack.version import Version @pytest.fixture @@ -1004,75 +1000,6 @@ def installation_dir_with_headers(tmpdir_factory): return root -########## -# Mock packages -########## - - -class MockPackage(object): - def __init__(self, name, dependencies, dependency_types, conditions=None, - versions=None): - self.name = name - self.spec = None - self.dependencies = ordereddict_backport.OrderedDict() - self._installed_upstream = False - - assert len(dependencies) == len(dependency_types) - for dep, dtype in zip(dependencies, dependency_types): - d = Dependency(self, Spec(dep.name), type=dtype) - if not conditions or dep.name not in conditions: - self.dependencies[dep.name] = {Spec(name): d} - else: - dep_conditions = conditions[dep.name] - dep_conditions = dict( - (Spec(x), Dependency(self, Spec(y), type=dtype)) - for x, y in dep_conditions.items()) - self.dependencies[dep.name] = dep_conditions - - 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 = {} - self.patches = {} - - def provides(self, vname): - return vname in self.provided - - @property - def virtuals_provided(self): - return [v.name for v, c in self.provided] - - -class MockPackageMultiRepo(object): - def __init__(self, packages): - self.spec_to_pkg = dict((x.name, x) for x in packages) - self.spec_to_pkg.update( - dict(('mockrepo.' + x.name, x) for x in packages)) - - def get(self, spec): - if not isinstance(spec, spack.spec.Spec): - spec = Spec(spec) - return self.spec_to_pkg[spec.name] - - def get_pkg_class(self, name): - return self.spec_to_pkg[name] - - def exists(self, name): - return name in self.spec_to_pkg - - def is_virtual(self, name): - return False - - def repo_for_pkg(self, name): - import collections - Repo = collections.namedtuple('Repo', ['namespace']) - return Repo('mockrepo') - ########## # Specs of various kind ########## diff --git a/lib/spack/spack/test/database.py b/lib/spack/spack/test/database.py index 28311c7501..70f9bffe59 100644 --- a/lib/spack/spack/test/database.py +++ b/lib/spack/spack/test/database.py @@ -28,7 +28,7 @@ import spack.database import spack.package import spack.spec -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo from spack.util.executable import Executable @@ -73,11 +73,11 @@ def test_installed_upstream(upstream_and_downstream_db): downstream_db, downstream_layout = (upstream_and_downstream_db) default = ('build', 'link') - x = MockPackage('x', [], []) - z = MockPackage('z', [], []) - y = MockPackage('y', [z], [default]) - w = MockPackage('w', [x, y], [default, default]) - mock_repo = MockPackageMultiRepo([w, x, y, z]) + mock_repo = MockPackageMultiRepo() + x = mock_repo.add_package('x', [], []) + z = mock_repo.add_package('z', [], []) + y = mock_repo.add_package('y', [z], [default]) + mock_repo.add_package('w', [x, y], [default, default]) with spack.repo.swap(mock_repo): spec = spack.spec.Spec('w') @@ -116,9 +116,9 @@ def test_removed_upstream_dep(upstream_and_downstream_db): downstream_db, downstream_layout = (upstream_and_downstream_db) default = ('build', 'link') - z = MockPackage('z', [], []) - y = MockPackage('y', [z], [default]) - mock_repo = MockPackageMultiRepo([y, z]) + mock_repo = MockPackageMultiRepo() + z = mock_repo.add_package('z', [], []) + mock_repo.add_package('y', [z], [default]) with spack.repo.swap(mock_repo): spec = spack.spec.Spec('y') @@ -150,8 +150,8 @@ def test_add_to_upstream_after_downstream(upstream_and_downstream_db): upstream_write_db, upstream_db, upstream_layout,\ downstream_db, downstream_layout = (upstream_and_downstream_db) - x = MockPackage('x', [], []) - mock_repo = MockPackageMultiRepo([x]) + mock_repo = MockPackageMultiRepo() + mock_repo.add_package('x', [], []) with spack.repo.swap(mock_repo): spec = spack.spec.Spec('x') @@ -183,8 +183,8 @@ def test_cannot_write_upstream(tmpdir_factory, test_store, gen_mock_layout): roots = [str(tmpdir_factory.mktemp(x)) for x in ['a', 'b']] layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/']] - x = MockPackage('x', [], []) - mock_repo = MockPackageMultiRepo([x]) + mock_repo = MockPackageMultiRepo() + mock_repo.add_package('x', [], []) # Instantiate the database that will be used as the upstream DB and make # sure it has an index file @@ -209,11 +209,10 @@ def test_recursive_upstream_dbs(tmpdir_factory, test_store, gen_mock_layout): layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/', '/rc/']] default = ('build', 'link') - z = MockPackage('z', [], []) - y = MockPackage('y', [z], [default]) - x = MockPackage('x', [y], [default]) - - mock_repo = MockPackageMultiRepo([x, y, z]) + mock_repo = MockPackageMultiRepo() + z = mock_repo.add_package('z', [], []) + y = mock_repo.add_package('y', [z], [default]) + mock_repo.add_package('x', [y], [default]) with spack.repo.swap(mock_repo): spec = spack.spec.Spec('x') @@ -675,7 +674,7 @@ def test_115_reindex_with_packages_not_in_repo(mutable_database): # Dont add any package definitions to this repository, the idea is that # packages should not have to be defined in the repository once they # are installed - with spack.repo.swap(MockPackageMultiRepo([])): + with spack.repo.swap(MockPackageMultiRepo()): spack.store.store.reindex() _check_db_sanity(mutable_database) diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py index e031f02c25..0b638ada04 100644 --- a/lib/spack/spack/test/spec_dag.py +++ b/lib/spack/spack/test/spec_dag.py @@ -12,7 +12,7 @@ from spack.spec import Spec from spack.dependency import all_deptypes, Dependency, canonical_deptype -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo def check_links(spec_to_check): @@ -69,12 +69,12 @@ def test_test_deptype(): 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() + x = mock_repo.add_package('x', [], []) + z = mock_repo.add_package('z', [], []) + y = mock_repo.add_package('y', [z], [test_only]) + w = mock_repo.add_package('w', [x, y], [test_only, default]) - mock_repo = MockPackageMultiRepo([w, x, y, z]) with spack.repo.swap(mock_repo): spec = Spec('w') spec.concretize(tests=(w.name,)) @@ -93,8 +93,9 @@ def test_installed_deps(): default = ('build', 'link') build_only = ('build',) - e = MockPackage('e', [], []) - d = MockPackage('d', [], []) + mock_repo = MockPackageMultiRepo() + e = mock_repo.add_package('e', [], []) + d = mock_repo.add_package('d', [], []) c_conditions = { d.name: { 'c': 'd@2' @@ -103,11 +104,10 @@ def test_installed_deps(): 'c': 'e@2' } } - c = MockPackage('c', [d, e], [build_only, default], - conditions=c_conditions) - b = MockPackage('b', [d, e], [default, default]) - a = MockPackage('a', [b, c], [default, default]) - mock_repo = MockPackageMultiRepo([a, b, c, d, e]) + c = mock_repo.add_package('c', [d, e], [build_only, default], + conditions=c_conditions) + b = mock_repo.add_package('b', [d, e], [default, default]) + mock_repo.add_package('a', [b, c], [default, default]) with spack.repo.swap(mock_repo): c_spec = Spec('c') @@ -133,10 +133,10 @@ def test_specify_preinstalled_dep(): """ default = ('build', 'link') - c = MockPackage('c', [], []) - b = MockPackage('b', [c], [default]) - a = MockPackage('a', [b], [default]) - mock_repo = MockPackageMultiRepo([a, b, c]) + mock_repo = MockPackageMultiRepo() + c = mock_repo.add_package('c', [], []) + b = mock_repo.add_package('b', [c], [default]) + mock_repo.add_package('a', [b], [default]) with spack.repo.swap(mock_repo): b_spec = Spec('b') @@ -161,15 +161,15 @@ def test_conditional_dep_with_user_constraints(): """ default = ('build', 'link') - y = MockPackage('y', [], []) + mock_repo = MockPackageMultiRepo() + y = mock_repo.add_package('y', [], []) x_on_y_conditions = { y.name: { 'x@2:': 'y' } } - x = MockPackage('x', [y], [default], conditions=x_on_y_conditions) + mock_repo.add_package('x', [y], [default], conditions=x_on_y_conditions) - mock_repo = MockPackageMultiRepo([x, y]) with spack.repo.swap(mock_repo): spec = Spec('x ^y@2') spec.concretize() diff --git a/lib/spack/spack/test/spec_yaml.py b/lib/spack/spack/test/spec_yaml.py index f9b41df19a..03129a7eb9 100644 --- a/lib/spack/spack/test/spec_yaml.py +++ b/lib/spack/spack/test/spec_yaml.py @@ -26,7 +26,7 @@ from spack import repo from spack.spec import Spec, save_dependency_spec_yamls from spack.util.spack_yaml import syaml_dict -from spack.test.conftest import MockPackage, MockPackageMultiRepo +from spack.util.mock_package import MockPackageMultiRepo def check_yaml_round_trip(spec): @@ -301,15 +301,14 @@ def test_save_dependency_spec_yamls_subset(tmpdir, config): default = ('build', 'link') - g = MockPackage('g', [], []) - f = MockPackage('f', [], []) - e = MockPackage('e', [], []) - d = MockPackage('d', [f, g], [default, default]) - c = MockPackage('c', [], []) - b = MockPackage('b', [d, e], [default, default]) - a = MockPackage('a', [b, c], [default, default]) - - mock_repo = MockPackageMultiRepo([a, b, c, d, e, f, g]) + mock_repo = MockPackageMultiRepo() + g = mock_repo.add_package('g', [], []) + f = mock_repo.add_package('f', [], []) + e = mock_repo.add_package('e', [], []) + d = mock_repo.add_package('d', [f, g], [default, default]) + c = mock_repo.add_package('c', [], []) + b = mock_repo.add_package('b', [d, e], [default, default]) + mock_repo.add_package('a', [b, c], [default, default]) with repo.swap(mock_repo): spec_a = Spec('a') diff --git a/lib/spack/spack/test/util/mock_package.py b/lib/spack/spack/test/util/mock_package.py new file mode 100644 index 0000000000..376ac581bd --- /dev/null +++ b/lib/spack/spack/test/util/mock_package.py @@ -0,0 +1,43 @@ +# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import spack.repo +from spack.util.mock_package import MockPackageMultiRepo + + +def test_mock_package_possible_dependencies(): + mock_repo = MockPackageMultiRepo() + e = mock_repo.add_package('e') + d = mock_repo.add_package('d', [e]) + c = mock_repo.add_package('c', [d]) + b = mock_repo.add_package('b', [d]) + a = mock_repo.add_package('a', [b, c]) + + with spack.repo.swap(mock_repo): + assert set(a.possible_dependencies()) == set(['a', 'b', 'c', 'd', 'e']) + assert set(b.possible_dependencies()) == set(['b', 'd', 'e']) + assert set(c.possible_dependencies()) == set(['c', 'd', 'e']) + assert set(d.possible_dependencies()) == set(['d', 'e']) + assert set(e.possible_dependencies()) == set(['e']) + + assert set( + a.possible_dependencies(transitive=False)) == set(['a', 'b', 'c']) + assert set( + b.possible_dependencies(transitive=False)) == set(['b', 'd']) + assert set( + c.possible_dependencies(transitive=False)) == set(['c', 'd']) + assert set( + d.possible_dependencies(transitive=False)) == set(['d', 'e']) + assert set( + e.possible_dependencies(transitive=False)) == set(['e']) + + +def test_mock_repo_is_virtual(): + mock_repo = MockPackageMultiRepo() + + # current implementation is always false + assert mock_repo.is_virtual("foo") is False + assert mock_repo.is_virtual("bar") is False + assert mock_repo.is_virtual("baz") is False diff --git a/lib/spack/spack/util/mock_package.py b/lib/spack/spack/util/mock_package.py new file mode 100644 index 0000000000..3d8ae30b10 --- /dev/null +++ b/lib/spack/spack/util/mock_package.py @@ -0,0 +1,161 @@ +# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +"""Infrastructure used by tests for mocking packages and repos.""" + +import ordereddict_backport + +import spack.util.naming +from spack.dependency import Dependency +from spack.spec import Spec +from spack.version import Version + +__all__ = ["MockPackageMultiRepo"] + + +class MockPackageBase(object): + """Internal base class for mocking ``spack.package.PackageBase``. + + Use ``MockPackageMultiRepo.add_package()`` to create new instances. + + """ + def __init__(self, dependencies, dependency_types, + conditions=None, versions=None): + """Instantiate a new MockPackageBase. + + This is not for general use; it needs to be constructed by a + ``MockPackageMultiRepo``, as we need to know about *all* packages + to find possible depenencies. + + """ + self.spec = None + self._installed_upstream = False + + def provides(self, vname): + return vname in self.provided + + @property + def virtuals_provided(self): + return [v.name for v, c in self.provided] + + @classmethod + def possible_dependencies( + cls, transitive=True, deptype='all', visited=None, virtuals=None): + visited = {} if visited is None else visited + + for name, conditions in cls.dependencies.items(): + # check whether this dependency could be of the type asked for + types = [dep.type for cond, dep in conditions.items()] + types = set.union(*types) + if not any(d in types for d in deptype): + continue + + visited.setdefault(cls.name, set()) + for dep_name in cls.dependencies: + if dep_name in visited: + continue + + visited.setdefault(dep_name, set()) + + if not transitive: + continue + + cls._repo.get(dep_name).possible_dependencies( + transitive, deptype, visited, virtuals) + + return visited + + +class MockPackageMultiRepo(object): + """Mock package repository, mimicking ``spack.repo.Repo``.""" + + def __init__(self): + self.spec_to_pkg = {} + + def get(self, spec): + if not isinstance(spec, spack.spec.Spec): + spec = Spec(spec) + return self.spec_to_pkg[spec.name] + + def get_pkg_class(self, name): + return self.spec_to_pkg[name] + + def exists(self, name): + return name in self.spec_to_pkg + + def is_virtual(self, name): + return False + + def repo_for_pkg(self, name): + import collections + Repo = collections.namedtuple('Repo', ['namespace']) + return Repo('mockrepo') + + def add_package(self, name, dependencies=None, dependency_types=None, + conditions=None): + """Factory method for creating mock packages. + + This creates a new subclass of ``MockPackageBase``, ensures that its + ``name`` and ``__name__`` properties are set up correctly, and + returns a new instance. + + We use a factory function here because many functions and properties + of packages need to be class functions. + + Args: + name (str): name of the new package + dependencies (list): list of mock packages to be dependencies + for this new package (optional; no deps if not provided) + dependency_type (list): list of deptypes for each dependency + (optional; will be default_deptype if not provided) + conditions (list): condition specs for each dependency (optional) + + """ + if not dependencies: + dependencies = [] + + if not dependency_types: + dependency_types = [ + spack.dependency.default_deptype] * len(dependencies) + + assert len(dependencies) == len(dependency_types) + + # new class for the mock package + class MockPackage(MockPackageBase): + pass + MockPackage.__name__ = spack.util.naming.mod_to_class(name) + MockPackage.name = name + MockPackage._repo = self + + # set up dependencies + MockPackage.dependencies = ordereddict_backport.OrderedDict() + for dep, dtype in zip(dependencies, dependency_types): + d = Dependency(MockPackage, Spec(dep.name), type=dtype) + if not conditions or dep.name not in conditions: + MockPackage.dependencies[dep.name] = {Spec(name): d} + else: + dep_conditions = conditions[dep.name] + dep_conditions = dict( + (Spec(x), Dependency(MockPackage, Spec(y), type=dtype)) + for x, y in dep_conditions.items()) + MockPackage.dependencies[dep.name] = dep_conditions + + # each package has some fake versions + versions = list(Version(x) for x in [1, 2, 3]) + MockPackage.versions = dict( + (x, {'preferred': False}) for x in versions + ) + + MockPackage.variants = {} + MockPackage.provided = {} + MockPackage.conflicts = {} + MockPackage.patches = {} + + mock_package = MockPackage( + dependencies, dependency_types, conditions, versions) + self.spec_to_pkg[name] = mock_package + self.spec_to_pkg["mockrepo." + name] = mock_package + + return mock_package