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
# DEPRECATED: remove blacklist in v0.20
[docs] @pytest.mark.parametrize("config_name", ["alter_environment", "blacklist_environment"]) def test_alter_environment(self, modulefile_content, module_configuration, config_name): """Tests modifications to run-time environment.""" module_configuration(config_name) 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] @pytest.mark.parametrize("config_name", ["exclude", "blacklist"]) def test_exclude(self, modulefile_content, module_configuration, config_name): """Tests excluding the generation of selected modules.""" module_configuration(config_name) 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."' assert short_description in content
[docs] @pytest.mark.regression("4400") @pytest.mark.db @pytest.mark.parametrize("config_name", ["exclude_implicits", "blacklist_implicits"]) def test_exclude_implicits( self, modulefile_content, module_configuration, database, config_name ): module_configuration(config_name) # 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.excluded # 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.excluded
[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