Source code for spack.test.modules.tcl

# Copyright 2013-2022 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 sys

import pytest

import spack.modules.common
import spack.modules.tcl
import spack.spec

mpich_spec_string = 'mpich@3.0.4'
mpileaks_spec_string = 'mpileaks'
libdwarf_spec_string = 'libdwarf target=x86_64'

#: Class of the writer tested in this module
writer_cls = spack.modules.tcl.TclModulefileWriter

pytestmark = pytest.mark.skipif(sys.platform == "win32",
                                reason="does not run on windows")


[docs]@pytest.mark.usefixtures('config', 'mock_packages', 'mock_module_filename') class TestTcl(object):
[docs] def test_simple_case(self, modulefile_content, module_configuration): """Tests the generation of a simple TCL module file.""" module_configuration('autoload_direct') content = modulefile_content(mpich_spec_string) assert 'module-whatis "mpich @3.0.4"' in content
[docs] def test_autoload_direct(self, modulefile_content, module_configuration): """Tests the automatic loading of direct dependencies.""" module_configuration('autoload_direct') content = modulefile_content(mpileaks_spec_string) assert len([x for x in content if 'is-loaded' in x]) == 2 assert len([x for x in content if 'module load ' in x]) == 2 # dtbuild1 has # - 1 ('run',) dependency # - 1 ('build','link') dependency # - 1 ('build',) dependency # Just make sure the 'build' dependency is not there content = modulefile_content('dtbuild1') assert len([x for x in content if 'is-loaded' in x]) == 2 assert len([x for x in content if 'module load ' in x]) == 2 # The configuration file sets the verbose keyword to False messages = [x for x in content if 'puts stderr "Autoloading' in x] assert len(messages) == 0
[docs] def test_autoload_all(self, modulefile_content, module_configuration): """Tests the automatic loading of all dependencies.""" module_configuration('autoload_all') content = modulefile_content(mpileaks_spec_string) assert len([x for x in content if 'is-loaded' in x]) == 5 assert len([x for x in content if 'module load ' in x]) == 5 # dtbuild1 has # - 1 ('run',) dependency # - 1 ('build','link') dependency # - 1 ('build',) dependency # Just make sure the 'build' dependency is not there content = modulefile_content('dtbuild1') assert len([x for x in content if 'is-loaded' in x]) == 2 assert len([x for x in content if 'module load ' in x]) == 2 # The configuration file sets the verbose keyword to True messages = [x for x in content if 'puts stderr "Autoloading' in x] assert len(messages) == 2
[docs] def test_prerequisites_direct( self, modulefile_content, module_configuration ): """Tests asking direct dependencies as prerequisites.""" module_configuration('prerequisites_direct') content = modulefile_content('mpileaks target=x86_64') assert len([x for x in content if 'prereq' in x]) == 2
[docs] def test_prerequisites_all(self, modulefile_content, module_configuration): """Tests asking all dependencies as prerequisites.""" module_configuration('prerequisites_all') content = modulefile_content('mpileaks target=x86_64') assert len([x for x in content if 'prereq' in x]) == 5
[docs] def test_alter_environment(self, modulefile_content, module_configuration): """Tests modifications to run-time environment.""" module_configuration('alter_environment') content = modulefile_content('mpileaks platform=test target=x86_64') assert len([x for x in content if x.startswith('prepend-path CMAKE_PREFIX_PATH') ]) == 0 assert len([x for x in content if 'setenv FOO "foo"' in x]) == 1 assert len([ x for x in content if 'setenv OMPI_MCA_mpi_leave_pinned "1"' in x ]) == 1 assert len([ x for x in content if 'setenv OMPI_MCA_MPI_LEAVE_PINNED "1"' in x ]) == 0 assert len([x for x in content if 'unsetenv BAR' in x]) == 1 assert len([x for x in content if 'setenv MPILEAKS_ROOT' in x]) == 1 content = modulefile_content( 'libdwarf platform=test target=core2' ) assert len([x for x in content if x.startswith('prepend-path CMAKE_PREFIX_PATH') ]) == 0 assert len([x for x in content if 'setenv FOO "foo"' in x]) == 0 assert len([x for x in content if 'unsetenv BAR' in x]) == 0 assert len([x for x in content if 'is-loaded foo/bar' in x]) == 1 assert len([x for x in content if 'module load foo/bar' in x]) == 1 assert len([x for x in content if 'setenv LIBDWARF_ROOT' in x]) == 1
[docs] def test_blacklist(self, modulefile_content, module_configuration): """Tests blacklisting the generation of selected modules.""" module_configuration('blacklist') content = modulefile_content('mpileaks ^zmpi') 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 # Catch "Exception" to avoid using FileNotFoundError on Python 3 # and IOError on Python 2 or common bases like EnvironmentError # which are not officially documented with pytest.raises(Exception): modulefile_content('callpath target=x86_64') content = modulefile_content('zmpi target=x86_64') 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
[docs] def test_naming_scheme_compat(self, factory, module_configuration): """Tests backwards compatibility for naming_scheme key""" module_configuration('naming_scheme') # Test we read the expected configuration for the naming scheme writer, _ = factory('mpileaks') expected = { 'all': '{name}/{version}-{compiler.name}' } assert writer.conf.projections == expected projection = writer.spec.format(writer.conf.projections['all']) assert projection in writer.layout.use_name
[docs] def test_projections_specific(self, factory, module_configuration): """Tests reading the correct naming scheme.""" # This configuration has no error, so check the conflicts directives # are there module_configuration('projections') # Test we read the expected configuration for the naming scheme writer, _ = factory('mpileaks') expected = { 'all': '{name}/{version}-{compiler.name}', 'mpileaks': '{name}-mpiprojection' } assert writer.conf.projections == expected projection = writer.spec.format(writer.conf.projections['mpileaks']) assert projection in writer.layout.use_name
[docs] def test_projections_all(self, factory, module_configuration): """Tests reading the correct naming scheme.""" # This configuration has no error, so check the conflicts directives # are there module_configuration('projections') # Test we read the expected configuration for the naming scheme writer, _ = factory('libelf') expected = { 'all': '{name}/{version}-{compiler.name}', 'mpileaks': '{name}-mpiprojection' } assert writer.conf.projections == expected projection = writer.spec.format(writer.conf.projections['all']) assert projection in writer.layout.use_name
[docs] def test_invalid_naming_scheme( self, factory, module_configuration, mock_module_filename ): """Tests the evaluation of an invalid naming scheme.""" module_configuration('invalid_naming_scheme') # Test that having invalid tokens in the naming scheme raises # a RuntimeError writer, _ = factory('mpileaks') with pytest.raises(RuntimeError): writer.layout.use_name
[docs] def test_invalid_token_in_env_name( self, factory, module_configuration, mock_module_filename ): """Tests setting environment variables with an invalid name.""" module_configuration('invalid_token_in_env_var_name') writer, _ = factory('mpileaks') with pytest.raises(RuntimeError): writer.write()
[docs] def test_conflicts(self, modulefile_content, module_configuration): """Tests adding conflicts to the module.""" # This configuration has no error, so check the conflicts directives # are there module_configuration('conflicts') content = modulefile_content('mpileaks') assert len([x for x in content if x.startswith('conflict')]) == 2 assert len([x for x in content if x == 'conflict mpileaks']) == 1 assert len([x for x in content if x == 'conflict intel/14.0.1']) == 1 # This configuration is inconsistent, check an error is raised module_configuration('wrong_conflicts') with pytest.raises(SystemExit): modulefile_content('mpileaks')
[docs] def test_module_index( self, module_configuration, factory, tmpdir_factory): module_configuration('suffix') w1, s1 = factory('mpileaks') w2, s2 = factory('callpath') w3, s3 = factory('openblas') test_root = str(tmpdir_factory.mktemp('module-root')) spack.modules.common.generate_module_index(test_root, [w1, w2]) index = spack.modules.common.read_module_index(test_root) assert index[s1.dag_hash()].use_name == w1.layout.use_name assert index[s2.dag_hash()].path == w2.layout.filename spack.modules.common.generate_module_index(test_root, [w3]) index = spack.modules.common.read_module_index(test_root) assert len(index) == 3 assert index[s1.dag_hash()].use_name == w1.layout.use_name assert index[s2.dag_hash()].path == w2.layout.filename spack.modules.common.generate_module_index( test_root, [w3], overwrite=True) index = spack.modules.common.read_module_index(test_root) assert len(index) == 1 assert index[s3.dag_hash()].use_name == w3.layout.use_name
[docs] def test_suffixes(self, module_configuration, factory): """Tests adding suffixes to module file name.""" module_configuration('suffix') writer, spec = factory('mpileaks+debug target=x86_64') assert 'foo' in writer.layout.use_name assert 'foo-foo' not in writer.layout.use_name writer, spec = factory('mpileaks~debug target=x86_64') assert 'foo-bar' in writer.layout.use_name assert 'baz' not in writer.layout.use_name writer, spec = factory('mpileaks~debug+opt target=x86_64') assert 'baz-foo-bar' in writer.layout.use_name
[docs] def test_setup_environment(self, modulefile_content, module_configuration): """Tests the internal set-up of run-time environment.""" module_configuration('suffix') content = modulefile_content('mpileaks') assert len([x for x in content if 'setenv FOOBAR' in x]) == 1 assert len( [x for x in content if 'setenv FOOBAR "mpileaks"' in x] ) == 1 spec = spack.spec.Spec('mpileaks') spec.concretize() content = modulefile_content(str(spec['callpath'])) assert len([x for x in content if 'setenv FOOBAR' in x]) == 1 assert len( [x for x in content if 'setenv FOOBAR "callpath"' in x] ) == 1
[docs] def test_override_config(self, module_configuration, factory): """Tests overriding some sections of the configuration file.""" module_configuration('override_config') writer, spec = factory('mpileaks~opt target=x86_64') assert 'mpich-static' in writer.layout.use_name assert 'over' not in writer.layout.use_name assert 'ridden' not in writer.layout.use_name writer, spec = factory('mpileaks+opt target=x86_64') assert 'over-ridden' in writer.layout.use_name assert 'mpich' not in writer.layout.use_name assert 'static' not in writer.layout.use_name
[docs] def test_override_template_in_package( self, modulefile_content, module_configuration ): """Tests overriding a template from and attribute in the package.""" module_configuration('autoload_direct') content = modulefile_content('override-module-templates') assert 'Override successful!' in content
[docs] def test_override_template_in_modules_yaml( self, modulefile_content, module_configuration ): """Tests overriding a template from `modules.yaml`""" module_configuration('override_template') content = modulefile_content('override-module-templates') assert 'Override even better!' in content content = modulefile_content('mpileaks target=x86_64') assert 'Override even better!' in content
[docs] def test_extend_context( self, modulefile_content, module_configuration ): """Tests using a package defined context""" module_configuration('autoload_direct') content = modulefile_content('override-context-templates') assert 'puts stderr "sentence from package"' in content short_description = 'module-whatis "This package updates the context for TCL modulefiles."' # NOQA: ignore=E501 assert short_description in content
[docs] @pytest.mark.regression('4400') @pytest.mark.db def test_blacklist_implicits( self, modulefile_content, module_configuration, database ): module_configuration('blacklist_implicits') # mpileaks has been installed explicitly when setting up # the tests database mpileaks_specs = database.query('mpileaks') for item in mpileaks_specs: writer = writer_cls(item, 'default') assert not writer.conf.blacklisted # callpath is a dependency of mpileaks, and has been pulled # in implicitly callpath_specs = database.query('callpath') for item in callpath_specs: writer = writer_cls(item, 'default') assert writer.conf.blacklisted
[docs] @pytest.mark.regression('9624') @pytest.mark.db def test_autoload_with_constraints( self, modulefile_content, module_configuration, database ): """Tests the automatic loading of direct dependencies.""" module_configuration('autoload_with_constraints') # Test the mpileaks that should have the autoloaded dependencies content = modulefile_content('mpileaks ^mpich2') assert len([x for x in content if 'is-loaded' in x]) == 2 # Test the mpileaks that should NOT have the autoloaded dependencies content = modulefile_content('mpileaks ^mpich') assert len([x for x in content if 'is-loaded' in x]) == 0
[docs] def test_modules_no_arch(self, factory, module_configuration): module_configuration('no_arch') module, spec = factory(mpileaks_spec_string) path = module.layout.filename assert str(spec.os) not in path