Add scratch module roots to test configuration (#19477)

fixes #19476

Module file content is written to file in a
temporary location and read back to be analyzed
by unit tests.

The approach to patch "open" and write to a
StringIO in memory has been abandoned, since
over time other operations insisting on the
filesystem have been added to the module file
generator.
This commit is contained in:
Massimiliano Culpo 2020-10-22 22:59:39 +02:00 committed by GitHub
parent d6f19eeed2
commit 4ec404dfc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 82 deletions

View File

@ -425,7 +425,17 @@ def configuration_dir(tmpdir_factory, linux_os):
# Create temporary 'defaults', 'site' and 'user' folders # Create temporary 'defaults', 'site' and 'user' folders
tmpdir.ensure('user', dir=True) tmpdir.ensure('user', dir=True)
# Slightly modify compilers.yaml to look like Linux # Slightly modify config.yaml and compilers.yaml
config_yaml = test_config.join('config.yaml')
modules_root = tmpdir_factory.mktemp('share')
tcl_root = modules_root.ensure('modules', dir=True)
lmod_root = modules_root.ensure('lmod', dir=True)
content = ''.join(config_yaml.read()).format(
str(tcl_root), str(lmod_root)
)
t = tmpdir.join('site', 'config.yaml')
t.write(content)
compilers_yaml = test_config.join('compilers.yaml') compilers_yaml = test_config.join('compilers.yaml')
content = ''.join(compilers_yaml.read()).format(linux_os) content = ''.join(compilers_yaml.read()).format(linux_os)
t = tmpdir.join('site', 'compilers.yaml') t = tmpdir.join('site', 'compilers.yaml')

View File

@ -14,4 +14,5 @@ config:
checksum: true checksum: true
dirty: false dirty: false
module_roots: module_roots:
tcl: $spack/share/spack/modules tcl: {0}
lmod: {1}

View File

@ -2,16 +2,9 @@
# 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 os.path
import collections
import contextlib
import inspect
import ruamel.yaml as yaml
import pytest import pytest
from six import StringIO
import spack.config
import spack.paths import spack.paths
import spack.spec import spack.spec
import spack.modules.common import spack.modules.common
@ -19,34 +12,7 @@
@pytest.fixture() @pytest.fixture()
def file_registry(): def modulefile_content(request):
"""Fake filesystem for modulefiles test"""
return collections.defaultdict(StringIO)
@pytest.fixture()
def filename_dict(file_registry, monkeypatch):
"""Returns a fake open that writes on a StringIO instance instead
of disk.
"""
@contextlib.contextmanager
def _mock(filename, mode):
if not mode == 'w':
raise RuntimeError('opening mode must be "w" [stringio_open]')
try:
yield file_registry[filename]
finally:
handle = file_registry[filename]
file_registry[filename] = handle.getvalue()
handle.close()
# Patch 'open' in the appropriate module
monkeypatch.setattr(spack.modules.common, 'open', _mock, raising=False)
return file_registry
@pytest.fixture()
def modulefile_content(filename_dict, request):
"""Returns a function that generates the content of a module file """Returns a function that generates the content of a module file
as a list of lines. as a list of lines.
""" """
@ -58,59 +24,21 @@ def _impl(spec_str):
spec = spack.spec.Spec(spec_str) spec = spack.spec.Spec(spec_str)
spec.concretize() spec.concretize()
generator = writer_cls(spec) generator = writer_cls(spec)
generator.write() generator.write(overwrite=True)
# Get its filename # Get its filename
filename = generator.layout.filename filename = generator.layout.filename
# Retrieve the content # Retrieve the content
content = filename_dict[filename].split('\n') with open(filename) as f:
content = f.readlines()
content = ''.join(content).split('\n')
generator.remove() generator.remove()
return content return content
return _impl return _impl
@pytest.fixture()
def patch_configuration(monkeypatch, request):
"""Reads a configuration file from the mock ones prepared for tests
and monkeypatches the right classes to hook it in.
"""
# Class of the module file writer
writer_cls = getattr(request.module, 'writer_cls')
# Module where the module file writer is defined
writer_mod = inspect.getmodule(writer_cls)
# Key for specific settings relative to this module type
writer_key = str(writer_mod.__name__).split('.')[-1]
# Root folder for configuration
root_for_conf = os.path.join(
spack.paths.test_path, 'data', 'modules', writer_key
)
def _impl(filename):
file = os.path.join(root_for_conf, filename + '.yaml')
with open(file) as f:
configuration = yaml.load(f)
monkeypatch.setattr(
spack.modules.common,
'configuration',
configuration
)
monkeypatch.setattr(
writer_mod,
'configuration',
configuration[writer_key]
)
monkeypatch.setattr(
writer_mod,
'configuration_registry',
{}
)
return _impl
@pytest.fixture() @pytest.fixture()
def update_template_dirs(config, monkeypatch): def update_template_dirs(config, monkeypatch):
"""Mocks the template directories for tests""" """Mocks the template directories for tests"""

View File

@ -133,8 +133,10 @@ def test_blacklist(self, modulefile_content, module_configuration):
assert len([x for x in content if 'is-loaded' in x]) == 1 assert len([x for x in content if 'is-loaded' in x]) == 1
assert len([x for x in content if 'module load ' in x]) == 1 assert len([x for x in content if 'module load ' in x]) == 1
# Returns a StringIO instead of a string as no module file was written # Catch "Exception" to avoid using FileNotFoundError on Python 3
with pytest.raises(AttributeError): # and IOError on Python 2 or common bases like EnvironmentError
# which are not officially documented
with pytest.raises(Exception):
modulefile_content('callpath arch=x86-linux') modulefile_content('callpath arch=x86-linux')
content = modulefile_content('zmpi arch=x86-linux') content = modulefile_content('zmpi arch=x86-linux')