Testing: use spack.store.use_store everywhere (#21656)

Keep spack.store.store and spack.store.db consistent in unit tests

* Remove calls to monkeypatch for spack.store.store and spack.store.db:
  tests that used these called one or the other, which lead to
  inconsistencies (the tests passed regardless but were fragile as a
  result)
* Fixtures making use of monkeypatch with mock_store now use the
  updated use_store function, which sets store.store and store.db
  consistently
* subprocess_context.TestState now transfers the serializes and
  restores spack.store.store (without the monkeypatch changes this
  would have created inconsistencies)
This commit is contained in:
Massimiliano Culpo 2021-02-18 22:22:49 +01:00 committed by GitHub
parent cbcf8d208b
commit f2e3edf6db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 178 additions and 176 deletions

View file

@ -1089,6 +1089,8 @@ def relocate_package(spec, allow_root):
""" """
Relocate the given package Relocate the given package
""" """
import spack.hooks.sbang as sbang
workdir = str(spec.prefix) workdir = str(spec.prefix)
buildinfo = read_buildinfo_file(workdir) buildinfo = read_buildinfo_file(workdir)
new_layout_root = str(spack.store.layout.root) new_layout_root = str(spack.store.layout.root)
@ -1127,7 +1129,6 @@ def relocate_package(spec, allow_root):
prefix_to_prefix_bin = OrderedDict({}) prefix_to_prefix_bin = OrderedDict({})
if old_sbang_install_path: if old_sbang_install_path:
import spack.hooks.sbang as sbang
prefix_to_prefix_text[old_sbang_install_path] = sbang.sbang_install_path() prefix_to_prefix_text[old_sbang_install_path] = sbang.sbang_install_path()
prefix_to_prefix_text[old_prefix] = new_prefix prefix_to_prefix_text[old_prefix] = new_prefix
@ -1141,7 +1142,6 @@ def relocate_package(spec, allow_root):
# sbang was a bash script, and it lived in the spack prefix. It is # sbang was a bash script, and it lived in the spack prefix. It is
# now a POSIX script that lives in the install prefix. Old packages # now a POSIX script that lives in the install prefix. Old packages
# will have the old sbang location in their shebangs. # will have the old sbang location in their shebangs.
import spack.hooks.sbang as sbang
orig_sbang = '#!/bin/bash {0}/bin/sbang'.format(old_spack_prefix) orig_sbang = '#!/bin/bash {0}/bin/sbang'.format(old_spack_prefix)
new_sbang = sbang.sbang_shebang_line() new_sbang = sbang.sbang_shebang_line()
prefix_to_prefix_text[orig_sbang] = new_sbang prefix_to_prefix_text[orig_sbang] = new_sbang
@ -1160,7 +1160,7 @@ def is_backup_file(file):
if not is_backup_file(text_name): if not is_backup_file(text_name):
text_names.append(text_name) text_names.append(text_name)
# If we are not installing back to the same install tree do the relocation # If we are not installing back to the same install tree do the relocation
if old_layout_root != new_layout_root: if old_layout_root != new_layout_root:
files_to_relocate = [os.path.join(workdir, filename) files_to_relocate = [os.path.join(workdir, filename)
for filename in buildinfo.get('relocate_binaries') for filename in buildinfo.get('relocate_binaries')

View file

@ -158,6 +158,8 @@ def __init__(
): ):
self.root = root self.root = root
self.unpadded_root = unpadded_root or root self.unpadded_root = unpadded_root or root
self.projections = projections
self.hash_length = hash_length
self.db = spack.database.Database( self.db = spack.database.Database(
root, upstream_dbs=retrieve_upstream_dbs()) root, upstream_dbs=retrieve_upstream_dbs())
self.layout = spack.directory_layout.YamlDirectoryLayout( self.layout = spack.directory_layout.YamlDirectoryLayout(
@ -167,6 +169,27 @@ def reindex(self):
"""Convenience function to reindex the store DB with its own layout.""" """Convenience function to reindex the store DB with its own layout."""
return self.db.reindex(self.layout) return self.db.reindex(self.layout)
def serialize(self):
"""Return a pickle-able object that can be used to reconstruct
a store.
"""
return (
self.root, self.unpadded_root, self.projections, self.hash_length
)
@staticmethod
def deserialize(token):
"""Return a store reconstructed from a token created by
the serialize method.
Args:
token: return value of the serialize method
Returns:
Store object reconstructed from the token
"""
return Store(*token)
def _store(): def _store():
"""Get the singleton store instance.""" """Get the singleton store instance."""
@ -240,7 +263,7 @@ def use_store(store_or_path):
Returns: Returns:
Store object associated with the context manager's store Store object associated with the context manager's store
""" """
global store global store, db, layout, root, unpadded_root
# Normalize input arguments # Normalize input arguments
temporary_store = store_or_path temporary_store = store_or_path
@ -248,8 +271,14 @@ def use_store(store_or_path):
temporary_store = Store(store_or_path) temporary_store = Store(store_or_path)
# Swap the store with the one just constructed and return it # Swap the store with the one just constructed and return it
_ = store.db
original_store, store = store, temporary_store original_store, store = store, temporary_store
db, layout = store.db, store.layout
root, unpadded_root = store.root, store.unpadded_root
yield temporary_store yield temporary_store
# Restore the original store # Restore the original store
store = original_store store = original_store
db, layout = original_store.db, original_store.layout
root, unpadded_root = original_store.root, original_store.unpadded_root

View file

@ -93,18 +93,21 @@ def __init__(self):
self.config = spack.config.config self.config = spack.config.config
self.platform = spack.architecture.platform self.platform = spack.architecture.platform
self.test_patches = store_patches() self.test_patches = store_patches()
self.store_token = spack.store.store.serialize()
# TODO: transfer spack.store.store? note that you should not
# transfer spack.store.store and spack.store.db: 'db' is a
# shortcut that accesses the store (so transferring both can
# create an inconsistency). Some tests set 'db' directly, and
# others set 'store'
def restore(self): def restore(self):
if _serialize: if _serialize:
spack.repo.path = spack.repo._path(self.repo_dirs) spack.repo.path = spack.repo._path(self.repo_dirs)
spack.config.config = self.config spack.config.config = self.config
spack.architecture.platform = self.platform spack.architecture.platform = self.platform
new_store = spack.store.Store.deserialize(self.store_token)
spack.store.store = new_store
spack.store.root = new_store.root
spack.store.unpadded_root = new_store.unpadded_root
spack.store.db = new_store.db
spack.store.layout = new_store.layout
self.test_patches.restore() self.test_patches.restore()

View file

@ -493,40 +493,39 @@ def test_update_sbang(tmpdir, test_mirror):
'${name}', '${version}', '${name}', '${version}',
'${architecture}-${compiler.name}-${compiler.version}-${hash}' '${architecture}-${compiler.name}-${compiler.version}-${hash}'
) )
# Save the original store and layout before we touch ANYTHING. spec_str = 'old-sbang'
real_store, real_layout = spack.store.store, spack.store.layout
# Concretize a package with some old-fashioned sbang lines. # Concretize a package with some old-fashioned sbang lines.
sspec = Spec('old-sbang') old_spec = Spec(spec_str).concretized()
sspec.concretize() old_spec_hash_str = '/{0}'.format(old_spec.dag_hash())
# Need a fake mirror with *function* scope. # Need a fake mirror with *function* scope.
mirror_dir = test_mirror mirror_dir = test_mirror
mirror_url = 'file://{0}'.format(mirror_dir)
# Assumes all commands will concretize sspec the same way. # Assume all commands will concretize old_spec the same way.
install_cmd('--no-cache', sspec.name) install_cmd('--no-cache', old_spec.name)
# Create a buildcache with the installed spec. # Create a buildcache with the installed spec.
buildcache_cmd('create', '-u', '-a', '-d', mirror_dir, buildcache_cmd('create', '-u', '-a', '-d', mirror_dir, old_spec_hash_str)
'/%s' % sspec.dag_hash())
# Need to force an update of the buildcache index # Need to force an update of the buildcache index
buildcache_cmd('update-index', '-d', 'file://%s' % mirror_dir) buildcache_cmd('update-index', '-d', mirror_url)
# Uninstall the original package. # Uninstall the original package.
uninstall_cmd('-y', '/%s' % sspec.dag_hash()) uninstall_cmd('-y', old_spec_hash_str)
try: # Switch the store to the new install tree locations
# New install tree locations... newtree_dir = tmpdir.join('newtree')
# Too fine-grained to do be done in a fixture s = spack.store.Store(str(newtree_dir))
newtree_dir = tmpdir.join('newtree') s.layout = YamlDirectoryLayout(str(newtree_dir), path_scheme=scheme)
spack.store.store = spack.store.Store(str(newtree_dir))
spack.store.layout = YamlDirectoryLayout( with spack.store.use_store(s):
str(newtree_dir), path_scheme=scheme new_spec = Spec('old-sbang')
) new_spec.concretize()
assert new_spec.dag_hash() == old_spec.dag_hash()
# Install package from buildcache # Install package from buildcache
buildcache_cmd('install', '-a', '-u', '-f', sspec.name) buildcache_cmd('install', '-a', '-u', '-f', new_spec.name)
# Continue blowing away caches # Continue blowing away caches
bindist.clear_spec_cache() bindist.clear_spec_cache()
@ -537,23 +536,19 @@ def test_update_sbang(tmpdir, test_mirror):
#!/usr/bin/env python #!/usr/bin/env python
{1} {1}
'''.format(sbang.sbang_shebang_line(), sspec.prefix.bin) '''.format(sbang.sbang_shebang_line(), new_spec.prefix.bin)
sbang_style_2_expected = '''{0} sbang_style_2_expected = '''{0}
#!/usr/bin/env python #!/usr/bin/env python
{1} {1}
'''.format(sbang.sbang_shebang_line(), sspec.prefix.bin) '''.format(sbang.sbang_shebang_line(), new_spec.prefix.bin)
installed_script_style_1_path = sspec.prefix.bin.join('sbang-style-1.sh') installed_script_style_1_path = new_spec.prefix.bin.join('sbang-style-1.sh')
assert sbang_style_1_expected == \ assert sbang_style_1_expected == \
open(str(installed_script_style_1_path)).read() open(str(installed_script_style_1_path)).read()
installed_script_style_2_path = sspec.prefix.bin.join('sbang-style-2.sh') installed_script_style_2_path = new_spec.prefix.bin.join('sbang-style-2.sh')
assert sbang_style_2_expected == \ assert sbang_style_2_expected == \
open(str(installed_script_style_2_path)).read() open(str(installed_script_style_2_path)).read()
uninstall_cmd('-y', '/%s' % sspec.dag_hash()) uninstall_cmd('-y', '/%s' % new_spec.dag_hash())
finally:
spack.store.store = real_store
spack.store.layout = real_layout

View file

@ -23,7 +23,7 @@ def ensure_module_files_are_there(
): ):
"""Generate module files for module tests.""" """Generate module files for module tests."""
module = spack.main.SpackCommand('module') module = spack.main.SpackCommand('module')
with spack.store.use_store(mock_store): with spack.store.use_store(str(mock_store)):
with spack.config.use_configuration(*mock_configuration_scopes): with spack.config.use_configuration(*mock_configuration_scopes):
with spack.repo.use_repositories(mock_repo_path): with spack.repo.use_repositories(mock_repo_path):
module('tcl', 'refresh', '-y') module('tcl', 'refresh', '-y')

View file

@ -2,36 +2,30 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import pytest
import spack.spec import spack.spec
import spack.store
def test_set_install_hash_length(mock_packages, mutable_config, monkeypatch, @pytest.mark.parametrize('hash_length', [1, 2, 3, 4, 5, 9])
tmpdir): @pytest.mark.use_fixtures('mock_packages')
# spack.store.layout caches initial config values, so we monkeypatch def test_set_install_hash_length(hash_length, mutable_config, tmpdir):
mutable_config.set('config:install_hash_length', 5) mutable_config.set('config:install_hash_length', hash_length)
mutable_config.set('config:install_tree', {'root': str(tmpdir)}) mutable_config.set('config:install_tree', {'root': str(tmpdir)})
monkeypatch.setattr(spack.store, 'store', spack.store._store()) # The call below is to reinitialize the directory layout associated
# with the store according to the configuration changes above (i.e.
spec = spack.spec.Spec('libelf').concretized() # with the shortened hash)
prefix = spec.prefix store = spack.store._store()
hash = prefix.rsplit('-')[-1] with spack.store.use_store(store):
spec = spack.spec.Spec('libelf').concretized()
assert len(hash) == 5 prefix = spec.prefix
hash_str = prefix.rsplit('-')[-1]
mutable_config.set('config:install_hash_length', 9) assert len(hash_str) == hash_length
monkeypatch.setattr(spack.store, 'store', spack.store._store())
spec = spack.spec.Spec('libelf').concretized()
prefix = spec.prefix
hash = prefix.rsplit('-')[-1]
assert len(hash) == 9
def test_set_install_hash_length_upper_case(mock_packages, mutable_config, @pytest.mark.use_fixtures('mock_packages')
monkeypatch, tmpdir): def test_set_install_hash_length_upper_case(mutable_config, tmpdir):
# spack.store.layout caches initial config values, so we monkeypatch
mutable_config.set('config:install_hash_length', 5) mutable_config.set('config:install_hash_length', 5)
mutable_config.set( mutable_config.set(
'config:install_tree', 'config:install_tree',
@ -42,10 +36,12 @@ def test_set_install_hash_length_upper_case(mock_packages, mutable_config,
} }
} }
) )
monkeypatch.setattr(spack.store, 'store', spack.store._store()) # The call below is to reinitialize the directory layout associated
# with the store according to the configuration changes above (i.e.
spec = spack.spec.Spec('libelf').concretized() # with the shortened hash and projection)
prefix = spec.prefix store = spack.store._store()
hash = prefix.rsplit('-')[-1] with spack.store.use_store(store):
spec = spack.spec.Spec('libelf').concretized()
assert len(hash) == 5 prefix = spec.prefix
hash_str = prefix.rsplit('-')[-1]
assert len(hash_str) == 5

View file

@ -563,12 +563,10 @@ def _install(spec):
pkg = spack.repo.get(s) pkg = spack.repo.get(s)
pkg.do_install(fake=True, explicit=True) pkg.do_install(fake=True, explicit=True)
# Transaction used to avoid repeated writes. _install('mpileaks ^mpich')
with mock_db.write_transaction(): _install('mpileaks ^mpich2')
_install('mpileaks ^mpich') _install('mpileaks ^zmpi')
_install('mpileaks ^mpich2') _install('externaltest')
_install('mpileaks ^zmpi')
_install('externaltest')
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
@ -605,7 +603,7 @@ def mock_store(tmpdir_factory, mock_repo_path, mock_configuration_scopes,
# Make the DB filesystem read-only to ensure we can't modify entries # Make the DB filesystem read-only to ensure we can't modify entries
store_path.join('.spack-db').chmod(mode=0o555, rec=1) store_path.join('.spack-db').chmod(mode=0o555, rec=1)
yield store yield store_path
store_path.join('.spack-db').chmod(mode=0o755, rec=1) store_path.join('.spack-db').chmod(mode=0o755, rec=1)
@ -613,10 +611,9 @@ def mock_store(tmpdir_factory, mock_repo_path, mock_configuration_scopes,
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def database(mock_store, mock_packages, config, monkeypatch): def database(mock_store, mock_packages, config, monkeypatch):
"""This activates the mock store, packages, AND config.""" """This activates the mock store, packages, AND config."""
monkeypatch.setattr(spack.store, 'store', mock_store) with spack.store.use_store(str(mock_store)) as store:
yield mock_store.db yield store.db
# Force reading the database again between tests store.db.last_seen_verifier = ''
mock_store.db.last_seen_verifier = ''
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
@ -687,20 +684,15 @@ def disable_compiler_execution(monkeypatch, request):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def install_mockery(tmpdir, config, mock_packages, monkeypatch): def install_mockery(temporary_store, config, mock_packages):
"""Hooks a fake install directory, DB, and stage directory into Spack.""" """Hooks a fake install directory, DB, and stage directory into Spack."""
monkeypatch.setattr(
spack.store, 'store', spack.store.Store(str(tmpdir.join('opt'))))
# We use a fake package, so temporarily disable checksumming # We use a fake package, so temporarily disable checksumming
with spack.config.override('config:checksum', False): with spack.config.override('config:checksum', False):
yield yield
tmpdir.join('opt').remove()
# Also wipe out any cached prefix failure locks (associated with # Also wipe out any cached prefix failure locks (associated with
# the session-scoped mock archive). # the session-scoped mock archive).
for pkg_id in list(spack.store.db._prefix_failures.keys()): for pkg_id in list(temporary_store.db._prefix_failures.keys()):
lock = spack.store.db._prefix_failures.pop(pkg_id, None) lock = spack.store.db._prefix_failures.pop(pkg_id, None)
if lock: if lock:
try: try:
@ -709,24 +701,29 @@ def install_mockery(tmpdir, config, mock_packages, monkeypatch):
pass pass
@pytest.fixture(scope='function')
def temporary_store(tmpdir):
"""Hooks a temporary empty store for the test function."""
temporary_store_path = tmpdir.join('opt')
with spack.store.use_store(str(temporary_store_path)) as s:
yield s
temporary_store_path.remove()
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def install_mockery_mutable_config( def install_mockery_mutable_config(
tmpdir, mutable_config, mock_packages, monkeypatch): temporary_store, mutable_config, mock_packages
):
"""Hooks a fake install directory, DB, and stage directory into Spack. """Hooks a fake install directory, DB, and stage directory into Spack.
This is specifically for tests which want to use 'install_mockery' but This is specifically for tests which want to use 'install_mockery' but
also need to modify configuration (and hence would want to use also need to modify configuration (and hence would want to use
'mutable config'): 'install_mockery' does not support this. 'mutable config'): 'install_mockery' does not support this.
""" """
monkeypatch.setattr(
spack.store, 'store', spack.store.Store(str(tmpdir.join('opt'))))
# We use a fake package, so temporarily disable checksumming # We use a fake package, so temporarily disable checksumming
with spack.config.override('config:checksum', False): with spack.config.override('config:checksum', False):
yield yield
tmpdir.join('opt').remove()
@pytest.fixture() @pytest.fixture()
def mock_fetch(mock_archive, monkeypatch): def mock_fetch(mock_archive, monkeypatch):

View file

@ -37,16 +37,6 @@
pytestmark = pytest.mark.db pytestmark = pytest.mark.db
@pytest.fixture()
def test_store(tmpdir):
real_store = spack.store.store
spack.store.store = spack.store.Store(str(tmpdir.join('test_store')))
yield
spack.store.store = real_store
@pytest.fixture() @pytest.fixture()
def upstream_and_downstream_db(tmpdir_factory, gen_mock_layout): def upstream_and_downstream_db(tmpdir_factory, gen_mock_layout):
mock_db_root = str(tmpdir_factory.mktemp('mock_db_root')) mock_db_root = str(tmpdir_factory.mktemp('mock_db_root'))
@ -180,8 +170,8 @@ def test_add_to_upstream_after_downstream(upstream_and_downstream_db):
spack.store.db = orig_db spack.store.db = orig_db
@pytest.mark.usefixtures('config') @pytest.mark.usefixtures('config', 'temporary_store')
def test_cannot_write_upstream(tmpdir_factory, test_store, gen_mock_layout): def test_cannot_write_upstream(tmpdir_factory, gen_mock_layout):
roots = [str(tmpdir_factory.mktemp(x)) for x in ['a', 'b']] roots = [str(tmpdir_factory.mktemp(x)) for x in ['a', 'b']]
layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/']] layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/']]
@ -205,8 +195,8 @@ def test_cannot_write_upstream(tmpdir_factory, test_store, gen_mock_layout):
upstream_dbs[0].add(spec, layouts[1]) upstream_dbs[0].add(spec, layouts[1])
@pytest.mark.usefixtures('config') @pytest.mark.usefixtures('config', 'temporary_store')
def test_recursive_upstream_dbs(tmpdir_factory, test_store, gen_mock_layout): def test_recursive_upstream_dbs(tmpdir_factory, gen_mock_layout):
roots = [str(tmpdir_factory.mktemp(x)) for x in ['a', 'b', 'c']] roots = [str(tmpdir_factory.mktemp(x)) for x in ['a', 'b', 'c']]
layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/', '/rc/']] layouts = [gen_mock_layout(x) for x in ['/ra/', '/rb/', '/rc/']]

View file

@ -7,6 +7,7 @@
This test verifies that the Spack directory layout works properly. This test verifies that the Spack directory layout works properly.
""" """
import os import os
import os.path
import pytest import pytest
import spack.paths import spack.paths
@ -19,16 +20,6 @@
max_packages = 10 max_packages = 10
@pytest.fixture()
def layout_and_dir(tmpdir):
"""Returns a directory layout and the corresponding directory."""
layout = YamlDirectoryLayout(str(tmpdir))
old_layout = spack.store.layout
spack.store.layout = layout
yield layout, str(tmpdir)
spack.store.layout = old_layout
def test_yaml_directory_layout_parameters(tmpdir, config): def test_yaml_directory_layout_parameters(tmpdir, config):
"""This tests the various parameters that can be used to configure """This tests the various parameters that can be used to configure
the install location """ the install location """
@ -84,14 +75,14 @@ def test_yaml_directory_layout_parameters(tmpdir, config):
projections=projections_package7) projections=projections_package7)
def test_read_and_write_spec(layout_and_dir, config, mock_packages): def test_read_and_write_spec(temporary_store, config, mock_packages):
"""This goes through each package in spack and creates a directory for """This goes through each package in spack and creates a directory for
it. It then ensures that the spec for the directory's it. It then ensures that the spec for the directory's
installed package can be read back in consistently, and installed package can be read back in consistently, and
finally that the directory can be removed by the directory finally that the directory can be removed by the directory
layout. layout.
""" """
layout, tmpdir = layout_and_dir layout = temporary_store.layout
packages = list(spack.repo.path.all_packages())[:max_packages] packages = list(spack.repo.path.all_packages())[:max_packages]
for pkg in packages: for pkg in packages:
@ -114,7 +105,7 @@ def test_read_and_write_spec(layout_and_dir, config, mock_packages):
# Ensure directory has been created in right place. # Ensure directory has been created in right place.
assert os.path.isdir(install_dir) assert os.path.isdir(install_dir)
assert install_dir.startswith(str(tmpdir)) assert install_dir.startswith(temporary_store.root)
# Ensure spec file exists when directory is created # Ensure spec file exists when directory is created
assert os.path.isfile(spec_path) assert os.path.isfile(spec_path)
@ -160,7 +151,7 @@ def test_read_and_write_spec(layout_and_dir, config, mock_packages):
assert not os.path.exists(install_dir) assert not os.path.exists(install_dir)
def test_handle_unknown_package(layout_and_dir, config, mock_packages): def test_handle_unknown_package(temporary_store, config, mock_packages):
"""This test ensures that spack can at least do *some* """This test ensures that spack can at least do *some*
operations with packages that are installed but that it operations with packages that are installed but that it
does not know about. This is actually not such an uncommon does not know about. This is actually not such an uncommon
@ -171,7 +162,7 @@ def test_handle_unknown_package(layout_and_dir, config, mock_packages):
information about installed packages' specs to uninstall information about installed packages' specs to uninstall
or query them again if the package goes away. or query them again if the package goes away.
""" """
layout, _ = layout_and_dir layout = temporary_store.layout
mock_db = spack.repo.RepoPath(spack.paths.mock_packages_path) mock_db = spack.repo.RepoPath(spack.paths.mock_packages_path)
not_in_mock = set.difference( not_in_mock = set.difference(
@ -209,9 +200,9 @@ def test_handle_unknown_package(layout_and_dir, config, mock_packages):
assert spec.dag_hash() == spec_from_file.dag_hash() assert spec.dag_hash() == spec_from_file.dag_hash()
def test_find(layout_and_dir, config, mock_packages): def test_find(temporary_store, config, mock_packages):
"""Test that finding specs within an install layout works.""" """Test that finding specs within an install layout works."""
layout, _ = layout_and_dir layout = temporary_store.layout
packages = list(spack.repo.path.all_packages())[:max_packages] packages = list(spack.repo.path.all_packages())[:max_packages]
# Create install prefixes for all packages in the list # Create install prefixes for all packages in the list

View file

@ -183,70 +183,70 @@ def test_flatten_deps(
assert os.path.isdir(dependency_dir) assert os.path.isdir(dependency_dir)
def test_installed_upstream_external( @pytest.fixture()
tmpdir_factory, install_mockery, mock_fetch, gen_mock_layout, def install_upstream(tmpdir_factory, gen_mock_layout, install_mockery):
monkeypatch): """Provides a function that installs a specified set of specs to an
"""Check that when a dependency package is recorded as installed in upstream database. The function returns a store which points to the
an upstream database that it is not reinstalled. upstream, as well as the upstream layout (for verifying that dependent
installs are using the upstream installs).
""" """
mock_db_root = str(tmpdir_factory.mktemp('mock_db_root')) mock_db_root = str(tmpdir_factory.mktemp('mock_db_root'))
prepared_db = spack.database.Database(mock_db_root) prepared_db = spack.database.Database(mock_db_root)
upstream_layout = gen_mock_layout('/a/') upstream_layout = gen_mock_layout('/a/')
dependency = spack.spec.Spec('externaltool') def _install_upstream(*specs):
dependency.concretize() for spec_str in specs:
prepared_db.add(dependency, upstream_layout) s = spack.spec.Spec(spec_str).concretized()
prepared_db.add(s, upstream_layout)
downstream_db_root = str( downstream_root = str(tmpdir_factory.mktemp('mock_downstream_db_root'))
tmpdir_factory.mktemp('mock_downstream_db_root')) db_for_test = spack.database.Database(
db_for_test = spack.database.Database( downstream_root, upstream_dbs=[prepared_db]
downstream_db_root, upstream_dbs=[prepared_db]) )
monkeypatch.setattr(spack.store, 'db', db_for_test) store = spack.store.Store(downstream_root)
dependent = spack.spec.Spec('externaltest') store.db = db_for_test
dependent.concretize() return store, upstream_layout
new_dependency = dependent['externaltool'] return _install_upstream
assert new_dependency.external
assert new_dependency.prefix == '/path/to/external_tool'
dependent.package.do_install()
assert not os.path.exists(new_dependency.prefix)
assert os.path.exists(dependent.prefix)
def test_installed_upstream(tmpdir_factory, install_mockery, mock_fetch, def test_installed_upstream_external(install_upstream, mock_fetch):
gen_mock_layout, monkeypatch):
"""Check that when a dependency package is recorded as installed in """Check that when a dependency package is recorded as installed in
an upstream database that it is not reinstalled. an upstream database that it is not reinstalled.
""" """
mock_db_root = str(tmpdir_factory.mktemp('mock_db_root')) s, _ = install_upstream('externaltool')
prepared_db = spack.database.Database(mock_db_root) with spack.store.use_store(s):
dependent = spack.spec.Spec('externaltest')
dependent.concretize()
upstream_layout = gen_mock_layout('/a/') new_dependency = dependent['externaltool']
assert new_dependency.external
assert new_dependency.prefix == '/path/to/external_tool'
dependency = spack.spec.Spec('dependency-install') dependent.package.do_install()
dependency.concretize()
prepared_db.add(dependency, upstream_layout)
downstream_db_root = str( assert not os.path.exists(new_dependency.prefix)
tmpdir_factory.mktemp('mock_downstream_db_root')) assert os.path.exists(dependent.prefix)
db_for_test = spack.database.Database(
downstream_db_root, upstream_dbs=[prepared_db])
monkeypatch.setattr(spack.store, 'db', db_for_test)
dependent = spack.spec.Spec('dependent-install')
dependent.concretize()
new_dependency = dependent['dependency-install']
assert new_dependency.package.installed_upstream
assert (new_dependency.prefix ==
upstream_layout.path_for_spec(dependency))
dependent.package.do_install() def test_installed_upstream(install_upstream, mock_fetch):
"""Check that when a dependency package is recorded as installed in
an upstream database that it is not reinstalled.
"""
s, upstream_layout = install_upstream('dependency-install')
with spack.store.use_store(s):
dependency = spack.spec.Spec('dependency-install').concretized()
dependent = spack.spec.Spec('dependent-install').concretized()
assert not os.path.exists(new_dependency.prefix) new_dependency = dependent['dependency-install']
assert os.path.exists(dependent.prefix) assert new_dependency.package.installed_upstream
assert (new_dependency.prefix ==
upstream_layout.path_for_spec(dependency))
dependent.package.do_install()
assert not os.path.exists(new_dependency.prefix)
assert os.path.exists(dependent.prefix)
@pytest.mark.disable_clean_stage_check @pytest.mark.disable_clean_stage_check

View file

@ -2,10 +2,11 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack import * from spack import *
import spack.paths
import spack.store
class OldSbang(Package): class OldSbang(Package):
"""Toy package for testing the old sbang replacement problem""" """Toy package for testing the old sbang replacement problem"""
@ -22,12 +23,12 @@ def install(self, spec, prefix):
#!/usr/bin/env python #!/usr/bin/env python
{1} {1}
'''.format(spack.paths.prefix, prefix.bin) '''.format(spack.paths.prefix, prefix.bin)
sbang_style_2 = '''#!/bin/sh {0}/bin/sbang sbang_style_2 = '''#!/bin/sh {0}/bin/sbang
#!/usr/bin/env python #!/usr/bin/env python
{1} {1}
'''.format(spack.store.unpadded_root, prefix.bin) '''.format(spack.store.unpadded_root, prefix.bin)
with open('%s/sbang-style-1.sh' % self.prefix.bin, 'w') as f: with open('%s/sbang-style-1.sh' % self.prefix.bin, 'w') as f:
f.write(sbang_style_1) f.write(sbang_style_1)