diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py index 19f5582bce4..aeba8c5bf28 100644 --- a/lib/spack/spack/directory_layout.py +++ b/lib/spack/spack/directory_layout.py @@ -23,7 +23,7 @@ from spack.error import SpackError default_projections = { - "all": "{architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash}" + "all": "{architecture.platform}/{architecture.target}/{name}-{version}-{hash}" } diff --git a/lib/spack/spack/reporters/cdash.py b/lib/spack/spack/reporters/cdash.py index d2da8bbed2a..c917a9339aa 100644 --- a/lib/spack/spack/reporters/cdash.py +++ b/lib/spack/spack/reporters/cdash.py @@ -173,7 +173,7 @@ def build_report_for_package(self, report_dir, package, duration): # something went wrong pre-cdash "configure" phase b/c we have an exception and only # "update" was encounterd. # dump the report in the configure line so teams can see what the issue is - if len(phases_encountered) == 1 and package["exception"]: + if len(phases_encountered) == 1 and package.get("exception"): # TODO this mapping is not ideal since these are pre-configure errors # we need to determine if a more appropriate cdash phase can be utilized # for now we will add a message to the log explaining this diff --git a/lib/spack/spack/test/bindist.py b/lib/spack/spack/test/bindist.py index c88e50332dd..5819756a839 100644 --- a/lib/spack/spack/test/bindist.py +++ b/lib/spack/spack/test/bindist.py @@ -392,7 +392,7 @@ def test_spec_needs_rebuild(monkeypatch, tmpdir): s = Spec("libdwarf").concretized() # Install a package - install_cmd(s.name) + install_cmd("--fake", s.name) # Put installed package in the buildcache buildcache_cmd("push", "-u", mirror_dir.strpath, s.name) @@ -421,7 +421,7 @@ def test_generate_index_missing(monkeypatch, tmpdir, mutable_config): s = Spec("libdwarf").concretized() # Install a package - install_cmd("--no-cache", s.name) + install_cmd("--fake", "--no-cache", s.name) # Create a buildcache and update index buildcache_cmd("push", "-u", mirror_dir.strpath, s.name) @@ -573,11 +573,8 @@ def test_install_legacy_buildcache_layout(mutable_config, compiler_factory, inst where the .spack file contained a repeated spec.json and another compressed archive file containing the install tree. This test makes sure we can still read that layout.""" - mutable_config.set( - "compilers", [compiler_factory(spec="gcc@4.5.0", operating_system="debian6")] - ) legacy_layout_dir = os.path.join(test_path, "data", "mirrors", "legacy_layout") - mirror_url = "file://{0}".format(legacy_layout_dir) + mirror_url = f"file://{legacy_layout_dir}" filename = ( "test-debian6-core2-gcc-4.5.0-archive-files-2.0-" "l3vdiqvbobmspwyb4q2b62fz6nitd4hk.spec.json" @@ -586,9 +583,7 @@ def test_install_legacy_buildcache_layout(mutable_config, compiler_factory, inst mirror_cmd("add", "--scope", "site", "test-legacy-layout", mirror_url) output = install_cmd("--no-check-signature", "--cache-only", "-f", spec_json_path, output=str) mirror_cmd("rm", "--scope=site", "test-legacy-layout") - expect_line = ( - "Extracting archive-files-2.0-" "l3vdiqvbobmspwyb4q2b62fz6nitd4hk from binary cache" - ) + expect_line = "Extracting archive-files-2.0-l3vdiqvbobmspwyb4q2b62fz6nitd4hk from binary cache" assert expect_line in output @@ -1190,10 +1185,11 @@ def test_get_valid_spec_file_no_json(tmp_path, filename): bindist._get_valid_spec_file(str(tmp_path / filename), max_supported_layout=1) -def test_download_tarball_with_unsupported_layout_fails(tmp_path, mutable_config, capsys): +def test_download_tarball_with_unsupported_layout_fails( + tmp_path, mock_packages, mutable_config, capsys +): layout_version = bindist.CURRENT_BUILD_CACHE_LAYOUT_VERSION + 1 - spec = Spec("gmake@4.4.1%gcc@13.1.0 arch=linux-ubuntu23.04-zen2") - spec._mark_concrete() + spec = Spec("pkg-c").concretized() spec_dict = spec.to_dict() spec_dict["buildcache_layout_version"] = layout_version diff --git a/lib/spack/spack/test/bootstrap.py b/lib/spack/spack/test/bootstrap.py index f5372ebc71e..883c3b9bef9 100644 --- a/lib/spack/spack/test/bootstrap.py +++ b/lib/spack/spack/test/bootstrap.py @@ -14,6 +14,8 @@ import spack.store import spack.util.path +from .conftest import _true + @pytest.fixture def active_mock_environment(mutable_config, mutable_mock_env_path): @@ -94,12 +96,14 @@ def test_raising_exception_if_bootstrap_disabled(mutable_config): spack.bootstrap.config.store_path() -def test_raising_exception_module_importable(): +def test_raising_exception_module_importable(config, monkeypatch): + monkeypatch.setattr(spack.bootstrap.core, "source_is_enabled", _true) with pytest.raises(ImportError, match='cannot bootstrap the "asdf" Python module'): spack.bootstrap.core.ensure_module_importable_or_raise("asdf") -def test_raising_exception_executables_in_path(): +def test_raising_exception_executables_in_path(config, monkeypatch): + monkeypatch.setattr(spack.bootstrap.core, "source_is_enabled", _true) with pytest.raises(RuntimeError, match="cannot bootstrap any of the asdf, fdsa executables"): spack.bootstrap.core.ensure_executables_in_path_or_raise(["asdf", "fdsa"], "python") @@ -219,16 +223,12 @@ def test_source_is_disabled(mutable_config): # Get the configuration dictionary of the current bootstrapping source conf = next(iter(spack.bootstrap.core.bootstrapping_sources())) - # The source is not explicitly enabled or disabled, so the following - # call should raise to skip using it for bootstrapping - with pytest.raises(ValueError): - spack.bootstrap.core.source_is_enabled_or_raise(conf) + # The source is not explicitly enabled or disabled, so the following should return False + assert not spack.bootstrap.core.source_is_enabled(conf) - # Try to explicitly disable the source and verify that the behavior - # is the same as above + # Try to explicitly disable the source and verify that the behavior is the same as above spack.config.add("bootstrap:trusted:{0}:{1}".format(conf["name"], False)) - with pytest.raises(ValueError): - spack.bootstrap.core.source_is_enabled_or_raise(conf) + assert not spack.bootstrap.core.source_is_enabled(conf) @pytest.mark.regression("45247") diff --git a/lib/spack/spack/test/build_environment.py b/lib/spack/spack/test/build_environment.py index dd42d5cb458..a6b5f733e7a 100644 --- a/lib/spack/spack/test/build_environment.py +++ b/lib/spack/spack/test/build_environment.py @@ -5,7 +5,6 @@ import os import platform import posixpath -import sys import pytest @@ -15,7 +14,6 @@ from llnl.util.filesystem import HeaderList, LibraryList import spack.build_environment -import spack.compiler import spack.compilers import spack.config import spack.deptypes as dt @@ -97,7 +95,7 @@ def build_environment(working_env): @pytest.fixture -def ensure_env_variables(config, mock_packages, monkeypatch, working_env): +def ensure_env_variables(mutable_config, mock_packages, monkeypatch, working_env): """Returns a function that takes a dictionary and updates os.environ for the test lifetime accordingly. Plugs-in mock config and repo. """ @@ -162,20 +160,24 @@ def test_static_to_shared_library(build_environment): @pytest.mark.regression("8345") -@pytest.mark.usefixtures("config", "mock_packages") -def test_cc_not_changed_by_modules(monkeypatch, working_env): - s = spack.spec.Spec("cmake") - s.concretize() - pkg = s.package +@pytest.mark.usefixtures("mock_packages") +@pytest.mark.not_on_windows("Module files are not supported on Windows") +def test_cc_not_changed_by_modules(monkeypatch, mutable_config, working_env, compiler_factory): + """Tests that external module files that are loaded cannot change the + CC environment variable. + """ + gcc_entry = compiler_factory(spec="gcc@14.0.1 languages=c,c++") + gcc_entry["modules"] = ["some_module"] + mutable_config.set("packages", {"gcc": {"externals": [gcc_entry]}}) def _set_wrong_cc(x): os.environ["CC"] = "NOT_THIS_PLEASE" os.environ["ANOTHER_VAR"] = "THIS_IS_SET" monkeypatch.setattr(spack.build_environment, "load_module", _set_wrong_cc) - monkeypatch.setattr(pkg.compiler, "modules", ["some_module"]) - spack.build_environment.setup_package(pkg, False) + s = spack.spec.Spec("cmake %gcc@14").concretized() + spack.build_environment.setup_package(s.package, dirty=False) assert os.environ["CC"] != "NOT_THIS_PLEASE" assert os.environ["ANOTHER_VAR"] == "THIS_IS_SET" @@ -186,7 +188,7 @@ def test_setup_dependent_package_inherited_modules( ): # This will raise on regression s = spack.spec.Spec("cmake-client-inheritor").concretized() - PackageInstaller([s.package]).install() + PackageInstaller([s.package], fake=True).install() @pytest.mark.parametrize( @@ -266,22 +268,30 @@ def test_setup_dependent_package_inherited_modules( ], ) def test_compiler_config_modifications( - initial, modifications, expected, ensure_env_variables, monkeypatch + initial, + modifications, + expected, + ensure_env_variables, + compiler_factory, + mutable_config, + monkeypatch, ): # Set the environment as per prerequisites ensure_env_variables(initial) + gcc_entry = compiler_factory(spec="gcc@14.0.1 languages=c,c++") + gcc_entry["extra_attributes"]["environment"] = modifications + mutable_config.set("packages", {"gcc": {"externals": [gcc_entry]}}) + def platform_pathsep(pathlist): if Path.platform_path == Path.windows: pathlist = pathlist.replace(":", ";") return convert_to_platform_path(pathlist) - # Monkeypatch a pkg.compiler.environment with the required modifications - pkg = spack.spec.Spec("cmake").concretized().package - monkeypatch.setattr(pkg.compiler, "environment", modifications) + pkg = spack.spec.Spec("cmake %gcc@14").concretized().package # Trigger the modifications - spack.build_environment.setup_package(pkg, False) + spack.build_environment.setup_package(pkg, dirty=False) # Check they were applied for name, value in expected.items(): @@ -292,25 +302,6 @@ def platform_pathsep(pathlist): assert name not in os.environ -def test_compiler_custom_env(config, mock_packages, monkeypatch, working_env): - if sys.platform == "win32": - test_path = r"C:\test\path\element\custom-env" + "\\" - else: - test_path = r"/test/path/element/custom-env/" - - def custom_env(pkg, env): - env.prepend_path("PATH", test_path) - env.append_flags("ENV_CUSTOM_CC_FLAGS", "--custom-env-flag1") - - pkg = spack.spec.Spec("cmake").concretized().package - monkeypatch.setattr(pkg.compiler, "setup_custom_environment", custom_env) - spack.build_environment.setup_package(pkg, False) - - # Note: trailing slash may be stripped by internal logic - assert test_path[:-1] in os.environ["PATH"] - assert "--custom-env-flag1" in os.environ["ENV_CUSTOM_CC_FLAGS"] - - def test_external_config_env(mock_packages, mutable_config, working_env): cmake_config = { "externals": [ @@ -330,25 +321,27 @@ def test_external_config_env(mock_packages, mutable_config, working_env): @pytest.mark.regression("9107") -def test_spack_paths_before_module_paths(config, mock_packages, monkeypatch, working_env): - s = spack.spec.Spec("cmake") - s.concretize() - pkg = s.package +@pytest.mark.not_on_windows("Windows does not support module files") +def test_spack_paths_before_module_paths( + mutable_config, mock_packages, compiler_factory, monkeypatch, working_env +): + gcc_entry = compiler_factory(spec="gcc@14.0.1 languages=c,c++") + gcc_entry["modules"] = ["some_module"] + mutable_config.set("packages", {"gcc": {"externals": [gcc_entry]}}) module_path = os.path.join("path", "to", "module") + spack_path = os.path.join(spack.paths.prefix, os.path.join("lib", "spack", "env")) def _set_wrong_cc(x): os.environ["PATH"] = module_path + os.pathsep + os.environ["PATH"] monkeypatch.setattr(spack.build_environment, "load_module", _set_wrong_cc) - monkeypatch.setattr(pkg.compiler, "modules", ["some_module"]) - spack.build_environment.setup_package(pkg, False) + s = spack.spec.Spec("cmake").concretized() - spack_path = os.path.join(spack.paths.prefix, os.path.join("lib", "spack", "env")) + spack.build_environment.setup_package(s.package, dirty=False) paths = os.environ["PATH"].split(os.pathsep) - assert paths.index(spack_path) < paths.index(module_path) @@ -499,15 +492,13 @@ def test_parallel_false_is_not_propagating(default_mock_concretization): ("rpath", "" if platform.system() == "Darwin" else "--disable-new-dtags"), ], ) -def test_setting_dtags_based_on_config(config_setting, expected_flag, config, mock_packages): +def test_setting_dtags_based_on_config( + config_setting, expected_flag, config, mock_packages, working_env +): # Pick a random package to be able to set compiler's variables - s = spack.spec.Spec("cmake") - s.concretize() - pkg = s.package - - env = EnvironmentModifications() + s = spack.spec.Spec("cmake").concretized() with spack.config.override("config:shared_linking", {"type": config_setting, "bind": False}): - spack.build_environment.set_compiler_environment_variables(pkg, env) + env = spack.build_environment.setup_package(s.package, dirty=False) modifications = env.group_by_name() assert "SPACK_DTAGS_TO_STRIP" in modifications assert "SPACK_DTAGS_TO_ADD" in modifications @@ -770,59 +761,24 @@ def test_rpath_with_duplicate_link_deps(): @pytest.mark.parametrize( "compiler_spec,target_name,expected_flags", [ - # Homogeneous compilers + # Semver versions ("gcc@4.7.2", "ivybridge", "-march=core-avx-i -mtune=core-avx-i"), ("clang@3.5", "x86_64", "-march=x86-64 -mtune=generic"), ("apple-clang@9.1.0", "x86_64", "-march=x86-64"), - # Mixed toolchain - ("clang@8.0.0", "broadwell", ""), + ("gcc@=9.2.0", "haswell", "-march=haswell -mtune=haswell"), + # Check that custom string versions are accepted + ("gcc@=9.2.0-foo", "icelake", "-march=icelake-client -mtune=icelake-client"), + # Check that the special case for Apple's clang is treated correctly + # i.e. it won't try to detect the version again + ("apple-clang@=9.1.0", "x86_64", "-march=x86-64"), + # FIXME (compiler as nodes): Check mixed toolchain + # ("clang@8.0.0", "broadwell", ""), ], ) @pytest.mark.filterwarnings("ignore:microarchitecture specific") @pytest.mark.not_on_windows("Windows doesn't support the compiler wrapper") def test_optimization_flags(compiler_spec, target_name, expected_flags, compiler_factory): target = archspec.cpu.TARGETS[target_name] - compiler_dict = compiler_factory(spec=compiler_spec, operating_system="")["compiler"] - if compiler_spec == "clang@8.0.0": - compiler_dict["paths"] = { - "cc": "/path/to/clang-8", - "cxx": "/path/to/clang++-8", - "f77": "/path/to/gfortran-9", - "fc": "/path/to/gfortran-9", - } - compiler = spack.compilers.compiler_from_dict(compiler_dict) - opt_flags = spack.build_environment.optimization_flags(compiler, target) - assert opt_flags == expected_flags - - -@pytest.mark.parametrize( - "compiler_str,real_version,target_str,expected_flags", - [ - ("gcc@=9.2.0", None, "haswell", "-march=haswell -mtune=haswell"), - # Check that custom string versions are accepted - ("gcc@=10foo", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"), - # Check that we run version detection (4.4.0 doesn't support icelake) - ("gcc@=4.4.0-special", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"), - # Check that the special case for Apple's clang is treated correctly - # i.e. it won't try to detect the version again - ("apple-clang@=9.1.0", None, "x86_64", "-march=x86-64"), - ], -) -def test_optimization_flags_with_custom_versions( - compiler_str, - real_version, - target_str, - expected_flags, - monkeypatch, - mutable_config, - compiler_factory, -): - target = archspec.cpu.TARGETS[target_str] - compiler_dict = compiler_factory(spec=compiler_str, operating_system="redhat6") - mutable_config.set("compilers", [compiler_dict]) - if real_version: - monkeypatch.setattr(spack.compiler.Compiler, "get_real_version", lambda x: real_version) - compiler = spack.compilers.compiler_from_dict(compiler_dict["compiler"]) - + compiler = spack.spec.parse_with_version_concrete(compiler_spec) opt_flags = spack.build_environment.optimization_flags(compiler, target) assert opt_flags == expected_flags diff --git a/lib/spack/spack/test/cmd/build_env.py b/lib/spack/spack/test/cmd/build_env.py index 545981bd4af..7334f5d237c 100644 --- a/lib/spack/spack/test/cmd/build_env.py +++ b/lib/spack/spack/test/cmd/build_env.py @@ -13,7 +13,7 @@ build_env = SpackCommand("build-env") -@pytest.mark.parametrize("pkg", [("zlib",), ("zlib", "--")]) +@pytest.mark.parametrize("pkg", [("pkg-c",), ("pkg-c", "--")]) @pytest.mark.usefixtures("config", "mock_packages", "working_env") def test_it_just_runs(pkg): build_env(*pkg) @@ -39,7 +39,7 @@ def test_build_env_requires_a_spec(args): @pytest.mark.usefixtures("config", "mock_packages", "working_env") def test_dump(shell_as, shell, tmpdir): with tmpdir.as_cwd(): - build_env("--dump", _out_file, "zlib") + build_env("--dump", _out_file, "pkg-c") with open(_out_file) as f: if shell == "pwsh": assert any(line.startswith("$Env:PATH") for line in f.readlines()) @@ -52,7 +52,7 @@ def test_dump(shell_as, shell, tmpdir): @pytest.mark.usefixtures("config", "mock_packages", "working_env") def test_pickle(tmpdir): with tmpdir.as_cwd(): - build_env("--pickle", _out_file, "zlib") + build_env("--pickle", _out_file, "pkg-c") environment = pickle.load(open(_out_file, "rb")) assert isinstance(environment, dict) assert "PATH" in environment diff --git a/lib/spack/spack/test/cmd/buildcache.py b/lib/spack/spack/test/cmd/buildcache.py index 840dd61f1ab..4d34c7056fb 100644 --- a/lib/spack/spack/test/cmd/buildcache.py +++ b/lib/spack/spack/test/cmd/buildcache.py @@ -149,7 +149,7 @@ def test_update_key_index( s = Spec("libdwarf").concretized() # Install a package - install(s.name) + install("--fake", s.name) # Put installed package in the buildcache, which, because we're signing # it, should result in the public key getting pushed to the buildcache @@ -179,7 +179,7 @@ def test_buildcache_autopush(tmp_path, install_mockery, mock_fetch): s = Spec("libdwarf").concretized() # Install and generate build cache index - PackageInstaller([s.package], explicit=True).install() + PackageInstaller([s.package], fake=True, explicit=True).install() metadata_file = spack.binary_distribution.tarball_name(s, ".spec.json") @@ -221,7 +221,7 @@ def verify_mirror_contents(): # Install a package and put it in the buildcache s = Spec(out_env_pkg).concretized() - install(s.name) + install("--fake", s.name) buildcache("push", "-u", "-f", src_mirror_url, s.name) env("create", "test") diff --git a/lib/spack/spack/test/cmd/ci.py b/lib/spack/spack/test/cmd/ci.py index 36aa992c639..638f8b6ab06 100644 --- a/lib/spack/spack/test/cmd/ci.py +++ b/lib/spack/spack/test/cmd/ci.py @@ -231,9 +231,9 @@ def test_ci_generate_with_env(ci_generate_test, tmp_path, mock_binary_index): assert yaml_contents["workflow"]["rules"] == [{"when": "always"}] assert "stages" in yaml_contents - assert len(yaml_contents["stages"]) == 5 + assert len(yaml_contents["stages"]) == 6 assert yaml_contents["stages"][0] == "stage-0" - assert yaml_contents["stages"][4] == "stage-rebuild-index" + assert yaml_contents["stages"][5] == "stage-rebuild-index" assert "rebuild-index" in yaml_contents rebuild_job = yaml_contents["rebuild-index"] @@ -1111,7 +1111,7 @@ def test_ci_rebuild_index( with open(tmp_path / "spec.json", "w") as f: f.write(concrete_spec.to_json(hash=ht.dag_hash)) - install_cmd("--add", "-f", str(tmp_path / "spec.json")) + install_cmd("--fake", "--add", "-f", str(tmp_path / "spec.json")) buildcache_cmd("push", "-u", "-f", mirror_url, "callpath") ci_cmd("rebuild-index") diff --git a/lib/spack/spack/test/cmd/compiler.py b/lib/spack/spack/test/cmd/compiler.py index 309042048ab..8075cce6115 100644 --- a/lib/spack/spack/test/cmd/compiler.py +++ b/lib/spack/spack/test/cmd/compiler.py @@ -11,7 +11,6 @@ import spack.compilers.config import spack.config import spack.main -import spack.spec import spack.util.pattern import spack.version diff --git a/lib/spack/spack/test/cmd/config.py b/lib/spack/spack/test/cmd/config.py index 93d7c70483a..85b4d808ee3 100644 --- a/lib/spack/spack/test/cmd/config.py +++ b/lib/spack/spack/test/cmd/config.py @@ -336,7 +336,7 @@ def test_config_add_override_leaf_from_file(mutable_empty_config, tmpdir): def test_config_add_update_dict_from_file(mutable_empty_config, tmpdir): - config("add", "packages:all:compiler:[gcc]") + config("add", "packages:all:require:['%gcc']") # contents to add to file contents = """spack: @@ -358,7 +358,7 @@ def test_config_add_update_dict_from_file(mutable_empty_config, tmpdir): expected = """packages: all: target: [x86_64] - compiler: [gcc] + require: ['%gcc'] """ assert expected == output @@ -608,7 +608,6 @@ def test_config_prefer_upstream( packages = syaml.load(open(cfg_file))["packages"] # Make sure only the non-default variants are set. - assert packages["all"] == {"compiler": ["gcc@=10.2.1"]} assert packages["boost"] == {"variants": "+debug +graph", "version": ["1.63.0"]} assert packages["dependency-install"] == {"version": ["2.0"]} # Ensure that neither variant gets listed for hdf5, since they conflict diff --git a/lib/spack/spack/test/cmd/dependencies.py b/lib/spack/spack/test/cmd/dependencies.py index 3bda72ce7a0..32bb13d6006 100644 --- a/lib/spack/spack/test/cmd/dependencies.py +++ b/lib/spack/spack/test/cmd/dependencies.py @@ -14,7 +14,7 @@ dependencies = SpackCommand("dependencies") -mpis = [ +MPIS = [ "intel-parallel-studio", "low-priority-provider", "mpich", @@ -22,20 +22,21 @@ "multi-provider-mpi", "zmpi", ] -mpi_deps = ["fake"] +COMPILERS = ["gcc", "llvm"] +MPI_DEPS = ["fake"] def test_direct_dependencies(mock_packages): out = dependencies("mpileaks") actual = set(re.split(r"\s+", out.strip())) - expected = set(["callpath"] + mpis) + expected = set(["callpath"] + MPIS + COMPILERS) assert expected == actual def test_transitive_dependencies(mock_packages): out = dependencies("--transitive", "mpileaks") actual = set(re.split(r"\s+", out.strip())) - expected = set(["callpath", "dyninst", "libdwarf", "libelf"] + mpis + mpi_deps) + expected = set(["callpath", "dyninst", "libdwarf", "libelf"] + MPIS + MPI_DEPS + COMPILERS) assert expected == actual @@ -58,12 +59,11 @@ def test_direct_installed_dependencies(mock_packages, database): with color_when(False): out = dependencies("--installed", "mpileaks^mpich") - lines = [line for line in out.strip().split("\n") if not line.startswith("--")] - hashes = set([re.split(r"\s+", line)[0] for line in lines]) + root = spack.store.STORE.db.query_one("mpileaks ^mpich") - expected = set( - [spack.store.STORE.db.query_one(s).dag_hash(7) for s in ["mpich", "callpath^mpich"]] - ) + lines = [line for line in out.strip().split("\n") if line and not line.startswith("--")] + hashes = {re.split(r"\s+", line)[0] for line in lines} + expected = {s.dag_hash(7) for s in root.dependencies()} assert expected == hashes @@ -73,14 +73,10 @@ def test_transitive_installed_dependencies(mock_packages, database): with color_when(False): out = dependencies("--installed", "--transitive", "mpileaks^zmpi") - lines = [line for line in out.strip().split("\n") if not line.startswith("--")] - hashes = set([re.split(r"\s+", line)[0] for line in lines]) + root = spack.store.STORE.db.query_one("mpileaks ^zmpi") - expected = set( - [ - spack.store.STORE.db.query_one(s).dag_hash(7) - for s in ["zmpi", "callpath^zmpi", "fake", "dyninst", "libdwarf", "libelf"] - ] - ) + lines = [line for line in out.strip().split("\n") if line and not line.startswith("--")] + hashes = {re.split(r"\s+", line)[0] for line in lines} + expected = {s.dag_hash(7) for s in root.traverse(root=False)} assert expected == hashes diff --git a/lib/spack/spack/test/cmd/deprecate.py b/lib/spack/spack/test/cmd/deprecate.py index 89e07bd04bb..a25232170e1 100644 --- a/lib/spack/spack/test/cmd/deprecate.py +++ b/lib/spack/spack/test/cmd/deprecate.py @@ -17,16 +17,16 @@ def test_deprecate(mock_packages, mock_archive, mock_fetch, install_mockery): - install("libelf@0.8.13") - install("libelf@0.8.10") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.10") - all_installed = spack.store.STORE.db.query() + all_installed = spack.store.STORE.db.query("libelf") assert len(all_installed) == 2 deprecate("-y", "libelf@0.8.10", "libelf@0.8.13") - non_deprecated = spack.store.STORE.db.query() - all_available = spack.store.STORE.db.query(installed=InstallRecordStatus.ANY) + non_deprecated = spack.store.STORE.db.query("libelf") + all_available = spack.store.STORE.db.query("libelf", installed=InstallRecordStatus.ANY) assert all_available == all_installed assert non_deprecated == spack.store.STORE.db.query("libelf@0.8.13") @@ -39,24 +39,24 @@ def test_deprecate_fails_no_such_package(mock_packages, mock_archive, mock_fetch output = deprecate("-y", "libelf@0.8.10", "libelf@0.8.13", fail_on_error=False) assert "Spec 'libelf@0.8.10' matches no installed packages" in output - install("libelf@0.8.10") + install("--fake", "libelf@0.8.10") output = deprecate("-y", "libelf@0.8.10", "libelf@0.8.13", fail_on_error=False) assert "Spec 'libelf@0.8.13' matches no installed packages" in output -def test_deprecate_install(mock_packages, mock_archive, mock_fetch, install_mockery): - """Tests that the ```-i`` option allows us to deprecate in favor of a spec - that is not yet installed.""" - install("libelf@0.8.10") - - to_deprecate = spack.store.STORE.db.query() +def test_deprecate_install(mock_packages, mock_archive, mock_fetch, install_mockery, monkeypatch): + """Tests that the -i option allows us to deprecate in favor of a spec + that is not yet installed. + """ + install("--fake", "libelf@0.8.10") + to_deprecate = spack.store.STORE.db.query("libelf") assert len(to_deprecate) == 1 deprecate("-y", "-i", "libelf@0.8.10", "libelf@0.8.13") - non_deprecated = spack.store.STORE.db.query() - deprecated = spack.store.STORE.db.query(installed=InstallRecordStatus.DEPRECATED) + non_deprecated = spack.store.STORE.db.query("libelf") + deprecated = spack.store.STORE.db.query("libelf", installed=InstallRecordStatus.DEPRECATED) assert deprecated == to_deprecate assert len(non_deprecated) == 1 assert non_deprecated[0].satisfies("libelf@0.8.13") @@ -64,8 +64,8 @@ def test_deprecate_install(mock_packages, mock_archive, mock_fetch, install_mock def test_deprecate_deps(mock_packages, mock_archive, mock_fetch, install_mockery): """Test that the deprecate command deprecates all dependencies properly.""" - install("libdwarf@20130729 ^libelf@0.8.13") - install("libdwarf@20130207 ^libelf@0.8.10") + install("--fake", "libdwarf@20130729 ^libelf@0.8.13") + install("--fake", "libdwarf@20130207 ^libelf@0.8.10") new_spec = spack.spec.Spec("libdwarf@20130729^libelf@0.8.13").concretized() old_spec = spack.spec.Spec("libdwarf@20130207^libelf@0.8.10").concretized() @@ -81,14 +81,14 @@ def test_deprecate_deps(mock_packages, mock_archive, mock_fetch, install_mockery assert all_available == all_installed assert sorted(all_available) == sorted(deprecated + non_deprecated) - assert sorted(non_deprecated) == sorted(list(new_spec.traverse())) - assert sorted(deprecated) == sorted(list(old_spec.traverse())) + assert sorted(non_deprecated) == sorted(new_spec.traverse()) + assert sorted(deprecated) == sorted([old_spec, old_spec["libelf"]]) def test_uninstall_deprecated(mock_packages, mock_archive, mock_fetch, install_mockery): """Tests that we can still uninstall deprecated packages.""" - install("libelf@0.8.13") - install("libelf@0.8.10") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.10") deprecate("-y", "libelf@0.8.10", "libelf@0.8.13") @@ -104,9 +104,9 @@ def test_uninstall_deprecated(mock_packages, mock_archive, mock_fetch, install_m def test_deprecate_already_deprecated(mock_packages, mock_archive, mock_fetch, install_mockery): """Tests that we can re-deprecate a spec to change its deprecator.""" - install("libelf@0.8.13") - install("libelf@0.8.12") - install("libelf@0.8.10") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.12") + install("--fake", "libelf@0.8.10") deprecated_spec = spack.spec.Spec("libelf@0.8.10").concretized() @@ -117,8 +117,8 @@ def test_deprecate_already_deprecated(mock_packages, mock_archive, mock_fetch, i deprecate("-y", "libelf@0.8.10", "libelf@0.8.13") - non_deprecated = spack.store.STORE.db.query() - all_available = spack.store.STORE.db.query(installed=InstallRecordStatus.ANY) + non_deprecated = spack.store.STORE.db.query("libelf") + all_available = spack.store.STORE.db.query("libelf", installed=InstallRecordStatus.ANY) assert len(non_deprecated) == 2 assert len(all_available) == 3 @@ -129,9 +129,9 @@ def test_deprecate_already_deprecated(mock_packages, mock_archive, mock_fetch, i def test_deprecate_deprecator(mock_packages, mock_archive, mock_fetch, install_mockery): """Tests that when a deprecator spec is deprecated, its deprecatee specs are updated to point to the new deprecator.""" - install("libelf@0.8.13") - install("libelf@0.8.12") - install("libelf@0.8.10") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.12") + install("--fake", "libelf@0.8.10") first_deprecated_spec = spack.spec.Spec("libelf@0.8.10").concretized() second_deprecated_spec = spack.spec.Spec("libelf@0.8.12").concretized() @@ -144,8 +144,8 @@ def test_deprecate_deprecator(mock_packages, mock_archive, mock_fetch, install_m deprecate("-y", "libelf@0.8.12", "libelf@0.8.13") - non_deprecated = spack.store.STORE.db.query() - all_available = spack.store.STORE.db.query(installed=InstallRecordStatus.ANY) + non_deprecated = spack.store.STORE.db.query("libelf") + all_available = spack.store.STORE.db.query("libelf", installed=InstallRecordStatus.ANY) assert len(non_deprecated) == 1 assert len(all_available) == 3 @@ -158,8 +158,8 @@ def test_deprecate_deprecator(mock_packages, mock_archive, mock_fetch, install_m def test_concretize_deprecated(mock_packages, mock_archive, mock_fetch, install_mockery): """Tests that the concretizer throws an error if we concretize to a deprecated spec""" - install("libelf@0.8.13") - install("libelf@0.8.10") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.10") deprecate("-y", "libelf@0.8.10", "libelf@0.8.13") diff --git a/lib/spack/spack/test/cmd/diff.py b/lib/spack/spack/test/cmd/diff.py index e352ce1352b..08bcd877fbe 100644 --- a/lib/spack/spack/test/cmd/diff.py +++ b/lib/spack/spack/test/cmd/diff.py @@ -183,7 +183,7 @@ def test_diff_cmd(install_mockery, mock_fetch, mock_archive, mock_packages): def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages): """Test with and without the --first option""" - install_cmd("mpileaks") + install_cmd("--fake", "mpileaks") # Only one version of mpileaks will work diff_cmd("mpileaks", "mpileaks") @@ -212,14 +212,12 @@ def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages): for dep in ("mpileaks", "callpath", "dyninst", "libelf", "libdwarf", "mpich") ) assert all( - len([diff for diff in result["intersect"] if diff[0] == attr]) == 6 + len([diff for diff in result["intersect"] if diff[0] == attr]) == 7 for attr in ( "version", "node_target", "node_platform", "node_os", - "node_compiler", - "node_compiler_version", "node", "package_hash", "hash", diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 099e6306ac5..3e748716dfc 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -2,6 +2,7 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import contextlib import filecmp import glob import io @@ -210,6 +211,49 @@ def test_env_untrack_managed(tmp_path, capfd): assert f"'{env_name}' is not a tracked env" in out +@pytest.fixture() +def installed_environment(tmp_path, mock_fetch, mock_packages, mock_archive, install_mockery): + spack_yaml = tmp_path / "spack.yaml" + + @contextlib.contextmanager + def _installed_environment(content): + spack_yaml.write_text(content) + with fs.working_dir(tmp_path): + env("create", "test", "./spack.yaml") + with ev.read("test"): + install("--fake") + + test = ev.read("test") + yield test + + return _installed_environment + + +@pytest.fixture() +def template_combinatorial_env(tmp_path): + """Returns a template base environment for tests. Since the environment configuration is + extended using str.format, we need double '{' escaping for the projections. + """ + view_dir = tmp_path / "view" + return f"""\ + spack: + definitions: + - packages: [mpileaks, callpath] + - targets: ['target=x86_64', 'target=core2'] + specs: + - matrix: + - [$packages] + - [$targets] + + view: + combinatorial: + root: {view_dir} + {{view_config}} + projections: + 'all': '{{{{architecture.target}}}}/{{{{name}}}}-{{{{version}}}}' + """ + + def test_add(): e = ev.create("test") e.add("mpileaks") @@ -455,7 +499,7 @@ def test_env_specs_partition(install_mockery, mock_fetch): assert roots_to_install[0].name == "cmake-client" # Single installed root. - e.install_all() + e.install_all(fake=True) roots_already_installed, roots_to_install = e._partition_roots_by_install_status() assert len(roots_already_installed) == 1 assert roots_already_installed[0].name == "cmake-client" @@ -475,7 +519,7 @@ def test_env_install_all(install_mockery, mock_fetch): e = ev.create("test") e.add("cmake-client") e.concretize() - e.install_all() + e.install_all(fake=True) env_specs = e._get_environment_specs() spec = next(x for x in env_specs if x.name == "cmake-client") assert spec.installed @@ -487,7 +531,7 @@ def test_env_install_single_spec(install_mockery, mock_fetch): e = ev.read("test") with e: - install("--add", "cmake-client") + install("--fake", "--add", "cmake-client") e = ev.read("test") assert e.user_specs[0].name == "cmake-client" @@ -508,7 +552,7 @@ def test_env_install_include_concrete_env(unify, install_mockery, mock_fetch, mu combined.write() with combined: - install() + install("--fake") test1_roots = test1.concretized_order test2_roots = test2.concretized_order @@ -556,7 +600,7 @@ def test_env_modifications_error_on_activate(install_mockery, mock_fetch, monkey e = ev.read("test") with e: - install("--add", "cmake-client") + install("--fake", "--add", "cmake-client") def setup_error(pkg, env): raise RuntimeError("cmake-client had issues!") @@ -625,12 +669,12 @@ def test_env_install_two_specs_same_dep(install_mockery, mock_fetch, tmpdir, cap with ev.read("test"): with capsys.disabled(): - out = install() + out = install("--fake") # Ensure both packages reach install phase processing and are installed out = str(out) - assert "depb: Executing phase:" in out - assert "a: Executing phase:" in out + assert "depb: Successfully installed" in out + assert "pkg-a: Successfully installed" in out depb = spack.store.STORE.db.query_one("depb", installed=True) assert depb, "Expected depb to be installed" @@ -1649,9 +1693,7 @@ def test_stage(mock_stage, mock_fetch, install_mockery): def check_stage(spec): spec = Spec(spec).concretized() for dep in spec.traverse(): - stage_name = "{0}{1}-{2}-{3}".format( - stage_prefix, dep.name, dep.version, dep.dag_hash() - ) + stage_name = f"{stage_prefix}{dep.name}-{dep.version}-{dep.dag_hash()}" assert os.path.isdir(os.path.join(root, stage_name)) check_stage("mpileaks") @@ -2821,207 +2863,75 @@ def test_stack_definition_conditional_add_write(tmpdir): assert "zmpi" not in packages_lists[1]["packages"] -def test_stack_combinatorial_view( - tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery -): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - combinatorial: - root: %s - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") +def test_stack_combinatorial_view(installed_environment, template_combinatorial_env, tmp_path): + """Tests creating a default view for a combinatorial stack.""" + view_dir = tmp_path / "view" + with installed_environment(template_combinatorial_env.format(view_config="")) as test: for spec in test._get_environment_specs(): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() and current_dir.is_dir() -def test_stack_view_select(tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - combinatorial: - root: %s - select: ['%%gcc'] - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") +def test_stack_view_select(installed_environment, template_combinatorial_env, tmp_path): + view_dir = tmp_path / "view" + content = template_combinatorial_env.format(view_config="select: ['target=x86_64']\n") + with installed_environment(content) as test: for spec in test._get_environment_specs(): - if spec.satisfies("%gcc"): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) - else: - assert not os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() is spec.satisfies("target=x86_64") -def test_stack_view_exclude(tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - combinatorial: - root: %s - exclude: [callpath] - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") +def test_stack_view_exclude(installed_environment, template_combinatorial_env, tmp_path): + view_dir = tmp_path / "view" + content = template_combinatorial_env.format(view_config="exclude: [callpath]\n") + with installed_environment(content) as test: for spec in test._get_environment_specs(): - if not spec.satisfies("callpath"): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) - else: - assert not os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() is not spec.satisfies("callpath") def test_stack_view_select_and_exclude( - tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery + installed_environment, template_combinatorial_env, tmp_path ): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - combinatorial: - root: %s - select: ['%%gcc'] - exclude: [callpath] - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") + view_dir = tmp_path / "view" + content = template_combinatorial_env.format( + view_config="""select: ['target=x86_64'] + exclude: [callpath] +""" + ) + with installed_environment(content) as test: for spec in test._get_environment_specs(): - if spec.satisfies("%gcc") and not spec.satisfies("callpath"): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) - else: - assert not os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() is ( + spec.satisfies("target=x86_64") and not spec.satisfies("callpath") + ) -def test_view_link_roots(tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - combinatorial: - root: %s - select: ['%%gcc'] - exclude: [callpath] - link: 'roots' - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") +def test_view_link_roots(installed_environment, template_combinatorial_env, tmp_path): + view_dir = tmp_path / "view" + content = template_combinatorial_env.format( + view_config="""select: ['target=x86_64'] + exclude: [callpath] + link: 'roots' + """ + ) + with installed_environment(content) as test: for spec in test._get_environment_specs(): - if spec in test.roots() and ( - spec.satisfies("%gcc") and not spec.satisfies("callpath") - ): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) - else: - assert not os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + expected_exists = spec in test.roots() and ( + spec.satisfies("target=x86_64") and not spec.satisfies("callpath") + ) + assert current_dir.exists() == expected_exists def test_view_link_run(tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery): @@ -3065,169 +2975,84 @@ def test_view_link_run(tmpdir, mock_fetch, mock_packages, mock_archive, install_ @pytest.mark.parametrize("link_type", ["hardlink", "copy", "symlink"]) -def test_view_link_type( - link_type, tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery -): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ +def test_view_link_type(link_type, installed_environment, tmp_path): + view_dir = tmp_path / "view" + with installed_environment( + f"""\ spack: specs: - mpileaks view: default: - root: %s - link_type: %s""" - % (viewdir, link_type) - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") - + root: {view_dir} + link_type: {link_type}""" + ) as test: for spec in test.roots(): - file_path = test.default_view.view()._root - file_to_test = os.path.join(file_path, spec.name) - assert os.path.isfile(file_to_test) - assert os.path.islink(file_to_test) == (link_type == "symlink") + # Assertions are based on the behavior of the "--fake" install + bin_file = pathlib.Path(test.default_view.view()._root) / "bin" / spec.name + assert bin_file.exists() + assert bin_file.is_symlink() == (link_type == "symlink") -def test_view_link_all(tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, callpath] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] +def test_view_link_all(installed_environment, template_combinatorial_env, tmp_path): + view_dir = tmp_path / "view" + content = template_combinatorial_env.format( + view_config="""select: ['target=x86_64'] + exclude: [callpath] + link: 'all' + """ + ) - view: - combinatorial: - root: %s - select: ['%%gcc'] - exclude: [callpath] - link: 'all' - projections: - 'all': '{name}/{version}-{compiler.name}'""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - - test = ev.read("test") + with installed_environment(content) as test: for spec in test._get_environment_specs(): - if spec.satisfies("%gcc") and not spec.satisfies("callpath"): - assert os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) - else: - assert not os.path.exists( - os.path.join(viewdir, spec.name, "%s-%s" % (spec.version, spec.compiler.name)) - ) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() == ( + spec.satisfies("target=x86_64") and not spec.satisfies("callpath") + ) def test_stack_view_activate_from_default( - tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery + installed_environment, template_combinatorial_env, tmp_path ): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, cmake] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - default: - root: %s - select: ['%%gcc']""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - + view_dir = tmp_path / "view" + content = template_combinatorial_env.format(view_config="select: ['target=x86_64']") + # Replace the name of the view + content = content.replace("combinatorial:", "default:") + with installed_environment(content): shell = env("activate", "--sh", "test") - - assert "PATH" in shell - assert os.path.join(viewdir, "bin") in shell + assert "PATH" in shell, shell + assert str(view_dir / "bin") in shell assert "FOOBAR=mpileaks" in shell def test_stack_view_no_activate_without_default( - tmpdir, mock_fetch, mock_packages, mock_archive, install_mockery + installed_environment, template_combinatorial_env, tmp_path ): - filename = str(tmpdir.join("spack.yaml")) - viewdir = str(tmpdir.join("view")) - with open(filename, "w") as f: - f.write( - """\ -spack: - definitions: - - packages: [mpileaks, cmake] - - compilers: ['%%gcc', '%%clang'] - specs: - - matrix: - - [$packages] - - [$compilers] - - view: - not-default: - root: %s - select: ['%%gcc']""" - % viewdir - ) - with tmpdir.as_cwd(): - env("create", "test", "./spack.yaml") - with ev.read("test"): - install() - + view_dir = tmp_path / "view" + content = template_combinatorial_env.format(view_config="select: ['target=x86_64']") + with installed_environment(content): shell = env("activate", "--sh", "test") assert "PATH" not in shell - assert viewdir not in shell + assert str(view_dir) not in shell @pytest.mark.parametrize("include_views", [True, False, "split"]) -def test_stack_view_multiple_views( - tmp_path, - mock_fetch, - mock_packages, - mock_archive, - install_mockery, - mutable_config, - include_views, -): +def test_stack_view_multiple_views(installed_environment, tmp_path, include_views): """Test multiple views as both included views (True), as both environment - views (False), or as one included and the other in the environment.""" + views (False), or as one included and the other in the environment. + """ # Write the view configuration and or manifest file view_filename = tmp_path / "view.yaml" base_content = """\ definitions: - packages: [mpileaks, cmake] - - compilers: ['%gcc', '%clang'] + - targets: ['target=x86_64', 'target=core2'] specs: - matrix: - [$packages] - - [$compilers] + - [$targets] """ include_content = f" include:\n - {view_filename}\n" @@ -3237,17 +3062,17 @@ def test_stack_view_multiple_views( comb_view = """\ {0}combinatorial: {0} root: {1} -{0} exclude: [callpath%gcc] +{0} exclude: [target=core2] {0} projections: """ - projection = " 'all': '{name}/{version}-{compiler.name}'" + projection = " 'all': '{architecture.target}/{name}-{version}'" default_dir = tmp_path / "default-view" default_view = """\ {0}default: {0} root: {1} -{0} select: ['%gcc'] +{0} select: ['target=x86_64'] """ content = "spack:\n" @@ -3272,22 +3097,13 @@ def test_stack_view_multiple_views( content += default_view.format(indent, str(default_dir)) content += comb_view.format(indent, str(comb_dir)) + indent + projection - filename = tmp_path / ev.manifest_name - filename.write_text(content) - - env("create", "test", str(filename)) - with ev.read("test"): - install() - - with ev.read("test") as e: + with installed_environment(content) as e: assert os.path.exists(str(default_dir / "bin")) for spec in e._get_environment_specs(): - spec_subdir = f"{spec.version}-{spec.compiler.name}" - comb_spec_dir = str(comb_dir / spec.name / spec_subdir) - if not spec.satisfies("callpath%gcc"): - assert os.path.exists(comb_spec_dir) - else: - assert not os.path.exists(comb_spec_dir) + if spec.name == "gcc-runtime": + continue + current_dir = comb_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() is not spec.satisfies("target=core2") def test_env_activate_sh_prints_shell_output(tmpdir, mock_stage, mock_fetch, install_mockery): @@ -3656,12 +3472,10 @@ def test_modules_relative_to_views(environment_from_manifest, install_mockery, m assert spec.prefix not in contents -def test_modules_exist_after_env_install( - environment_from_manifest, install_mockery, mock_fetch, monkeypatch -): +def test_modules_exist_after_env_install(installed_environment, monkeypatch): # Some caching issue monkeypatch.setattr(spack.modules.tcl, "configuration_registry", {}) - environment_from_manifest( + with installed_environment( """ spack: specs: @@ -3677,16 +3491,15 @@ def test_modules_exist_after_env_install( roots: tcl: without_view """ - ) - - with ev.read("test") as e: - install() + ) as e: specs = e.all_specs() - for module_set in ("uses_view", "without_view"): modules = glob.glob(f"{e.path}/{module_set}/**/*/*") assert len(modules) == len(specs), "Not all modules were generated" for spec in specs: + if spec.external: + continue + module = next((m for m in modules if os.path.dirname(m).endswith(spec.name)), None) assert module, f"Module for {spec} not found" @@ -3728,7 +3541,7 @@ def test_install_develop_keep_stage( (mpileaks_spec,) = e.all_matching_specs("mpileaks") assert not os.path.exists(libelf_spec.package.stage.path) assert not os.path.exists(mpileaks_spec.package.stage.path) - install() + install("--fake") assert os.path.exists(libelf_spec.package.stage.path) assert not os.path.exists(mpileaks_spec.package.stage.path) @@ -3938,7 +3751,7 @@ def test_environment_query_spec_by_hash(mock_stage, mock_fetch, install_mockery) concretize() with ev.read("test") as e: spec = e.matching_spec("libelf") - install("/{0}".format(spec.dag_hash())) + install("--fake", f"/{spec.dag_hash()}") with ev.read("test") as e: assert not e.matching_spec("libdwarf").installed assert e.matching_spec("libelf").installed @@ -4401,7 +4214,7 @@ def test_env_include_packages_url( ev.activate(env) cfg = spack.config.get("packages") - assert "openmpi" in cfg["all"]["providers"]["mpi"] + assert "mpich" in cfg["all"]["providers"]["mpi"] def test_relative_view_path_on_command_line_is_made_absolute(tmp_path): @@ -4525,7 +4338,7 @@ def test_env_include_mixed_views(tmp_path, mutable_mock_env_path, mutable_config def test_stack_view_multiple_views_same_name( - tmp_path, mock_fetch, mock_packages, mock_archive, install_mockery, mutable_config + installed_environment, template_combinatorial_env, tmp_path ): """Test multiple views with the same name combine settings with precedence given to the options in spack.yaml.""" @@ -4537,58 +4350,52 @@ def test_stack_view_multiple_views_same_name( view: default: root: {default_dir} - select: ['%gcc'] + select: ['target=x86_64'] projections: - all: '{{name}}/{{version}}-{{compiler.name}}' + all: '{{architecture.target}}/{{name}}-{{version}}-from-view' """ view_filename.write_text(default_view) view_dir = tmp_path / "view" - content = f"""\ + with installed_environment( + f"""\ spack: include: - {view_filename} definitions: - packages: [mpileaks, cmake] - - compilers: ['%gcc', '%clang'] + - targets: ['target=x86_64', 'target=core2'] + specs: - matrix: - [$packages] - - [$compilers] + - [$targets] view: default: root: {view_dir} exclude: ['cmake'] projections: - all: '{{name}}/{{compiler.name}}-{{version}}' + all: '{{architecture.target}}/{{name}}-{{version}}' """ - - filename = tmp_path / ev.manifest_name - filename.write_text(content) - - env("create", "test", str(filename)) - with ev.read("test"): - install() - - with ev.read("test") as e: + ) as e: # the view root in the included view should NOT exist assert not os.path.exists(str(default_dir)) for spec in e._get_environment_specs(): # no specs will exist in the included view projection - included_spec_subdir = f"{spec.version}-{spec.compiler.name}" - included_spec_dir = str(view_dir / spec.name / included_spec_subdir) - assert not os.path.exists(included_spec_dir) + base_dir = view_dir / f"{spec.architecture.target}" + included_dir = base_dir / f"{spec.name}-{spec.version}-from-view" + assert not included_dir.exists() - # only specs compiled with %gcc (selected in the included view) that + # only target=x86_64 specs (selected in the included view) that # are also not cmake (excluded in the environment view) should exist - env_spec_subdir = f"{spec.compiler.name}-{spec.version}" - env_spec_dir = str(view_dir / spec.name / env_spec_subdir) - if spec.satisfies("cmake") or spec.satisfies("%clang"): - assert not os.path.exists(env_spec_dir) - else: - assert os.path.exists(env_spec_dir) + if spec.name == "gcc-runtime": + continue + current_dir = view_dir / f"{spec.architecture.target}" / f"{spec.name}-{spec.version}" + assert current_dir.exists() is not ( + spec.satisfies("cmake") or spec.satisfies("target=core2") + ) def test_env_view_resolves_identical_file_conflicts(tmp_path, install_mockery, mock_fetch): diff --git a/lib/spack/spack/test/cmd/find.py b/lib/spack/spack/test/cmd/find.py index 5398dff4691..daae706ac9a 100644 --- a/lib/spack/spack/test/cmd/find.py +++ b/lib/spack/spack/test/cmd/find.py @@ -171,7 +171,7 @@ def _check_json_output(spec_list): def _check_json_output_deps(spec_list): - assert len(spec_list) == 13 + assert len(spec_list) == 15 names = [spec["name"] for spec in spec_list] assert names.count("mpileaks") == 3 @@ -232,21 +232,28 @@ def test_display_json_deps(database, capsys): @pytest.mark.db def test_find_format(database, config): output = find("--format", "{name}-{^mpi.name}", "mpileaks") - assert set(output.strip().split("\n")) == set( - ["mpileaks-zmpi", "mpileaks-mpich", "mpileaks-mpich2"] - ) + assert set(output.strip().split("\n")) == { + "mpileaks-zmpi", + "mpileaks-mpich", + "mpileaks-mpich2", + } - output = find("--format", "{name}-{version}-{compiler.name}-{^mpi.name}", "mpileaks") - assert "installed package" not in output - assert set(output.strip().split("\n")) == set( - ["mpileaks-2.3-gcc-zmpi", "mpileaks-2.3-gcc-mpich", "mpileaks-2.3-gcc-mpich2"] - ) + # FIXME (compiler as nodes): recover the {compiler} in Spec.format + # output = find("--format", "{name}-{version}-{compiler.name}-{^mpi.name}", "mpileaks") + # assert "installed package" not in output + # assert set(output.strip().split("\n")) == { + # "mpileaks-2.3-gcc-zmpi", + # "mpileaks-2.3-gcc-mpich", + # "mpileaks-2.3-gcc-mpich2", + # } output = find("--format", "{name}-{^mpi.name}-{hash:7}", "mpileaks") elements = output.strip().split("\n") - assert set(e[:-7] for e in elements) == set( - ["mpileaks-zmpi-", "mpileaks-mpich-", "mpileaks-mpich2-"] - ) + assert set(e[:-7] for e in elements) == { + "mpileaks-zmpi-", + "mpileaks-mpich-", + "mpileaks-mpich2-", + } # hashes are in base32 for e in elements: @@ -265,6 +272,8 @@ def test_find_format_deps(database, config): dyninst-8.2 libdwarf-20130729 libelf-0.8.13 + gcc-10.2.1 + gcc-runtime-10.2.1 zmpi-1.0 fake-1.0 @@ -275,24 +284,21 @@ def test_find_format_deps(database, config): @pytest.mark.db def test_find_format_deps_paths(database, config): output = find("-dp", "--format", "{name}-{version}", "mpileaks", "^zmpi") - - spec = Spec("mpileaks ^zmpi").concretized() - prefixes = [s.prefix for s in spec.traverse()] - + mpileaks = Spec("mpileaks ^zmpi").concretized() assert ( output - == """\ -mpileaks-2.3 {0} - callpath-1.0 {1} - dyninst-8.2 {2} - libdwarf-20130729 {3} - libelf-0.8.13 {4} - zmpi-1.0 {5} - fake-1.0 {6} + == f"""\ +mpileaks-2.3 {mpileaks.prefix} + callpath-1.0 {mpileaks['callpath'].prefix} + dyninst-8.2 {mpileaks['dyninst'].prefix} + libdwarf-20130729 {mpileaks['libdwarf'].prefix} + libelf-0.8.13 {mpileaks['libelf'].prefix} + gcc-10.2.1 {mpileaks['gcc'].prefix} + gcc-runtime-10.2.1 {mpileaks['gcc-runtime'].prefix} + zmpi-1.0 {mpileaks['zmpi'].prefix} + fake-1.0 {mpileaks['fake'].prefix} -""".format( - *prefixes - ) +""" ) @@ -309,12 +315,6 @@ def test_find_very_long(database, config): ) -@pytest.mark.db -def test_find_show_compiler(database, config): - output = find("--no-groups", "--show-full-compiler", "mpileaks") - assert "mpileaks@2.3%gcc@10.2.1" in output - - @pytest.mark.db def test_find_not_found(database, config, capsys): with capsys.disabled(): @@ -346,7 +346,7 @@ def test_find_prefix_in_env( """Test `find` formats requiring concrete specs work in environments.""" env("create", "test") with ev.read("test"): - install("--add", "mpileaks") + install("--fake", "--add", "mpileaks") find("-p") find("-l") find("-L") @@ -456,7 +456,7 @@ def test_environment_with_version_range_in_compiler_doesnt_fail(tmp_path): with test_environment: output = find() - assert "zlib%gcc@12.1.0" in output + assert "zlib" in output _pkga = ( diff --git a/lib/spack/spack/test/cmd/gc.py b/lib/spack/spack/test/cmd/gc.py index d997be59b2a..02475de70dd 100644 --- a/lib/spack/spack/test/cmd/gc.py +++ b/lib/spack/spack/test/cmd/gc.py @@ -21,7 +21,8 @@ @pytest.mark.db def test_gc_without_build_dependency(mutable_database): assert "There are no unused specs." in gc("-yb") - assert "There are no unused specs." in gc("-y") + # 'gcc' is a pure build dependency in the DB + assert "There are no unused specs." not in gc("-y") @pytest.mark.db @@ -62,7 +63,7 @@ def test_gc_with_environment(mutable_database, mutable_mock_env_path): add("cmake") install() assert mutable_database.query_local("cmake") - output = gc("-y") + output = gc("-by") assert "Restricting garbage collection" in output assert "There are no unused specs" in output diff --git a/lib/spack/spack/test/cmd/install.py b/lib/spack/spack/test/cmd/install.py index 445f376b1b4..f8d0d0293ca 100644 --- a/lib/spack/spack/test/cmd/install.py +++ b/lib/spack/spack/test/cmd/install.py @@ -41,6 +41,16 @@ find = SpackCommand("find") +# @pytest.fixture(autouse=True) +# def gcc_runtime_mock_install(mock_packages, monkeypatch): +# import spack.pkg.builtin.mock.gcc_runtime +# +# def _mock_install(self, spec, prefix): +# mkdir(prefix.lib) +# +# monkeypatch.setattr(spack.pkg.builtin.mock.gcc_runtime.GccRuntime, "install", _mock_install) + + @pytest.fixture() def noop_install(monkeypatch): def noop(*args, **kwargs): @@ -54,14 +64,14 @@ def test_install_package_and_dependency( ): log = "test" with tmpdir.as_cwd(): - install("--log-format=junit", "--log-file={0}".format(log), "libdwarf") + install("--fake", "--log-format=junit", f"--log-file={log}", "libdwarf") files = tmpdir.listdir() - filename = tmpdir.join("{0}.xml".format(log)) + filename = tmpdir.join(f"{log}.xml") assert filename in files content = filename.open().read() - assert 'tests="2"' in content + assert 'tests="3"' in content assert 'failures="0"' in content assert 'errors="0"' in content @@ -97,20 +107,21 @@ def test_install_package_already_installed( tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery ): with tmpdir.as_cwd(): - install("libdwarf") - install("--log-format=junit", "--log-file=test.xml", "libdwarf") + install("--fake", "libdwarf") + install("--fake", "--log-format=junit", "--log-file=test.xml", "libdwarf") files = tmpdir.listdir() filename = tmpdir.join("test.xml") assert filename in files content = filename.open().read() - assert 'tests="2"' in content + print(content) + assert 'tests="4"' in content assert 'failures="0"' in content assert 'errors="0"' in content skipped = [line for line in content.split("\n") if "skipped" in line] - assert len(skipped) == 2 + assert len(skipped) == 4 @pytest.mark.parametrize( @@ -183,9 +194,8 @@ def test_install_with_source(mock_packages, mock_archive, mock_fetch, install_mo def test_install_env_variables(mock_packages, mock_archive, mock_fetch, install_mockery): - spec = Spec("libdwarf") - spec.concretize() - install("libdwarf") + spec = Spec("pkg-c").concretized() + install("pkg-c") assert os.path.isfile(spec.package.install_env_path) @@ -204,11 +214,9 @@ def test_show_log_on_error(mock_packages, mock_archive, mock_fetch, install_mock def test_install_overwrite(mock_packages, mock_archive, mock_fetch, install_mockery): - # Try to install a spec and then to reinstall it. - spec = Spec("libdwarf") - spec.concretize() - - install("libdwarf") + """Tests installing a spec, and then re-installing it in the same prefix.""" + spec = Spec("pkg-c").concretized() + install("pkg-c") # Ignore manifest and install times manifest = os.path.join( @@ -230,7 +238,7 @@ def test_install_overwrite(mock_packages, mock_archive, mock_fetch, install_mock assert bad_md5 != expected_md5 - install("--overwrite", "-y", "libdwarf") + install("--overwrite", "-y", "pkg-c") assert os.path.exists(spec.prefix) assert fs.hash_directory(spec.prefix, ignore=ignores) == expected_md5 @@ -238,13 +246,10 @@ def test_install_overwrite(mock_packages, mock_archive, mock_fetch, install_mock def test_install_overwrite_not_installed(mock_packages, mock_archive, mock_fetch, install_mockery): - # Try to install a spec and then to reinstall it. - spec = Spec("libdwarf") - spec.concretize() - + """Tests that overwrite doesn't fail if the package is not installed""" + spec = Spec("pkg-c").concretized() assert not os.path.exists(spec.prefix) - - install("--overwrite", "-y", "libdwarf") + install("--overwrite", "-y", "pkg-c") assert os.path.exists(spec.prefix) @@ -274,15 +279,11 @@ def test_install_commit(mock_git_version_info, install_mockery, mock_packages, m def test_install_overwrite_multiple(mock_packages, mock_archive, mock_fetch, install_mockery): # Try to install a spec and then to reinstall it. - libdwarf = Spec("libdwarf") - libdwarf.concretize() + libdwarf = Spec("libdwarf").concretized() + cmake = Spec("cmake").concretized() - install("libdwarf") - - cmake = Spec("cmake") - cmake.concretize() - - install("cmake") + install("--fake", "libdwarf") + install("--fake", "cmake") ld_manifest = os.path.join( libdwarf.prefix, @@ -318,7 +319,7 @@ def test_install_overwrite_multiple(mock_packages, mock_archive, mock_fetch, ins assert bad_libdwarf_md5 != expected_libdwarf_md5 assert bad_cmake_md5 != expected_cmake_md5 - install("--overwrite", "-y", "libdwarf", "cmake") + install("--fake", "--overwrite", "-y", "libdwarf", "cmake") assert os.path.exists(libdwarf.prefix) assert os.path.exists(cmake.prefix) @@ -555,10 +556,10 @@ def test_cdash_upload_build_error(tmpdir, mock_fetch, install_mockery, capfd): def test_cdash_upload_clean_build(tmpdir, mock_fetch, install_mockery, capfd): # capfd interferes with Spack's capturing of e.g., Build.xml output with capfd.disabled(), tmpdir.as_cwd(): - install("--log-file=cdash_reports", "--log-format=cdash", "pkg-a") + install("--log-file=cdash_reports", "--log-format=cdash", "pkg-c") report_dir = tmpdir.join("cdash_reports") assert report_dir in tmpdir.listdir() - report_file = report_dir.join("pkg-a_Build.xml") + report_file = report_dir.join("Build.xml") assert report_file in report_dir.listdir() content = report_file.open().read() assert "" in content @@ -575,14 +576,14 @@ def test_cdash_upload_extra_params(tmpdir, mock_fetch, install_mockery, capfd): "--cdash-build=my_custom_build", "--cdash-site=my_custom_site", "--cdash-track=my_custom_track", - "pkg-a", + "pkg-c", ) report_dir = tmpdir.join("cdash_reports") assert report_dir in tmpdir.listdir() - report_file = report_dir.join("pkg-a_Build.xml") + report_file = report_dir.join("Build.xml") assert report_file in report_dir.listdir() content = report_file.open().read() - assert 'Site BuildName="my_custom_build - pkg-a"' in content + assert 'Site BuildName="my_custom_build"' in content assert 'Name="my_custom_site"' in content assert "-my_custom_track" in content @@ -592,17 +593,17 @@ def test_cdash_buildstamp_param(tmpdir, mock_fetch, install_mockery, capfd): # capfd interferes with Spack's capture of e.g., Build.xml output with capfd.disabled(), tmpdir.as_cwd(): cdash_track = "some_mocked_track" - buildstamp_format = "%Y%m%d-%H%M-{0}".format(cdash_track) + buildstamp_format = f"%Y%m%d-%H%M-{cdash_track}" buildstamp = time.strftime(buildstamp_format, time.localtime(int(time.time()))) install( "--log-file=cdash_reports", "--log-format=cdash", - "--cdash-buildstamp={0}".format(buildstamp), - "pkg-a", + f"--cdash-buildstamp={buildstamp}", + "pkg-c", ) report_dir = tmpdir.join("cdash_reports") assert report_dir in tmpdir.listdir() - report_file = report_dir.join("pkg-a_Build.xml") + report_file = report_dir.join("Build.xml") assert report_file in report_dir.listdir() content = report_file.open().read() assert buildstamp in content @@ -616,9 +617,7 @@ def test_cdash_install_from_spec_json( with capfd.disabled(), tmpdir.as_cwd(): spec_json_path = str(tmpdir.join("spec.json")) - pkg_spec = Spec("pkg-a") - pkg_spec.concretize() - + pkg_spec = Spec("pkg-c").concretized() with open(spec_json_path, "w") as fd: fd.write(pkg_spec.to_json(hash=ht.dag_hash)) @@ -634,7 +633,7 @@ def test_cdash_install_from_spec_json( report_dir = tmpdir.join("cdash_reports") assert report_dir in tmpdir.listdir() - report_file = report_dir.join("pkg-a_Configure.xml") + report_file = report_dir.join("Configure.xml") assert report_file in report_dir.listdir() content = report_file.open().read() install_command_regex = re.compile( @@ -643,7 +642,7 @@ def test_cdash_install_from_spec_json( m = install_command_regex.search(content) assert m install_command = m.group(1) - assert "pkg-a@" in install_command + assert "pkg-c@" in install_command @pytest.mark.disable_clean_stage_check @@ -680,7 +679,7 @@ def test_cache_only_fails(tmpdir, mock_fetch, install_mockery, capfd): with capfd.disabled(): out = install("--cache-only", "libdwarf", fail_on_error=False) - assert "Failed to install libelf" in out + assert "Failed to install gcc-runtime" in out assert "Skipping build of libdwarf" in out assert "was not installed" in out @@ -814,12 +813,12 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock # Activate the environment with e: # Assert using --no-add with a spec not in the env fails - inst_out = install("--no-add", "boost", fail_on_error=False, output=str) + inst_out = install("--fake", "--no-add", "boost", fail_on_error=False, output=str) assert "You can add specs to the environment with 'spack add " in inst_out # Without --add, ensure that two packages "a" get installed - inst_out = install("pkg-a", output=str) + inst_out = install("--fake", "pkg-a", output=str) assert len([x for x in e.all_specs() if x.installed and x.name == "pkg-a"]) == 2 # Install an unambiguous dependency spec (that already exists as a dep @@ -853,14 +852,14 @@ def test_install_no_add_in_env(tmpdir, mock_fetch, install_mockery, mutable_mock # root of the environment as well as installed. assert b_spec not in e.roots() - install("--add", "pkg-b") + install("--fake", "--add", "pkg-b") assert b_spec in e.roots() assert b_spec not in e.uninstalled_specs() # Install a novel spec with --add and make sure it is added as a root # and installed. - install("--add", "bowtie") + install("--fake", "--add", "bowtie") assert any([s.name == "bowtie" for s in e.roots()]) assert not any([s.name == "bowtie" for s in e.uninstalled_specs()]) @@ -888,7 +887,7 @@ def test_cdash_auth_token(tmpdir, mock_fetch, install_mockery, monkeypatch, capf # capfd interferes with Spack's capturing with tmpdir.as_cwd(), capfd.disabled(): monkeypatch.setenv("SPACK_CDASH_AUTH_TOKEN", "asdf") - out = install("-v", "--log-file=cdash_reports", "--log-format=cdash", "pkg-a") + out = install("--fake", "-v", "--log-file=cdash_reports", "--log-format=cdash", "pkg-a") assert "Using CDash auth token from environment" in out @@ -949,7 +948,7 @@ def test_install_env_with_tests_all( with ev.read("test"): test_dep = Spec("test-dependency").concretized() add("depb") - install("--test", "all") + install("--fake", "--test", "all") assert os.path.exists(test_dep.prefix) @@ -961,7 +960,7 @@ def test_install_env_with_tests_root( with ev.read("test"): test_dep = Spec("test-dependency").concretized() add("depb") - install("--test", "root") + install("--fake", "--test", "root") assert not os.path.exists(test_dep.prefix) diff --git a/lib/spack/spack/test/cmd/load.py b/lib/spack/spack/test/cmd/load.py index 1c36bcc86ab..3d35682dcef 100644 --- a/lib/spack/spack/test/cmd/load.py +++ b/lib/spack/spack/test/cmd/load.py @@ -30,7 +30,7 @@ def test_manpath_trailing_colon( """Test that the commands generated by load add the MANPATH prefix inspections. Also test that Spack correctly preserves the default/existing manpath search path via a trailing colon""" - install("mpileaks") + install("--fake", "mpileaks") sh_out = load(shell, "mpileaks") lines = [line.strip("\n") for line in sh_out.split(commandsep)] @@ -49,7 +49,7 @@ def test_load_recursive(install_mockery, mock_fetch, mock_archive, mock_packages def test_load_shell(shell, set_command): """Test that `spack load` applies prefix inspections of its required runtime deps in topo-order""" - install("mpileaks") + install("--fake", "mpileaks") mpileaks_spec = spack.spec.Spec("mpileaks").concretized() # Ensure our reference variable is clean. @@ -116,7 +116,7 @@ def test_load_includes_run_env( """Tests that environment changes from the package's `setup_run_environment` method are added to the user environment in addition to the prefix inspections""" - install("mpileaks") + install("--fake", "mpileaks") shell_out = load(shell, "mpileaks") @@ -126,8 +126,8 @@ def test_load_includes_run_env( def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages): """Test with and without the --first option""" shell = "--bat" if sys.platform == "win32" else "--sh" - install("libelf@0.8.12") - install("libelf@0.8.13") + install("--fake", "libelf@0.8.12") + install("--fake", "libelf@0.8.13") # Now there are two versions of libelf, which should cause an error out = load(shell, "libelf", fail_on_error=False) @@ -140,7 +140,7 @@ def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages): def test_load_fails_no_shell(install_mockery, mock_fetch, mock_archive, mock_packages): """Test that spack load prints an error message without a shell.""" - install("mpileaks") + install("--fake", "mpileaks") out = load("mpileaks", fail_on_error=False) assert "To set up shell support" in out @@ -166,7 +166,7 @@ def test_unload( ): """Tests that any variables set in the user environment are undone by the unload command""" - install("mpileaks") + install("--fake", "mpileaks") mpileaks_spec = spack.spec.Spec("mpileaks").concretized() # Set so unload has something to do @@ -187,7 +187,7 @@ def test_unload_fails_no_shell( install_mockery, mock_fetch, mock_archive, mock_packages, working_env ): """Test that spack unload prints an error message without a shell.""" - install("mpileaks") + install("--fake", "mpileaks") mpileaks_spec = spack.spec.Spec("mpileaks").concretized() os.environ[uenv.spack_loaded_hashes_var] = mpileaks_spec.dag_hash() diff --git a/lib/spack/spack/test/cmd/location.py b/lib/spack/spack/test/cmd/location.py index 25fa02a6b08..fc043b88749 100644 --- a/lib/spack/spack/test/cmd/location.py +++ b/lib/spack/spack/test/cmd/location.py @@ -37,8 +37,8 @@ def mock_spec(): def test_location_first(install_mockery, mock_fetch, mock_archive, mock_packages): """Test with and without the --first option""" install = SpackCommand("install") - install("libelf@0.8.12") - install("libelf@0.8.13") + install("--fake", "libelf@0.8.12") + install("--fake", "libelf@0.8.13") # This would normally return an error without --first assert location("--first", "--install-dir", "libelf") diff --git a/lib/spack/spack/test/cmd/logs.py b/lib/spack/spack/test/cmd/logs.py index b668cd449cb..8ad5388b604 100644 --- a/lib/spack/spack/test/cmd/logs.py +++ b/lib/spack/spack/test/cmd/logs.py @@ -54,19 +54,19 @@ def disable_capture(capfd): def test_logs_cmd_errors(install_mockery, mock_fetch, mock_archive, mock_packages): - spec = spack.spec.Spec("libelf").concretized() + spec = spack.spec.Spec("pkg-c").concretized() assert not spec.installed with pytest.raises(spack.main.SpackCommandError, match="is not installed or staged"): - logs("libelf") + logs("pkg-c") with pytest.raises(spack.main.SpackCommandError, match="Too many specs"): - logs("libelf mpi") + logs("pkg-c mpi") - install("libelf") + install("pkg-c") os.remove(spec.package.install_log_path) with pytest.raises(spack.main.SpackCommandError, match="No logs are available"): - logs("libelf") + logs("pkg-c") def _write_string_to_path(string, path): @@ -98,7 +98,7 @@ def test_dump_logs(install_mockery, mock_fetch, mock_archive, mock_packages, dis spack.cmd.logs._logs(cmdline_spec, concrete_spec) assert _rewind_collect_and_decode(redirected_stdout) == stage_log_content - install("libelf") + install("--fake", "libelf") # Sanity check: make sure a path is recorded, regardless of whether # it exists (if it does exist, we will overwrite it with content diff --git a/lib/spack/spack/test/cmd/maintainers.py b/lib/spack/spack/test/cmd/maintainers.py index 3928bebff11..4745d98f97c 100644 --- a/lib/spack/spack/test/cmd/maintainers.py +++ b/lib/spack/spack/test/cmd/maintainers.py @@ -12,7 +12,13 @@ maintainers = spack.main.SpackCommand("maintainers") -MAINTAINED_PACKAGES = ["maintainers-1", "maintainers-2", "maintainers-3", "py-extension1"] +MAINTAINED_PACKAGES = [ + "gcc-runtime", + "maintainers-1", + "maintainers-2", + "maintainers-3", + "py-extension1", +] def split(output): @@ -35,6 +41,8 @@ def test_all(mock_packages, capfd): with capfd.disabled(): out = split(maintainers("--all")) assert out == [ + "gcc-runtime:", + "haampie", "maintainers-1:", "user1,", "user2", @@ -60,6 +68,8 @@ def test_all_by_user(mock_packages, capfd): with capfd.disabled(): out = split(maintainers("--all", "--by-user")) assert out == [ + "haampie:", + "gcc-runtime", "user0:", "maintainers-3", "user1:", diff --git a/lib/spack/spack/test/cmd/mark.py b/lib/spack/spack/test/cmd/mark.py index 0aa7b359205..c69cd45aa16 100644 --- a/lib/spack/spack/test/cmd/mark.py +++ b/lib/spack/spack/test/cmd/mark.py @@ -31,7 +31,7 @@ def test_mark_all_explicit(mutable_database): mark("-e", "-a") gc("-y") all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 15 + assert len(all_specs) == 16 @pytest.mark.db @@ -48,7 +48,7 @@ def test_mark_one_explicit(mutable_database): uninstall("-y", "-a", "mpileaks") gc("-y") all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 3 + assert len(all_specs) == 4 @pytest.mark.db @@ -56,7 +56,7 @@ def test_mark_one_implicit(mutable_database): mark("-i", "externaltest") gc("-y") all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 14 + assert len(all_specs) == 15 @pytest.mark.db @@ -65,4 +65,4 @@ def test_mark_all_implicit_then_explicit(mutable_database): mark("-e", "-a") gc("-y") all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 15 + assert len(all_specs) == 16 diff --git a/lib/spack/spack/test/cmd/mirror.py b/lib/spack/spack/test/cmd/mirror.py index ee827c05547..6dae870d611 100644 --- a/lib/spack/spack/test/cmd/mirror.py +++ b/lib/spack/spack/test/cmd/mirror.py @@ -351,7 +351,7 @@ def test_mirror_destroy( spec_name = "libdwarf" # Put a binary package in a buildcache - install("--no-cache", spec_name) + install("--fake", "--no-cache", spec_name) buildcache("push", "-u", "-f", mirror_dir.strpath, spec_name) contents = os.listdir(mirror_dir.strpath) diff --git a/lib/spack/spack/test/cmd/reindex.py b/lib/spack/spack/test/cmd/reindex.py index a7d6d8fda0e..11f9533a1f6 100644 --- a/lib/spack/spack/test/cmd/reindex.py +++ b/lib/spack/spack/test/cmd/reindex.py @@ -15,13 +15,10 @@ def test_reindex_basic(mock_packages, mock_archive, mock_fetch, install_mockery): - install("libelf@0.8.13") - install("libelf@0.8.12") - + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.12") all_installed = spack.store.STORE.db.query() - reindex() - assert spack.store.STORE.db.query() == all_installed @@ -36,23 +33,19 @@ def _clear_db(tmp_path): def test_reindex_db_deleted(mock_packages, mock_archive, mock_fetch, install_mockery, tmp_path): - install("libelf@0.8.13") - install("libelf@0.8.12") - + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.12") all_installed = spack.store.STORE.db.query() - _clear_db(tmp_path) - reindex() - assert spack.store.STORE.db.query() == all_installed def test_reindex_with_deprecated_packages( mock_packages, mock_archive, mock_fetch, install_mockery, tmp_path ): - install("libelf@0.8.13") - install("libelf@0.8.12") + install("--fake", "libelf@0.8.13") + install("--fake", "libelf@0.8.12") deprecate("-y", "libelf@0.8.12", "libelf@0.8.13") diff --git a/lib/spack/spack/test/cmd/test.py b/lib/spack/spack/test/cmd/test.py index 866d579aa7f..c7a4c0cea63 100644 --- a/lib/spack/spack/test/cmd/test.py +++ b/lib/spack/spack/test/cmd/test.py @@ -52,7 +52,7 @@ def test_test_dup_alias( mock_test_stage, mock_packages, mock_archive, mock_fetch, install_mockery, capfd ): """Ensure re-using an alias fails with suggestion to change.""" - install("libdwarf") + install("--fake", "libdwarf") # Run the (no) tests with the alias once spack_test("run", "--alias", "libdwarf", "libdwarf") diff --git a/lib/spack/spack/test/cmd/uninstall.py b/lib/spack/spack/test/cmd/uninstall.py index 6ac74b8abc6..98a76a3c08a 100644 --- a/lib/spack/spack/test/cmd/uninstall.py +++ b/lib/spack/spack/test/cmd/uninstall.py @@ -78,9 +78,8 @@ def test_recursive_uninstall(mutable_database): """Test recursive uninstall.""" uninstall("-y", "-a", "--dependents", "callpath") - all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 9 # query specs with multiple configurations + all_specs = spack.store.STORE.layout.all_specs() mpileaks_specs = [s for s in all_specs if s.satisfies("mpileaks")] callpath_specs = [s for s in all_specs if s.satisfies("callpath")] mpi_specs = [s for s in all_specs if s.satisfies("mpi")] @@ -92,23 +91,21 @@ def test_recursive_uninstall(mutable_database): @pytest.mark.db @pytest.mark.regression("3690") -@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 8), ("libelf", 6)]) +@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 9), ("libelf", 7)]) def test_uninstall_spec_with_multiple_roots( constraint, expected_number_of_specs, mutable_database ): uninstall("-y", "-a", "--dependents", constraint) - all_specs = spack.store.STORE.layout.all_specs() assert len(all_specs) == expected_number_of_specs @pytest.mark.db -@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 14), ("libelf", 14)]) +@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 15), ("libelf", 15)]) def test_force_uninstall_spec_with_ref_count_not_zero( constraint, expected_number_of_specs, mutable_database ): uninstall("-f", "-y", constraint) - all_specs = spack.store.STORE.layout.all_specs() assert len(all_specs) == expected_number_of_specs @@ -174,7 +171,7 @@ def db_specs(): all_specs, mpileaks_specs, callpath_specs, mpi_specs = db_specs() total_specs = len(all_specs) - assert total_specs == 14 + assert total_specs == 15 assert len(mpileaks_specs) == 3 assert len(callpath_specs) == 2 assert len(mpi_specs) == 3 diff --git a/lib/spack/spack/test/cmd/verify.py b/lib/spack/spack/test/cmd/verify.py index 53b57cd7902..3884bbf3738 100644 --- a/lib/spack/spack/test/cmd/verify.py +++ b/lib/spack/spack/test/cmd/verify.py @@ -65,7 +65,7 @@ def test_single_file_verify_cmd(tmpdir): def test_single_spec_verify_cmd(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery): # Test the verify command interface to verify a single spec - install("libelf") + install("--fake", "libelf") s = spack.spec.Spec("libelf").concretized() prefix = s.prefix hash = s.dag_hash() diff --git a/lib/spack/spack/test/cmd/view.py b/lib/spack/spack/test/cmd/view.py index 1c405728b44..d715616fb36 100644 --- a/lib/spack/spack/test/cmd/view.py +++ b/lib/spack/spack/test/cmd/view.py @@ -31,26 +31,26 @@ commands = ["hardlink", "symlink", "hard", "add", "copy", "relocate"] -def create_projection_file(tmpdir, projection): +def create_projection_file(tmp_path, projection): if "projections" not in projection: projection = {"projections": projection} - - projection_file = tmpdir.mkdir("projection").join("projection.yaml") - projection_file.write(s_yaml.dump(projection)) + projection_file = tmp_path / "projection" / "projection.yaml" + projection_file.parent.mkdir(parents=True, exist_ok=True) + projection_file.write_text(s_yaml.dump(projection)) return projection_file @pytest.mark.parametrize("cmd", commands) -def test_view_link_type(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, cmd): - install("libdwarf") - viewpath = str(tmpdir.mkdir("view_{0}".format(cmd))) - view(cmd, viewpath, "libdwarf") - package_prefix = os.path.join(viewpath, "libdwarf") - assert os.path.exists(package_prefix) +def test_view_link_type(tmp_path, mock_packages, mock_archive, mock_fetch, install_mockery, cmd): + install("--fake", "libdwarf") + view_dir = tmp_path / f"view_{cmd}" + view(cmd, str(view_dir), "libdwarf") + package_bin = view_dir / "bin" / "libdwarf" + assert package_bin.exists() # Check that we use symlinks for and only for the appropriate subcommands is_link_cmd = cmd in ("symlink", "add") - assert os.path.islink(package_prefix) == is_link_cmd + assert os.path.islink(str(package_bin)) == is_link_cmd @pytest.mark.parametrize("add_cmd", commands) @@ -68,60 +68,60 @@ def test_view_link_type_remove( @pytest.mark.parametrize("cmd", commands) -def test_view_projections(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery, cmd): - install("libdwarf@20130207") +def test_view_projections(tmp_path, mock_packages, mock_archive, mock_fetch, install_mockery, cmd): + install("--fake", "libdwarf@20130207") + view_dir = tmp_path / f"view_{cmd}" - viewpath = str(tmpdir.mkdir("view_{0}".format(cmd))) view_projection = {"projections": {"all": "{name}-{version}"}} - projection_file = create_projection_file(tmpdir, view_projection) - view(cmd, viewpath, "--projection-file={0}".format(projection_file), "libdwarf") + projection_file = create_projection_file(tmp_path, view_projection) + view(cmd, str(view_dir), f"--projection-file={projection_file}", "libdwarf") - package_prefix = os.path.join(viewpath, "libdwarf-20130207/libdwarf") - assert os.path.exists(package_prefix) + package_bin = view_dir / "libdwarf-20130207" / "bin" / "libdwarf" + assert package_bin.exists() # Check that we use symlinks for and only for the appropriate subcommands is_symlink_cmd = cmd in ("symlink", "add") - assert os.path.islink(package_prefix) == is_symlink_cmd + assert package_bin.is_symlink() == is_symlink_cmd def test_view_multiple_projections( - tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery + tmp_path, mock_packages, mock_archive, mock_fetch, install_mockery ): - install("libdwarf@20130207") - install("extendee@1.0%gcc") + install("--fake", "libdwarf@20130207") + install("--fake", "extendee@1.0") + view_dir = tmp_path / "view" - viewpath = str(tmpdir.mkdir("view")) view_projection = s_yaml.syaml_dict( - [("extendee", "{name}-{compiler.name}"), ("all", "{name}-{version}")] + [("extendee", "{name}-{architecture.platform}"), ("all", "{name}-{version}")] ) - projection_file = create_projection_file(tmpdir, view_projection) - view("add", viewpath, "--projection-file={0}".format(projection_file), "libdwarf", "extendee") + projection_file = create_projection_file(tmp_path, view_projection) + view("add", str(view_dir), f"--projection-file={projection_file}", "libdwarf", "extendee") - libdwarf_prefix = os.path.join(viewpath, "libdwarf-20130207/libdwarf") - extendee_prefix = os.path.join(viewpath, "extendee-gcc/bin") - assert os.path.exists(libdwarf_prefix) - assert os.path.exists(extendee_prefix) + libdwarf_prefix = view_dir / "libdwarf-20130207" / "bin" + extendee_prefix = view_dir / "extendee-test" / "bin" + assert libdwarf_prefix.exists() + assert extendee_prefix.exists() def test_view_multiple_projections_all_first( - tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery + tmp_path, mock_packages, mock_archive, mock_fetch, install_mockery ): - install("libdwarf@20130207") - install("extendee@1.0%gcc") + install("--fake", "libdwarf@20130207") + install("--fake", "extendee@1.0") + view_dir = tmp_path / "view" - viewpath = str(tmpdir.mkdir("view")) view_projection = s_yaml.syaml_dict( - [("all", "{name}-{version}"), ("extendee", "{name}-{compiler.name}")] + [("all", "{name}-{version}"), ("extendee", "{name}-{architecture.platform}")] ) - projection_file = create_projection_file(tmpdir, view_projection) - view("add", viewpath, "--projection-file={0}".format(projection_file), "libdwarf", "extendee") + projection_file = create_projection_file(tmp_path, view_projection) + view("add", str(view_dir), f"--projection-file={projection_file}", "libdwarf", "extendee") - libdwarf_prefix = os.path.join(viewpath, "libdwarf-20130207/libdwarf") - extendee_prefix = os.path.join(viewpath, "extendee-gcc/bin") - assert os.path.exists(libdwarf_prefix) - assert os.path.exists(extendee_prefix) + libdwarf_prefix = view_dir / "libdwarf-20130207" / "bin" + extendee_prefix = view_dir / "extendee-test" / "bin" + assert libdwarf_prefix.exists() + assert extendee_prefix.exists() def test_view_external(tmpdir, mock_packages, mock_archive, mock_fetch, install_mockery): diff --git a/lib/spack/spack/test/compilers/libraries.py b/lib/spack/spack/test/compilers/libraries.py index 710e8734738..dda19565a14 100644 --- a/lib/spack/spack/test/compilers/libraries.py +++ b/lib/spack/spack/test/compilers/libraries.py @@ -74,6 +74,7 @@ def test_compile_dummy_c_source_no_verbose_flags(self, mock_gcc, monkeypatch): detector = spack.compilers.libraries.CompilerPropertyDetector(mock_gcc) assert detector._compile_dummy_c_source() is None + @pytest.mark.not_on_windows("Module files are not supported on Windows") def test_compile_dummy_c_source_load_env(self, mock_gcc, monkeypatch, tmp_path): gcc = tmp_path / "gcc" gcc.write_text( @@ -104,10 +105,13 @@ def module(*args): @pytest.mark.not_on_windows("Not supported on Windows") def test_implicit_rpaths(self, mock_gcc, dirs_with_libfiles, monkeypatch): lib_to_dirs, all_dirs = dirs_with_libfiles - monkeypatch.setattr(spack.compilers.libraries.CompilerPropertyDetector, "_CACHE", {}) detector = spack.compilers.libraries.CompilerPropertyDetector(mock_gcc) - detector._CACHE[mock_gcc.dag_hash()] = "ld " + " ".join(f"-L{d}" for d in all_dirs) + monkeypatch.setattr( + spack.compilers.libraries.CompilerPropertyDetector, + "_compile_dummy_c_source", + lambda self: "ld " + " ".join(f"-L{d}" for d in all_dirs), + ) retrieved_rpaths = detector.implicit_rpaths() assert set(retrieved_rpaths) == set(lib_to_dirs["libstdc++"] + lib_to_dirs["libgfortran"]) diff --git a/lib/spack/spack/test/concretization/compiler_runtimes.py b/lib/spack/spack/test/concretization/compiler_runtimes.py index e8926edf02d..b1e5c6fa5db 100644 --- a/lib/spack/spack/test/concretization/compiler_runtimes.py +++ b/lib/spack/spack/test/concretization/compiler_runtimes.py @@ -17,8 +17,6 @@ from spack.environment.environment import ViewDescriptor from spack.version import Version -pytestmark = [pytest.mark.usefixtures("enable_runtimes")] - def _concretize_with_reuse(*, root_str, reused_str): reused_spec = spack.spec.Spec(reused_str).concretized() @@ -36,14 +34,6 @@ def runtime_repo(mutable_config): yield mock_repo -@pytest.fixture -def enable_runtimes(): - original = spack.solver.asp.WITH_RUNTIME - spack.solver.asp.WITH_RUNTIME = True - yield - spack.solver.asp.WITH_RUNTIME = original - - def test_correct_gcc_runtime_is_injected_as_dependency(runtime_repo): s = spack.spec.Spec("pkg-a%gcc@10.2.1 ^pkg-b%gcc@9.4.0").concretized() a, b = s["pkg-a"], s["pkg-b"] diff --git a/lib/spack/spack/test/concretization/core.py b/lib/spack/spack/test/concretization/core.py index 16b3094886c..e858f35dad3 100644 --- a/lib/spack/spack/test/concretization/core.py +++ b/lib/spack/spack/test/concretization/core.py @@ -454,11 +454,13 @@ def test_compiler_flag_propagation(self, spec_str, expected, not_expected): for constraint in not_expected: assert not root.satisfies(constraint) + @pytest.mark.xfail(reason="FIXME (compiler as nodes): flaky test, revisit") def test_mixing_compilers_only_affects_subdag(self): """Tests that, when we mix compilers, the one with lower penalty is used for nodes where the compiler is not forced. """ spec = Spec("dt-diamond%clang ^dt-diamond-bottom%gcc").concretized() + for x in spec.traverse(deptype=("link", "run")): if "c" not in x or not x.name.startswith("dt-diamond"): continue @@ -466,8 +468,7 @@ def test_mixing_compilers_only_affects_subdag(self): assert bool(x.dependencies(name="llvm", deptype="build")) is not expected_gcc assert bool(x.dependencies(name="gcc", deptype="build")) is expected_gcc assert x.satisfies("%clang") is not expected_gcc - # FIXME (compiler as nodes): satisfies semantic should be only for direct build deps - # assert x.satisfies("%gcc") is expected_gcc + assert x.satisfies("%gcc") is expected_gcc def test_compiler_inherited_upwards(self): spec = Spec("dt-diamond ^dt-diamond-bottom%clang").concretized() @@ -932,13 +933,13 @@ def test_noversion_pkg(self, spec): [ ( "mpileaks%gcc@=4.4.7 ^dyninst@=10.2.1 target=x86_64:", - "gcc@4.4.7 languages=c,cxx,fortran", + "gcc@4.4.7 languages=c,c++,fortran", "core2", ), - ("mpileaks%gcc@=4.8 target=x86_64:", "gcc@4.8 languages=c,cxx,fortran", "haswell"), + ("mpileaks%gcc@=4.8 target=x86_64:", "gcc@4.8 languages=c,c++,fortran", "haswell"), ( "mpileaks%gcc@=5.3.0 target=x86_64:", - "gcc@5.3.0 languages=c,cxx,fortran", + "gcc@5.3.0 languages=c,c++,fortran", "broadwell", ), ], @@ -985,7 +986,6 @@ def test_concretize_anonymous_dep(self, spec_str): ("bowtie@1.4.0", "%gcc@10.2.1"), # Version with conflicts and no valid gcc select another compiler ("bowtie@1.3.0", "%clang@15.0.0"), - # FIXME (compiler as nodes): does this make sense? # If a higher gcc is available, with a worse os, still prefer that ("bowtie@1.2.2", "%gcc@11.1.0"), ], @@ -1452,31 +1452,6 @@ def test_external_with_non_default_variant_as_dependency(self): assert "~bar" in s["external-non-default-variant"] assert s["external-non-default-variant"].external - # FIXME (compiler as nodes): revisit this test - # @pytest.mark.regression("22871") - # @pytest.mark.parametrize( - # "spec_str,expected_os", - # [ - # ("mpileaks", "os=debian6"), - # # To trigger the bug in 22871 we need to have the same compiler - # # spec available on both operating systems - # ("mpileaks%gcc@10.2.1 platform=test os=debian6", "os=debian6"), - # ("mpileaks%gcc@10.2.1 platform=test os=redhat6", "os=redhat6"), - # ], - # ) - # def test_os_selection_when_multiple_choices_are_possible( - # self, spec_str, expected_os, compiler_factory - # ): - # # GCC 10.2.1 is defined both for debian and for redhat - # with spack.config.override( - # "packages", {"gcc": {"externals": [compiler_factory(spec="gcc@10.2.1 os=redhat6")]}} - # ): - # s = Spec(spec_str).concretized() - # for node in s.traverse(): - # if node.name == "glibc": - # continue - # assert node.satisfies(expected_os) - @pytest.mark.regression("22718") @pytest.mark.parametrize( "spec_str,expected_compiler", @@ -1774,12 +1749,11 @@ def test_reuse_with_unknown_package_dont_raise(self, tmpdir, temporary_store, mo (["libdwarf%gcc", "libelf%clang"], {"libdwarf": 1, "libelf": 1}), (["libdwarf%gcc", "libdwarf%clang"], {"libdwarf": 2, "libelf": 1}), (["libdwarf^libelf@0.8.12", "libdwarf^libelf@0.8.13"], {"libdwarf": 2, "libelf": 2}), - # FIXME (compiler as nodes): fix these - # (["hdf5", "zmpi"], 3, 1), - # (["hdf5", "mpich"], 2, 1), - # (["hdf5^zmpi", "mpich"], 4, 1), - # (["mpi", "zmpi"], 2, 1), - # (["mpi", "mpich"], 1, 1), + (["hdf5", "zmpi"], {"zmpi": 1, "fake": 1}), + (["hdf5", "mpich"], {"mpich": 1}), + (["hdf5^zmpi", "mpich"], {"mpi": 2, "mpich": 1, "zmpi": 1, "fake": 1}), + (["mpi", "zmpi"], {"mpi": 1, "mpich": 0, "zmpi": 1, "fake": 1}), + (["mpi", "mpich"], {"mpi": 1, "mpich": 1, "zmpi": 0}), ], ) def test_best_effort_coconcretize(self, specs, checks): @@ -1791,9 +1765,13 @@ def test_best_effort_coconcretize(self, specs, checks): for s in result.specs: concrete_specs.update(s.traverse()) + for x in concrete_specs: + print(x.tree(hashes=True)) + print() + for matching_spec, expected_count in checks.items(): matches = [x for x in concrete_specs if x.satisfies(matching_spec)] - assert len(matches) == expected_count, matching_spec + assert len(matches) == expected_count @pytest.mark.parametrize( "specs,expected_spec,occurances", @@ -2207,10 +2185,19 @@ def test_unsolved_specs_raises_error(self, monkeypatch, mock_packages): solver.driver.solve(setup, specs, reuse=[]) @pytest.mark.regression("43141") - def test_clear_error_when_unknown_compiler_requested(self, mock_packages): + @pytest.mark.parametrize( + "spec_str,expected_match", + [ + # A package does not exist + ("pkg-a ^foo", "since 'foo' does not exist"), + # Request a compiler for a package that doesn't need it + ("pkg-c %gcc", "according to its recipe"), + ], + ) + def test_errors_on_statically_checked_preconditions(self, spec_str, expected_match): """Tests that the solver can report a case where the compiler cannot be set""" - with pytest.raises(spack.error.UnsatisfiableSpecError, match="since 'foo' does not exist"): - Spec("pkg-a %foo").concretized() + with pytest.raises(spack.error.UnsatisfiableSpecError, match=expected_match): + Spec(spec_str).concretized() @pytest.mark.regression("36339") @pytest.mark.parametrize( diff --git a/lib/spack/spack/test/concretization/requirements.py b/lib/spack/spack/test/concretization/requirements.py index c1aaafefc17..2d8835d5da6 100644 --- a/lib/spack/spack/test/concretization/requirements.py +++ b/lib/spack/spack/test/concretization/requirements.py @@ -494,7 +494,7 @@ def test_default_requirements_with_all(spec_str, requirement_str, concretize_sco spec = Spec(spec_str).concretized() assert "c" in spec for s in spec.traverse(): - if "c" in s: + if "c" in s and s.name not in ("gcc", "llvm"): assert s.satisfies(requirement_str) diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py index a5a4fc9787c..0d957e83187 100644 --- a/lib/spack/spack/test/config.py +++ b/lib/spack/spack/test/config.py @@ -273,10 +273,10 @@ def test_add_config_path(mutable_config): assert set_value == "/path/to/config.yaml" # Now a package:all setting - path = "packages:all:compiler:[gcc]" + path = "packages:all:target:[x86_64]" spack.config.add(path) - compilers = spack.config.get("packages")["all"]["compiler"] - assert "gcc" in compilers + targets = spack.config.get("packages")["all"]["target"] + assert "x86_64" in targets # Try quotes to escape brackets path = "config:install_tree:projections:cmake:\ @@ -1013,7 +1013,6 @@ def test_single_file_scope(config, env_yaml): # from the single-file config assert spack.config.get("config:verify_ssl") is False assert spack.config.get("config:dirty") is False - assert spack.config.get("packages:all:compiler") == ["gcc@4.5.3", "gcc", "clang"] # from the lower config scopes assert spack.config.get("config:checksum") is True @@ -1037,7 +1036,7 @@ def test_single_file_scope_section_override(tmpdir, config): verify_ssl: False packages:: all: - compiler: [ 'gcc@4.5.3' ] + target: [ x86_64 ] repos: - /x/y/z """ @@ -1050,7 +1049,7 @@ def test_single_file_scope_section_override(tmpdir, config): with spack.config.override(scope): # from the single-file config assert spack.config.get("config:verify_ssl") is False - assert spack.config.get("packages:all:compiler") == ["gcc@4.5.3"] + assert spack.config.get("packages:all:target") == ["x86_64"] # from the lower config scopes assert spack.config.get("config:checksum") is True @@ -1315,10 +1314,10 @@ def test_user_config_path_is_default_when_env_var_is_empty(working_env): def test_default_install_tree(monkeypatch, default_config): - s = spack.spec.Spec("nonexistent@x.y.z %none@a.b.c arch=foo-bar-baz") + s = spack.spec.Spec("nonexistent@x.y.z arch=foo-bar-baz") monkeypatch.setattr(s, "dag_hash", lambda length: "abc123") _, _, projections = spack.store.parse_install_tree(spack.config.get("config")) - assert s.format(projections["all"]) == "foo-bar-baz/none-a.b.c/nonexistent-x.y.z-abc123" + assert s.format(projections["all"]) == "foo/baz/nonexistent-x.y.z-abc123" def test_local_config_can_be_disabled(working_env): @@ -1389,7 +1388,7 @@ def test_config_collect_urls(mutable_empty_config, mock_spider_configs, url, isf (github_url.format("tree"), False, False), (gitlab_url, False, False), ("{0}/README.md".format(github_url.format("blob")), True, True), - ("{0}/compilers.yaml".format(gitlab_url), True, False), + ("{0}/packages.yaml".format(gitlab_url), True, False), (None, False, True), ], ) diff --git a/lib/spack/spack/test/database.py b/lib/spack/spack/test/database.py index 07cbe3dd81f..3afae161f1d 100644 --- a/lib/spack/spack/test/database.py +++ b/lib/spack/spack/test/database.py @@ -65,11 +65,11 @@ def upstream_and_downstream_db(tmpdir, gen_mock_layout): @pytest.mark.parametrize( "install_tree,result", [ - ("all", ["pkg-b", "pkg-c"]), + ("all", ["pkg-b", "pkg-c", "gcc-runtime", "gcc"]), ("upstream", ["pkg-c"]), - ("local", ["pkg-b"]), + ("local", ["pkg-b", "gcc-runtime", "gcc"]), ("{u}", ["pkg-c"]), - ("{d}", ["pkg-b"]), + ("{d}", ["pkg-b", "gcc-runtime", "gcc"]), ], ) def test_query_by_install_tree( @@ -85,7 +85,7 @@ def test_query_by_install_tree( down_db.add(b) specs = down_db.query(install_tree=install_tree.format(u=up_db.root, d=down_db.root)) - assert [s.name for s in specs] == result + assert {s.name for s in specs} == set(result) def test_spec_installed_upstream( @@ -454,7 +454,7 @@ def test_005_db_exists(database): def test_010_all_install_sanity(database): """Ensure that the install layout reflects what we think it does.""" all_specs = spack.store.STORE.layout.all_specs() - assert len(all_specs) == 15 + assert len(all_specs) == 16 # Query specs with multiple configurations mpileaks_specs = [s for s in all_specs if s.satisfies("mpileaks")] @@ -571,7 +571,7 @@ def test_050_basic_query(database): """Ensure querying database is consistent with what is installed.""" # query everything total_specs = len(spack.store.STORE.db.query()) - assert total_specs == 17 + assert total_specs == 19 # query specs with multiple configurations mpileaks_specs = database.query("mpileaks") @@ -793,11 +793,11 @@ def check_unused(roots, deptype, expected): assert set(u.name for u in unused) == set(expected) default_dt = dt.LINK | dt.RUN - check_unused(None, default_dt, ["cmake"]) + check_unused(None, default_dt, ["cmake", "gcc"]) check_unused( [si, ml_mpich, ml_mpich2, ml_zmpi, externaltest], default_dt, - ["trivial-smoke-test", "cmake"], + ["trivial-smoke-test", "cmake", "gcc"], ) check_unused( [si, ml_mpich, ml_mpich2, ml_zmpi, externaltest], @@ -812,7 +812,7 @@ def check_unused(roots, deptype, expected): check_unused( [si, ml_mpich, ml_mpich2, ml_zmpi], default_dt, - ["trivial-smoke-test", "cmake", "externaltest", "externaltool", "externalvirtual"], + ["trivial-smoke-test", "cmake", "externaltest", "externaltool", "externalvirtual", "gcc"], ) @@ -1047,7 +1047,7 @@ def test_check_parents(spec_str, parent_name, expected_nparents, database): def test_db_all_hashes(database): # ensure we get the right number of hashes without a read transaction hashes = database.all_hashes() - assert len(hashes) == 17 + assert len(hashes) == 19 # and make sure the hashes match with database.read_transaction(): diff --git a/lib/spack/spack/test/directory_layout.py b/lib/spack/spack/test/directory_layout.py index 169c6a9c5e8..9b4cd03669a 100644 --- a/lib/spack/spack/test/directory_layout.py +++ b/lib/spack/spack/test/directory_layout.py @@ -35,11 +35,7 @@ def test_yaml_directory_layout_parameters(tmpdir, default_mock_concretization): layout_default = DirectoryLayout(str(tmpdir)) path_default = layout_default.relative_path_for_spec(spec) assert path_default == str( - Path( - spec.format( - "{architecture}/" "{compiler.name}-{compiler.version}/" "{name}-{version}-{hash}" - ) - ) + Path(spec.format("{architecture.platform}/{architecture.target}/{name}-{version}-{hash}")) ) # Test hash_length parameter works correctly diff --git a/lib/spack/spack/test/graph.py b/lib/spack/spack/test/graph.py index c26363bb46d..33f03cc7492 100644 --- a/lib/spack/spack/test/graph.py +++ b/lib/spack/spack/test/graph.py @@ -47,31 +47,35 @@ def test_ascii_graph_mpileaks(config, mock_packages, monkeypatch): graph_str = stream.getvalue() graph_str = "\n".join([line.rstrip() for line in graph_str.split("\n")]) + print(graph_str) + assert ( graph_str == r"""o mpileaks |\ -| o callpath -|/| -o | mpich - / -o dyninst -|\ -| o libdwarf +| |\ +| | |\ +| | | o callpath +| |_|/| +|/| |/| +| |/|/| +o | | | mpich +|\| | | +| |/ / +|/| | +| | o dyninst +| |/| +|/|/| +| | |\ +| | | o libdwarf +| |_|/| +|/| |/| +| |/|/ +| | o libelf +| |/| +|/|/ +| o gcc-runtime |/ -o libelf -""" - or graph_str - == r"""o mpileaks -|\ -o | callpath -|\| -| o mpich -| -o dyninst -|\ -o | libdwarf -|/ -o libelf +o gcc """ ) diff --git a/lib/spack/spack/test/installer.py b/lib/spack/spack/test/installer.py index 38bc1609821..72272978a8c 100644 --- a/lib/spack/spack/test/installer.py +++ b/lib/spack/spack/test/installer.py @@ -1025,6 +1025,7 @@ def test_install_fail_multi(install_mockery, mock_fetch, monkeypatch): def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys): """Test fail_fast install when an install failure is detected.""" + # Note: this test depends on the order of the installations b, c = spack.spec.Spec("pkg-b").concretized(), spack.spec.Spec("pkg-c").concretized() b_id, c_id = inst.package_id(b), inst.package_id(c) @@ -1037,9 +1038,9 @@ def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys): with pytest.raises(spack.error.InstallError, match="after first install failure"): installer.install() - assert b_id in installer.failed, "Expected b to be marked as failed" - assert c_id not in installer.failed, "Expected no attempt to install pkg-c" - assert f"{b_id} failed to install" in capsys.readouterr().err + assert c_id in installer.failed, "Expected b to be marked as failed" + assert b_id not in installer.failed, "Expected no attempt to install pkg-c" + assert f"{c_id} failed to install" in capsys.readouterr().err def _test_install_fail_fast_on_except_patch(installer, **kwargs): @@ -1072,10 +1073,11 @@ def test_install_fail_fast_on_except(install_mockery, monkeypatch, capsys): def test_install_lock_failures(install_mockery, monkeypatch, capfd): """Cover basic install lock failure handling in a single pass.""" + # Note: this test relies on installing a package with no dependencies def _requeued(installer, task, install_status): tty.msg("requeued {0}".format(task.pkg.spec.name)) - installer = create_installer(["pkg-b"], {}) + installer = create_installer(["pkg-c"], {}) # Ensure never acquire a lock monkeypatch.setattr(inst.PackageInstaller, "_ensure_locked", _not_locked) @@ -1094,13 +1096,14 @@ def _requeued(installer, task, install_status): def test_install_lock_installed_requeue(install_mockery, monkeypatch, capfd): """Cover basic install handling for installed package.""" - b = spack.spec.Spec("pkg-b").concretized() - b_pkg_id = inst.package_id(b) - installer = create_installer([b]) + # Note: this test relies on installing a package with no dependencies + concrete_spec = spack.spec.Spec("pkg-c").concretized() + pkg_id = inst.package_id(concrete_spec) + installer = create_installer([concrete_spec]) def _prep(installer, task): - installer.installed.add(b_pkg_id) - tty.msg(f"{b_pkg_id} is installed") + installer.installed.add(pkg_id) + tty.msg(f"{pkg_id} is installed") # also do not allow the package to be locked again monkeypatch.setattr(inst.PackageInstaller, "_ensure_locked", _not_locked) @@ -1117,7 +1120,7 @@ def _requeued(installer, task, install_status): with pytest.raises(spack.error.InstallError, match="request failed"): installer.install() - assert b_pkg_id not in installer.installed + assert pkg_id not in installer.installed expected = ["is installed", "read locked", "requeued"] for exp, ln in zip(expected, capfd.readouterr().out.splitlines()): @@ -1126,6 +1129,7 @@ def _requeued(installer, task, install_status): def test_install_read_locked_requeue(install_mockery, monkeypatch, capfd): """Cover basic read lock handling for uninstalled package with requeue.""" + # Note: this test relies on installing a package with no dependencies orig_fn = inst.PackageInstaller._ensure_locked def _read(installer, lock_type, pkg): @@ -1148,7 +1152,7 @@ def _requeued(installer, task, install_status): # Ensure don't continually requeue the task monkeypatch.setattr(inst.PackageInstaller, "_requeue_task", _requeued) - installer = create_installer(["pkg-b"], {}) + installer = create_installer(["pkg-c"], {}) with pytest.raises(spack.error.InstallError, match="request failed"): installer.install() @@ -1163,7 +1167,8 @@ def _requeued(installer, task, install_status): def test_install_skip_patch(install_mockery, mock_fetch): """Test the path skip_patch install path.""" - installer = create_installer(["pkg-b"], {"fake": False, "skip_patch": True}) + # Note: this test relies on installing a package with no dependencies + installer = create_installer(["pkg-c"], {"fake": False, "skip_patch": True}) installer.install() assert inst.package_id(installer.build_requests[0].pkg.spec) in installer.installed @@ -1183,8 +1188,9 @@ def test_overwrite_install_backup_success(temporary_store, config, mock_packages When doing an overwrite install that fails, Spack should restore the backup of the original prefix, and leave the original spec marked installed. """ + # Note: this test relies on installing a package with no dependencies # Get a build task. TODO: refactor this to avoid calling internal methods - installer = create_installer(["pkg-b"]) + installer = create_installer(["pkg-c"]) installer._init_queue() task = installer._pop_task() @@ -1225,6 +1231,7 @@ def test_overwrite_install_backup_failure(temporary_store, config, mock_packages original prefix. If that fails, the spec is lost, and it should be removed from the database. """ + # Note: this test relies on installing a package with no dependencies class InstallerThatAccidentallyDeletesTheBackupDir: def _install_task(self, task, install_status): @@ -1244,7 +1251,7 @@ def remove(self, spec): self.called = True # Get a build task. TODO: refactor this to avoid calling internal methods - installer = create_installer(["pkg-b"]) + installer = create_installer(["pkg-c"]) installer._init_queue() task = installer._pop_task() diff --git a/lib/spack/spack/test/link_paths.py b/lib/spack/spack/test/link_paths.py index 1f8e626b490..7fb1d7344f8 100644 --- a/lib/spack/spack/test/link_paths.py +++ b/lib/spack/spack/test/link_paths.py @@ -8,6 +8,7 @@ import pytest +import spack.compilers.libraries import spack.paths from spack.compilers.libraries import parse_non_system_link_dirs diff --git a/lib/spack/spack/test/llnl/util/lang.py b/lib/spack/spack/test/llnl/util/lang.py index 6926c50cd89..fe400c89887 100644 --- a/lib/spack/spack/test/llnl/util/lang.py +++ b/lib/spack/spack/test/llnl/util/lang.py @@ -7,7 +7,6 @@ import re import sys from datetime import datetime, timedelta -from textwrap import dedent import pytest @@ -291,37 +290,6 @@ def inner(): with h.forward("top-level"): raise TypeError("ok") - assert h.grouped_message(with_tracebacks=False) == dedent( - """\ - due to the following failures: - inner method raised ValueError: wow! - top-level raised TypeError: ok""" - ) - - full_message = h.grouped_message(with_tracebacks=True) - no_line_numbers = re.sub(r"line [0-9]+,", "line xxx,", full_message) - - assert ( - no_line_numbers - == dedent( - """\ - due to the following failures: - inner method raised ValueError: wow! - File "{0}", \ -line xxx, in test_grouped_exception - inner() - File "{0}", \ -line xxx, in inner - raise ValueError("wow!") - - top-level raised TypeError: ok - File "{0}", \ -line xxx, in test_grouped_exception - raise TypeError("ok") - """ - ).format(__file__) - ) - def test_grouped_exception_base_type(): h = llnl.util.lang.GroupedExceptionHandler() diff --git a/lib/spack/spack/test/modules/lmod.py b/lib/spack/spack/test/modules/lmod.py index a985cb1b7e0..f9350bd377e 100644 --- a/lib/spack/spack/test/modules/lmod.py +++ b/lib/spack/spack/test/modules/lmod.py @@ -112,7 +112,7 @@ def test_compilers_provided_different_name( self, factory, module_configuration, compiler_factory ): with spack.config.override( - "compilers", [compiler_factory(spec="clang@3.3", operating_system="debian6")] + "packages", {"llvm": {"externals": [compiler_factory(spec="llvm@3.3")]}} ): module_configuration("complex_hierarchy") module, spec = factory("intel-oneapi-compilers%clang@3.3") @@ -120,7 +120,7 @@ def test_compilers_provided_different_name( provides = module.conf.provides assert "compiler" in provides - assert provides["compiler"] == spack.spec.CompilerSpec("oneapi@=3.0") + assert provides["compiler"] == spack.spec.Spec("intel-oneapi-compilers@=3.0") def test_simple_case(self, modulefile_content, module_configuration): """Tests the generation of a simple Lua module file.""" @@ -139,7 +139,7 @@ def test_autoload_direct(self, modulefile_content, module_configuration): module_configuration("autoload_direct") content = modulefile_content(mpileaks_spec_string) - assert len([x for x in content if "depends_on(" in x]) == 2 + assert len([x for x in content if "depends_on(" in x]) == 3 def test_autoload_all(self, modulefile_content, module_configuration): """Tests the automatic loading of all dependencies.""" @@ -147,7 +147,7 @@ def test_autoload_all(self, modulefile_content, module_configuration): module_configuration("autoload_all") content = modulefile_content(mpileaks_spec_string) - assert len([x for x in content if "depends_on(" in x]) == 5 + assert len([x for x in content if "depends_on(" in x]) == 6 def test_alter_environment(self, modulefile_content, module_configuration): """Tests modifications to run-time environment.""" @@ -265,7 +265,7 @@ def test_exclude(self, modulefile_content, module_configuration): module_configuration("exclude") content = modulefile_content(mpileaks_spec_string) - assert len([x for x in content if "depends_on(" in x]) == 1 + assert len([x for x in content if "depends_on(" in x]) == 2 def test_no_hash(self, factory, module_configuration): """Makes sure that virtual providers (in the hierarchy) always @@ -372,7 +372,7 @@ def test_guess_core_compilers(self, factory, module_configuration, monkeypatch): module_configuration("missing_core_compilers") # Our mock paths must be detected as system paths - monkeypatch.setattr(spack.util.environment, "SYSTEM_DIRS", ["/path/to"]) + monkeypatch.setattr(spack.util.environment, "SYSTEM_DIRS", ["/path/bin"]) # We don't want to really write into user configuration # when running tests @@ -434,7 +434,7 @@ def test_modules_relative_to_view( ): with ev.create_in_dir(str(tmpdir), with_view=True) as e: module_configuration("with_view") - install("--add", "cmake") + install("--fake", "--add", "cmake") spec = spack.spec.Spec("cmake").concretized() diff --git a/lib/spack/spack/test/modules/tcl.py b/lib/spack/spack/test/modules/tcl.py index d2a8b18b67a..71fedca2e0c 100644 --- a/lib/spack/spack/test/modules/tcl.py +++ b/lib/spack/spack/test/modules/tcl.py @@ -46,8 +46,8 @@ def test_autoload_direct(self, modulefile_content, module_configuration): len([x for x in content if "if {![info exists ::env(LMOD_VERSION_MAJOR)]} {" in x]) == 1 ) - assert len([x for x in content if "depends-on " in x]) == 2 - assert len([x for x in content if "module load " in x]) == 2 + assert len([x for x in content if "depends-on " in x]) == 3 + assert len([x for x in content if "module load " in x]) == 3 # dtbuild1 has # - 1 ('run',) dependency @@ -77,8 +77,8 @@ def test_autoload_all(self, modulefile_content, module_configuration): len([x for x in content if "if {![info exists ::env(LMOD_VERSION_MAJOR)]} {" in x]) == 1 ) - assert len([x for x in content if "depends-on " in x]) == 5 - assert len([x for x in content if "module load " in x]) == 5 + assert len([x for x in content if "depends-on " in x]) == 6 + assert len([x for x in content if "module load " in x]) == 6 # dtbuild1 has # - 1 ('run',) dependency @@ -102,7 +102,7 @@ def test_prerequisites_direct( module_configuration("prerequisites_direct") content = modulefile_content(f"mpileaks target={host_architecture_str}") - assert len([x for x in content if "prereq" in x]) == 2 + assert len([x for x in content if "prereq" in x]) == 3 def test_prerequisites_all( self, modulefile_content, module_configuration, host_architecture_str @@ -112,7 +112,7 @@ def test_prerequisites_all( module_configuration("prerequisites_all") content = modulefile_content(f"mpileaks target={host_architecture_str}") - assert len([x for x in content if "prereq" in x]) == 5 + assert len([x for x in content if "prereq" in x]) == 6 def test_alter_environment(self, modulefile_content, module_configuration): """Tests modifications to run-time environment.""" @@ -237,7 +237,7 @@ def test_exclude(self, modulefile_content, module_configuration, host_architectu module_configuration("exclude") content = modulefile_content("mpileaks ^zmpi") - 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]) == 2 # Catch "Exception" to avoid using FileNotFoundError on Python 3 # and IOError on Python 2 or common bases like EnvironmentError @@ -247,7 +247,7 @@ def test_exclude(self, modulefile_content, module_configuration, host_architectu content = modulefile_content(f"zmpi target={host_architecture_str}") - 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]) == 2 def test_naming_scheme_compat(self, factory, module_configuration): """Tests backwards compatibility for naming_scheme key""" @@ -488,8 +488,8 @@ def test_autoload_with_constraints(self, modulefile_content, module_configuratio # Test the mpileaks that should have the autoloaded dependencies content = modulefile_content("mpileaks ^mpich2") - assert len([x for x in content if "depends-on " in x]) == 2 - assert len([x for x in content if "module load " in x]) == 2 + assert len([x for x in content if "depends-on " in x]) == 3 + assert len([x for x in content if "module load " in x]) == 3 # Test the mpileaks that should NOT have the autoloaded dependencies content = modulefile_content("mpileaks ^mpich") diff --git a/lib/spack/spack/test/multimethod.py b/lib/spack/spack/test/multimethod.py index 7e5231ee936..46b6fe12f61 100644 --- a/lib/spack/spack/test/multimethod.py +++ b/lib/spack/spack/test/multimethod.py @@ -7,7 +7,6 @@ import pytest -import spack.config import spack.platforms import spack.spec from spack.multimethod import NoSuchMethodError @@ -54,7 +53,7 @@ def test_no_version_match(pkg_name): # Constraints on compilers with a default ("%gcc", "has_a_default", "gcc"), ("%clang", "has_a_default", "clang"), - ("%apple-clang os=elcapitan", "has_a_default", "default"), + ("%gcc@9", "has_a_default", "default"), # Constraints on dependencies ("^zmpi", "different_by_dep", "zmpi"), ("^mpich", "different_by_dep", "mpich"), @@ -69,13 +68,9 @@ def test_no_version_match(pkg_name): ], ) def test_multimethod_calls( - pkg_name, constraint_str, method_name, expected_result, compiler_factory + pkg_name, constraint_str, method_name, expected_result, default_mock_concretization ): - # Add apple-clang, as it is required by one of the tests - with spack.config.override( - "compilers", [compiler_factory(spec="apple-clang@9.1.0", operating_system="elcapitan")] - ): - s = spack.spec.Spec(pkg_name + constraint_str).concretized() + s = default_mock_concretization(f"{pkg_name}{constraint_str}") msg = f"Method {method_name} from {s} is giving a wrong result" assert getattr(s.package, method_name)() == expected_result, msg diff --git a/lib/spack/spack/test/package_class.py b/lib/spack/spack/test/package_class.py index b1572f23662..5cec8e8a5c8 100644 --- a/lib/spack/spack/test/package_class.py +++ b/lib/spack/spack/test/package_class.py @@ -17,8 +17,6 @@ import llnl.util.filesystem as fs -import spack.compilers.config -import spack.config import spack.deptypes as dt import spack.error import spack.install_test @@ -34,21 +32,28 @@ def mpi_names(mock_repo_path): return [spec.name for spec in mock_repo_path.providers_for("mpi")] +@pytest.fixture(scope="module") +def compiler_names(mock_repo_path): + return [spec.name for spec in mock_repo_path.providers_for("c")] + + @pytest.fixture() -def mpileaks_possible_deps(mock_packages, mpi_names): +def mpileaks_possible_deps(mock_packages, mpi_names, compiler_names): possible = { - "callpath": set(["dyninst"] + mpi_names), + "callpath": set(["dyninst"] + mpi_names + compiler_names), "low-priority-provider": set(), - "dyninst": set(["libdwarf", "libelf"]), + "dyninst": set(["libdwarf", "libelf"] + compiler_names), "fake": set(), + "gcc": set(), "intel-parallel-studio": set(), - "libdwarf": set(["libelf"]), - "libelf": set(), - "mpich": set(), - "mpich2": set(), - "mpileaks": set(["callpath"] + mpi_names), + "libdwarf": set(["libelf"] + compiler_names), + "libelf": set(compiler_names), + "llvm": set(), + "mpich": set(compiler_names), + "mpich2": set(compiler_names), + "mpileaks": set(["callpath"] + mpi_names + compiler_names), "multi-provider-mpi": set(), - "zmpi": set(["fake"]), + "zmpi": set(["fake"] + compiler_names), } return possible @@ -58,32 +63,39 @@ def test_possible_dependencies(mock_packages, mpileaks_possible_deps): expanded_possible_deps = pkg_cls.possible_dependencies(expand_virtuals=True) assert mpileaks_possible_deps == expanded_possible_deps assert { - "callpath": {"dyninst", "mpi"}, - "dyninst": {"libdwarf", "libelf"}, - "libdwarf": {"libelf"}, - "libelf": set(), + "c": set(), + "callpath": {"dyninst", "mpi", "c"}, + "cxx": set(), + "dyninst": {"libdwarf", "libelf", "c"}, + "libdwarf": {"libelf", "c", "cxx"}, + "libelf": {"c"}, "mpi": set(), - "mpileaks": {"callpath", "mpi"}, + "mpileaks": {"c", "callpath", "mpi"}, } == pkg_cls.possible_dependencies(expand_virtuals=False) def test_possible_direct_dependencies(mock_packages, mpileaks_possible_deps): pkg_cls = spack.repo.PATH.get_pkg_class("mpileaks") deps = pkg_cls.possible_dependencies(transitive=False, expand_virtuals=False) - assert {"callpath": set(), "mpi": set(), "mpileaks": {"callpath", "mpi"}} == deps + assert { + "c": set(), + "callpath": set(), + "mpi": set(), + "mpileaks": {"callpath", "mpi", "c"}, + } == deps -def test_possible_dependencies_virtual(mock_packages, mpi_names): - expected = dict( - (name, set(dep for dep in spack.repo.PATH.get_pkg_class(name).dependencies_by_name())) - for name in mpi_names +def test_possible_dependencies_virtual(mock_packages, mpi_names, compiler_names): + expected = { + name: set(spack.repo.PATH.get_pkg_class(name).dependencies_by_name()) for name in mpi_names + } + + # One mock MPI has a dependency, and they depend on C, C++, and Fortran + expected.update({"fake": set(), "c": set(), "cxx": set(), "fortran": set()}) + assert expected == spack.package_base.possible_dependencies( + "mpi", expand_virtuals=False, transitive=False ) - # only one mock MPI has a dependency - expected["fake"] = set() - - assert expected == spack.package_base.possible_dependencies("mpi", transitive=False) - def test_possible_dependencies_missing(mock_packages): pkg_cls = spack.repo.PATH.get_pkg_class("missing-dependency") @@ -117,10 +129,10 @@ def test_possible_dependencies_with_multiple_classes(mock_packages, mpileaks_pos expected = mpileaks_possible_deps.copy() expected.update( { - "dt-diamond": set(["dt-diamond-left", "dt-diamond-right"]), - "dt-diamond-left": set(["dt-diamond-bottom"]), - "dt-diamond-right": set(["dt-diamond-bottom"]), - "dt-diamond-bottom": set(), + "dt-diamond": {"dt-diamond-left", "dt-diamond-right", "gcc", "llvm"}, + "dt-diamond-left": {"dt-diamond-bottom", "gcc", "llvm"}, + "dt-diamond-right": {"dt-diamond-bottom", "gcc", "llvm"}, + "dt-diamond-bottom": {"gcc", "llvm"}, } ) diff --git a/lib/spack/spack/test/packages.py b/lib/spack/spack/test/packages.py index ad4c797cf9d..5af2424b65b 100644 --- a/lib/spack/spack/test/packages.py +++ b/lib/spack/spack/test/packages.py @@ -69,7 +69,7 @@ def test_inheritance_of_directives(self): # Check dictionaries that should have been filled by directives dependencies = pkg_cls.dependencies_by_name() - assert len(dependencies) == 3 + assert len(dependencies) == 4 assert "cmake" in dependencies assert "openblas" in dependencies assert "mpi" in dependencies diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py index 05cc15f3dab..1a2e30d17d2 100644 --- a/lib/spack/spack/test/spec_dag.py +++ b/lib/spack/spack/test/spec_dag.py @@ -9,6 +9,7 @@ import spack.deptypes as dt import spack.error +import spack.installer import spack.repo import spack.util.hash as hashutil import spack.version @@ -80,8 +81,7 @@ def test_test_deptype(tmpdir): assert "z" not in spec -@pytest.mark.usefixtures("config") -def test_installed_deps(monkeypatch, mock_packages): +def test_installed_deps(monkeypatch, install_mockery): """Ensure that concrete specs and their build deps don't constrain solves. Preinstall a package ``c`` that has a constrained build dependency on ``d``, then @@ -101,28 +101,21 @@ def test_installed_deps(monkeypatch, mock_packages): # |/ \| c --> d build # d e c --> e build/link # - a, b, c, d, e = ["installed-deps-%s" % s for s in "abcde"] + a, b, c, d, e = [f"installed-deps-{s}" for s in "abcde"] # install C, which will force d's version to be 2 # BUT d is only a build dependency of C, so it won't constrain # link/run dependents of C when C is depended on as an existing # (concrete) installation. - c_spec = Spec(c) - c_spec.concretize() + c_spec = Spec(c).concretized() assert c_spec[d].version == spack.version.Version("2") - installed_names = [s.name for s in c_spec.traverse()] - - def _mock_installed(self): - return self.name in installed_names - - monkeypatch.setattr(Spec, "installed", _mock_installed) + spack.installer.PackageInstaller([c_spec.package], fake=True, explicit=True).install() # install A, which depends on B, C, D, and E, and force A to # use the installed C. It should *not* force A to use the installed D # *if* we're doing a fresh installation. - a_spec = Spec(a) - a_spec._add_dependency(c_spec, depflag=dt.BUILD | dt.LINK, virtuals=()) + a_spec = Spec(f"{a} ^/{c_spec.dag_hash()}") a_spec.concretize() assert spack.version.Version("2") == a_spec[c][d].version assert spack.version.Version("2") == a_spec[e].version @@ -184,120 +177,231 @@ def test_conflicting_package_constraints(self, set_dependency): with pytest.raises(spack.error.UnsatisfiableSpecError): spec.concretize() - def test_preorder_node_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() + @pytest.mark.parametrize( + "pairs,traverse_kwargs", + [ + # Preorder node traversal + ( + [ + (0, "mpileaks"), + (1, "callpath"), + (2, "dyninst"), + (3, "gcc"), + (3, "gcc-runtime"), + (3, "libdwarf"), + (4, "libelf"), + (2, "zmpi"), + (3, "fake"), + ], + {}, + ), + # Preorder edge traversal + ( + [ + (0, "mpileaks"), + (1, "callpath"), + (2, "dyninst"), + (3, "gcc"), + (3, "gcc-runtime"), + (4, "gcc"), + (3, "libdwarf"), + (4, "gcc"), + (4, "gcc-runtime"), + (4, "libelf"), + (5, "gcc"), + (5, "gcc-runtime"), + (3, "libelf"), + (2, "gcc"), + (2, "gcc-runtime"), + (2, "zmpi"), + (3, "fake"), + (3, "gcc"), + (3, "gcc-runtime"), + (1, "gcc"), + (1, "gcc-runtime"), + (1, "zmpi"), + ], + {"cover": "edges"}, + ), + # Preorder path traversal + ( + [ + (0, "mpileaks"), + (1, "callpath"), + (2, "dyninst"), + (3, "gcc"), + (3, "gcc-runtime"), + (4, "gcc"), + (3, "libdwarf"), + (4, "gcc"), + (4, "gcc-runtime"), + (5, "gcc"), + (4, "libelf"), + (5, "gcc"), + (5, "gcc-runtime"), + (6, "gcc"), + (3, "libelf"), + (4, "gcc"), + (4, "gcc-runtime"), + (5, "gcc"), + (2, "gcc"), + (2, "gcc-runtime"), + (3, "gcc"), + (2, "zmpi"), + (3, "fake"), + (3, "gcc"), + (3, "gcc-runtime"), + (4, "gcc"), + (1, "gcc"), + (1, "gcc-runtime"), + (2, "gcc"), + (1, "zmpi"), + (2, "fake"), + (2, "gcc"), + (2, "gcc-runtime"), + (3, "gcc"), + ], + {"cover": "paths"}, + ), + # Postorder node traversal + ( + [ + (3, "gcc"), + (3, "gcc-runtime"), + (4, "libelf"), + (3, "libdwarf"), + (2, "dyninst"), + (3, "fake"), + (2, "zmpi"), + (1, "callpath"), + (0, "mpileaks"), + ], + {"order": "post"}, + ), + # Postorder edge traversal + ( + [ + (3, "gcc"), + (4, "gcc"), + (3, "gcc-runtime"), + (4, "gcc"), + (4, "gcc-runtime"), + (5, "gcc"), + (5, "gcc-runtime"), + (4, "libelf"), + (3, "libdwarf"), + (3, "libelf"), + (2, "dyninst"), + (2, "gcc"), + (2, "gcc-runtime"), + (3, "fake"), + (3, "gcc"), + (3, "gcc-runtime"), + (2, "zmpi"), + (1, "callpath"), + (1, "gcc"), + (1, "gcc-runtime"), + (1, "zmpi"), + (0, "mpileaks"), + ], + {"cover": "edges", "order": "post"}, + ), + # Postorder path traversal + ( + [ + (3, "gcc"), + (4, "gcc"), + (3, "gcc-runtime"), + (4, "gcc"), + (5, "gcc"), + (4, "gcc-runtime"), + (5, "gcc"), + (6, "gcc"), + (5, "gcc-runtime"), + (4, "libelf"), + (3, "libdwarf"), + (4, "gcc"), + (5, "gcc"), + (4, "gcc-runtime"), + (3, "libelf"), + (2, "dyninst"), + (2, "gcc"), + (3, "gcc"), + (2, "gcc-runtime"), + (3, "fake"), + (3, "gcc"), + (4, "gcc"), + (3, "gcc-runtime"), + (2, "zmpi"), + (1, "callpath"), + (1, "gcc"), + (2, "gcc"), + (1, "gcc-runtime"), + (2, "fake"), + (2, "gcc"), + (3, "gcc"), + (2, "gcc-runtime"), + (1, "zmpi"), + (0, "mpileaks"), + ], + {"cover": "paths", "order": "post"}, + ), + ], + ) + def test_traversal(self, pairs, traverse_kwargs, default_mock_concretization): + r"""Tests different traversals of the following graph - names = ["mpileaks", "callpath", "dyninst", "libdwarf", "libelf", "zmpi", "fake"] - pairs = list(zip([0, 1, 2, 3, 4, 2, 3], names)) + o mpileaks + |\ + | |\ + | | |\ + | | | |\ + | | | | o callpath + | |_|_|/| + |/| |_|/| + | |/| |/| + | | |/|/| + o | | | | zmpi + |\| | | | + |\ \ \ \ \ + | |_|/ / / + |/| | | | + | |\ \ \ \ + | | |_|/ / + | |/| | | + | | o | | fake + | | / / + | | | o dyninst + | |_|/| + |/| |/| + | |/|/| + | | | |\ + | | | | o libdwarf + | |_|_|/| + |/| |_|/| + | |/| |/| + | | |/|/ + | | | o libel + | |_|/| + |/| |/| + | |/|/ + o | | gcc-runtime + |\| | + | |/ + |/| + o | glibc + / + o gcc + """ + dag = default_mock_concretization("mpileaks ^zmpi") + names = [x for _, x in pairs] - traversal = dag.traverse() - assert [x.name for x in traversal] == names - - traversal = dag.traverse(depth=True) + traversal = dag.traverse(**traverse_kwargs, depth=True) assert [(x, y.name) for x, y in traversal] == pairs - def test_preorder_edge_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() - - names = [ - "mpileaks", - "callpath", - "dyninst", - "libdwarf", - "libelf", - "libelf", - "zmpi", - "fake", - "zmpi", - ] - pairs = list(zip([0, 1, 2, 3, 4, 3, 2, 3, 1], names)) - - traversal = dag.traverse(cover="edges") + traversal = dag.traverse(**traverse_kwargs) assert [x.name for x in traversal] == names - traversal = dag.traverse(cover="edges", depth=True) - assert [(x, y.name) for x, y in traversal] == pairs - - def test_preorder_path_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() - - names = [ - "mpileaks", - "callpath", - "dyninst", - "libdwarf", - "libelf", - "libelf", - "zmpi", - "fake", - "zmpi", - "fake", - ] - pairs = list(zip([0, 1, 2, 3, 4, 3, 2, 3, 1, 2], names)) - - traversal = dag.traverse(cover="paths") - assert [x.name for x in traversal] == names - - traversal = dag.traverse(cover="paths", depth=True) - assert [(x, y.name) for x, y in traversal] == pairs - - def test_postorder_node_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() - - names = ["libelf", "libdwarf", "dyninst", "fake", "zmpi", "callpath", "mpileaks"] - pairs = list(zip([4, 3, 2, 3, 2, 1, 0], names)) - - traversal = dag.traverse(order="post") - assert [x.name for x in traversal] == names - - traversal = dag.traverse(depth=True, order="post") - assert [(x, y.name) for x, y in traversal] == pairs - - def test_postorder_edge_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() - - names = [ - "libelf", - "libdwarf", - "libelf", - "dyninst", - "fake", - "zmpi", - "callpath", - "zmpi", - "mpileaks", - ] - pairs = list(zip([4, 3, 3, 2, 3, 2, 1, 1, 0], names)) - - traversal = dag.traverse(cover="edges", order="post") - assert [x.name for x in traversal] == names - - traversal = dag.traverse(cover="edges", depth=True, order="post") - assert [(x, y.name) for x, y in traversal] == pairs - - def test_postorder_path_traversal(self): - dag = Spec("mpileaks ^zmpi").concretized() - - names = [ - "libelf", - "libdwarf", - "libelf", - "dyninst", - "fake", - "zmpi", - "callpath", - "fake", - "zmpi", - "mpileaks", - ] - pairs = list(zip([4, 3, 3, 2, 3, 2, 1, 2, 1, 0], names)) - - traversal = dag.traverse(cover="paths", order="post") - assert [x.name for x in traversal] == names - - traversal = dag.traverse(cover="paths", depth=True, order="post") - assert [(x, y.name) for x, y in traversal] == pairs - def test_dependents_and_dependencies_are_correct(self): spec = Spec.from_literal( { @@ -593,7 +697,9 @@ def test_construct_spec_with_deptypes(self): assert s["d"].edges_to_dependencies(name="e")[0].depflag == dt.BUILD | dt.LINK assert s["e"].edges_to_dependencies(name="f")[0].depflag == dt.RUN - assert s["c"].edges_from_dependents(name="b")[0].depflag == dt.BUILD + # The subscript follows link/run transitive deps or direct build deps, therefore + # we need an extra step to get to "c" + assert s["b"]["c"].edges_from_dependents(name="b")[0].depflag == dt.BUILD assert s["e"].edges_from_dependents(name="d")[0].depflag == dt.BUILD | dt.LINK assert s["f"].edges_from_dependents(name="e")[0].depflag == dt.RUN @@ -760,10 +866,10 @@ def test_spec_tree_respect_deptypes(self): "query,expected_length,expected_satisfies", [ ({"virtuals": ["mpi"]}, 1, ["mpich", "mpi"]), - ({"depflag": dt.BUILD}, 2, ["mpich", "mpi", "callpath"]), + ({"depflag": dt.BUILD}, 3, ["mpich", "mpi", "callpath"]), ({"depflag": dt.BUILD, "virtuals": ["mpi"]}, 1, ["mpich", "mpi"]), - ({"depflag": dt.LINK}, 2, ["mpich", "mpi", "callpath"]), - ({"depflag": dt.BUILD | dt.LINK}, 2, ["mpich", "mpi", "callpath"]), + ({"depflag": dt.LINK}, 3, ["mpich", "mpi", "callpath"]), + ({"depflag": dt.BUILD | dt.LINK}, 4, ["mpich", "mpi", "callpath"]), ({"virtuals": ["lapack"]}, 0, []), ], ) @@ -772,12 +878,14 @@ def test_query_dependency_edges( ): """Tests querying edges to dependencies on the following DAG: - [ ] mpileaks@=2.3 - [bl ] ^callpath@=1.0 - [bl ] ^dyninst@=8.2 - [bl ] ^libdwarf@=20130729 - [bl ] ^libelf@=0.8.13 - [bl ] ^mpich@=3.0.4 + - [ ] mpileaks@2.3 + - [bl ] ^callpath@1.0 + - [bl ] ^dyninst@8.2 + - [bl ] ^libdwarf@20130729 + - [bl ] ^libelf@0.8.13 + [e] [b ] ^gcc@10.1.0 + - [ l ] ^gcc-runtime@10.1.0 + - [bl ] ^mpich@3.0.4~debug """ mpileaks = default_mock_concretization("mpileaks") edges = mpileaks.edges_to_dependencies(**query) @@ -843,8 +951,8 @@ def test_synthetic_construction_of_split_dependencies_from_same_package(mock_pac root.add_dependency_edge(build_spec, depflag=dt.BUILD, virtuals=()) # Check dependencies from the perspective of root - assert len(root.dependencies()) == 2 - assert all(x.name == "pkg-c" for x in root.dependencies()) + assert len(root.dependencies()) == 4 + assert len([x for x in root.dependencies() if x.name == "pkg-c"]) == 2 assert "@2.0" in root.dependencies(name="pkg-c", deptype=dt.BUILD)[0] assert "@1.0" in root.dependencies(name="pkg-c", deptype=dt.LINK | dt.RUN)[0] @@ -868,8 +976,7 @@ def test_synthetic_construction_bootstrapping(mock_packages, config): root.add_dependency_edge(bootstrap, depflag=dt.BUILD, virtuals=()) - assert len(root.dependencies()) == 1 - assert root.dependencies()[0].name == "pkg-b" + assert len([x for x in root.dependencies() if x.name == "pkg-b"]) == 1 assert root.name == "pkg-b" @@ -888,8 +995,8 @@ def test_addition_of_different_deptypes_in_multiple_calls(mock_packages, config) root.add_dependency_edge(bootstrap, depflag=current_depflag, virtuals=()) # Check edges in dependencies - assert len(root.edges_to_dependencies()) == 1 - forward_edge = root.edges_to_dependencies(depflag=current_depflag)[0] + assert len(root.edges_to_dependencies(name="pkg-b")) == 1 + forward_edge = root.edges_to_dependencies(depflag=current_depflag, name="pkg-b")[0] assert current_depflag & forward_edge.depflag assert id(forward_edge.parent) == id(root) assert id(forward_edge.spec) == id(bootstrap) @@ -920,8 +1027,9 @@ def test_adding_same_deptype_with_the_same_name_raises( @pytest.mark.regression("33499") def test_indexing_prefers_direct_or_transitive_link_deps(): - # Test whether spec indexing prefers direct/transitive link type deps over deps of - # build/run/test deps, and whether it does fall back to a full dag search. + """Tests whether spec indexing prefers direct/transitive link/run type deps over deps of + build/test deps. + """ root = Spec("root") # Use a and z to since we typically traverse by edges sorted alphabetically. @@ -934,7 +1042,7 @@ def test_indexing_prefers_direct_or_transitive_link_deps(): z3_flavor_1 = Spec("z3 +through_a1") z3_flavor_2 = Spec("z3 +through_z1") - root.add_dependency_edge(a1, depflag=dt.BUILD | dt.RUN | dt.TEST, virtuals=()) + root.add_dependency_edge(a1, depflag=dt.BUILD | dt.TEST, virtuals=()) # unique package as a dep of a build/run/test type dep. a1.add_dependency_edge(a2, depflag=dt.ALL, virtuals=()) @@ -949,9 +1057,6 @@ def test_indexing_prefers_direct_or_transitive_link_deps(): assert "through_z1" in root["z3"].variants assert "through_a1" in a1["z3"].variants - # Ensure that the full DAG is still searched - assert root["a2"] - def test_getitem_sticks_to_subdag(): """Test that indexing on Spec by virtual does not traverse outside the dag, which happens in diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py index 34e5c6c11c7..9cbb011d995 100644 --- a/lib/spack/spack/test/spec_semantics.py +++ b/lib/spack/spack/test/spec_semantics.py @@ -18,15 +18,7 @@ import spack.variant import spack.version as vn from spack.error import SpecError, UnsatisfiableSpecError -from spack.spec import ( - ArchSpec, - CompilerSpec, - DependencySpec, - Spec, - SpecFormatSigilError, - SpecFormatStringError, - UnsupportedCompilerError, -) +from spack.spec import ArchSpec, DependencySpec, Spec, SpecFormatSigilError, SpecFormatStringError from spack.variant import ( InvalidVariantValueError, MultipleValuesInExclusiveVariantError, @@ -461,8 +453,6 @@ def test_concrete_specs_which_satisfies_abstract(self, lhs, rhs, default_mock_co ("foo os=redhat6", "platform=test os=debian6 target=x86_64"), ("foo target=x86_64", "platform=test os=redhat6 target=x86"), ("foo arch=test-frontend-frontend", "platform=test os=frontend target=backend"), - ("foo%intel", "%gcc"), - ("foo%intel", "%gcc"), ("foo%gcc@4.3", "%gcc@4.4:4.6"), ("foo@4.0%gcc", "@1:3%gcc"), ("foo@4.0%gcc@4.5", "@1:3%gcc@4.4:4.6"), @@ -868,18 +858,21 @@ def test_spec_formatting(self, default_mock_concretization): package_segments = [ ("{NAME}", "", "name", lambda spec: spec), ("{VERSION}", "", "version", lambda spec: spec), - ("{compiler}", "", "compiler", lambda spec: spec), + # FIXME (compiler as nodes): recover this semantic + # ("{compiler}", "", "compiler", lambda spec: spec), ("{compiler_flags}", "", "compiler_flags", lambda spec: spec), ("{variants}", "", "variants", lambda spec: spec), ("{architecture}", "", "architecture", lambda spec: spec), ("{@VERSIONS}", "@", "versions", lambda spec: spec), - ("{%compiler}", "%", "compiler", lambda spec: spec), + # FIXME (compiler as nodes): recover this semantic + # ("{%compiler}", "%", "compiler", lambda spec: spec), ("{arch=architecture}", "arch=", "architecture", lambda spec: spec), ("{namespace=namespace}", "namespace=", "namespace", lambda spec: spec), - ("{compiler.name}", "", "name", lambda spec: spec.compiler), - ("{compiler.version}", "", "version", lambda spec: spec.compiler), - ("{%compiler.name}", "%", "name", lambda spec: spec.compiler), - ("{@compiler.version}", "@", "version", lambda spec: spec.compiler), + # FIXME (compiler as nodes): recover this semantic + # ("{compiler.name}", "", "name", lambda spec: spec.compiler), + # ("{compiler.version}", "", "version", lambda spec: spec.compiler), + # ("{%compiler.name}", "%", "name", lambda spec: spec.compiler), + # ("{@compiler.version}", "@", "version", lambda spec: spec.compiler), ("{architecture.platform}", "", "platform", lambda spec: spec.architecture), ("{architecture.os}", "", "os", lambda spec: spec.architecture), ("{architecture.target}", "", "target", lambda spec: spec.architecture), @@ -942,7 +935,6 @@ def check_prop(check_spec, fmt_str, prop, getter): "{name}", "{version}", "{@version}", - "{%compiler}", "{namespace}", "{ namespace=namespace}", "{ namespace =namespace}", @@ -1520,16 +1512,17 @@ def test_unsatisfiable_virtual_deps_bindings(self, spec_str): ("git-test@git.foo/bar", "{name}-{version}", str(pathlib.Path("git-test-git.foo_bar"))), ("git-test@git.foo/bar", "{name}-{version}-{/hash}", None), ("git-test@git.foo/bar", "{name}/{version}", str(pathlib.Path("git-test", "git.foo_bar"))), - ( - "git-test@{0}=1.0%gcc".format("a" * 40), - "{name}/{version}/{compiler}", - str(pathlib.Path("git-test", "{0}_1.0".format("a" * 40), "gcc")), - ), - ( - "git-test@git.foo/bar=1.0%gcc", - "{name}/{version}/{compiler}", - str(pathlib.Path("git-test", "git.foo_bar_1.0", "gcc")), - ), + # FIXME (compiler as nodes): revisit these tests + # ( + # "git-test@{0}=1.0%gcc".format("a" * 40), + # "{name}/{version}/{compiler}", + # str(pathlib.Path("git-test", "{0}_1.0".format("a" * 40), "gcc")), + # ), + # ( + # "git-test@git.foo/bar=1.0%gcc", + # "{name}/{version}/{compiler}", + # str(pathlib.Path("git-test", "git.foo_bar_1.0", "gcc")), + # ), ], ) def test_spec_format_path(spec_str, format_str, expected, mock_git_test_package): @@ -1712,12 +1705,18 @@ def test_call_dag_hash_on_old_dag_hash_spec(mock_packages, default_mock_concreti def test_spec_trim(mock_packages, config): top = Spec("dt-diamond").concretized() top.trim("dt-diamond-left") - remaining = set(x.name for x in top.traverse()) - assert set(["dt-diamond", "dt-diamond-right", "dt-diamond-bottom"]) == remaining + remaining = {x.name for x in top.traverse()} + assert { + "dt-diamond", + "dt-diamond-right", + "dt-diamond-bottom", + "gcc-runtime", + "gcc", + } == remaining top.trim("dt-diamond-right") - remaining = set(x.name for x in top.traverse()) - assert set(["dt-diamond"]) == remaining + remaining = {x.name for x in top.traverse()} + assert {"dt-diamond", "gcc-runtime", "gcc"} == remaining @pytest.mark.regression("30861") @@ -1747,11 +1746,6 @@ def test_concretize_partial_old_dag_hash_spec(mock_packages, config): assert not getattr(spec["dt-diamond-bottom"], "_package_hash", None) -def test_unsupported_compiler(): - with pytest.raises(UnsupportedCompilerError): - Spec("gcc%fake-compiler").validate_or_raise() - - def test_package_hash_affects_dunder_and_dag_hash(mock_packages, default_mock_concretization): a1 = default_mock_concretization("pkg-a") a2 = default_mock_concretization("pkg-a") @@ -1824,10 +1818,10 @@ def test_abstract_contains_semantic(lhs, rhs, expected, mock_packages): (ArchSpec, "None-ubuntu20.04-None", "None-ubuntu20.04-None", (True, True, True)), (ArchSpec, "None-ubuntu20.04-None", "None-ubuntu22.04-None", (False, False, False)), # Compiler - (CompilerSpec, "gcc", "clang", (False, False, False)), - (CompilerSpec, "gcc", "gcc@5", (True, False, True)), - (CompilerSpec, "gcc@5", "gcc@5.3", (True, False, True)), - (CompilerSpec, "gcc@5", "gcc@5-tag", (True, False, True)), + (Spec, "gcc", "clang", (False, False, False)), + (Spec, "gcc", "gcc@5", (True, False, True)), + (Spec, "gcc@5", "gcc@5.3", (True, False, True)), + (Spec, "gcc@5", "gcc@5-tag", (True, False, True)), # Flags (flags are a map, so for convenience we initialize a full Spec) # Note: the semantic is that of sv variants, not mv variants (Spec, "cppflags=-foo", "cppflags=-bar", (True, False, False)), @@ -1883,8 +1877,8 @@ def test_intersects_and_satisfies(factory, lhs_str, rhs_str, results): "None-ubuntu20.04-nocona,haswell", ), # Compiler - (CompilerSpec, "gcc@5", "gcc@5-tag", True, "gcc@5-tag"), - (CompilerSpec, "gcc@5", "gcc@5", False, "gcc@5"), + (Spec, "foo %gcc@5", "foo %gcc@5-tag", True, "foo %gcc@5-tag"), + (Spec, "foo %gcc@5", "foo %gcc@5", False, "foo %gcc@5"), # Flags (Spec, "cppflags=-foo", "cppflags=-foo", False, "cppflags=-foo"), (Spec, "cppflags=-foo", "cflags=-foo", True, "cppflags=-foo cflags=-foo"), diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py index 74f9bb7ea02..af09cff5386 100644 --- a/lib/spack/spack/test/spec_syntax.py +++ b/lib/spack/spack/test/spec_syntax.py @@ -815,9 +815,7 @@ def test_dep_spec_by_hash(database, config): assert "zmpi" in mpileaks_hash_fake assert mpileaks_hash_fake["zmpi"] == spack.spec.Spec("zmpi") - mpileaks_hash_zmpi = SpecParser( - f"mpileaks %{mpileaks_zmpi.compiler} ^ /{zmpi.dag_hash()}" - ).next_spec() + mpileaks_hash_zmpi = SpecParser(f"mpileaks ^ /{zmpi.dag_hash()}").next_spec() mpileaks_hash_zmpi.replace_hash() assert "zmpi" in mpileaks_hash_zmpi assert mpileaks_hash_zmpi["zmpi"] == zmpi diff --git a/lib/spack/spack/test/tag.py b/lib/spack/spack/test/tag.py index e429b9f4578..6af31b0eec4 100644 --- a/lib/spack/spack/test/tag.py +++ b/lib/spack/spack/test/tag.py @@ -86,7 +86,7 @@ def test_tag_get_available(tags, expected, mock_packages): def test_tag_get_installed_packages(mock_packages, mock_archive, mock_fetch, install_mockery): - install("mpich") + install("--fake", "mpich") for skip in [False, True]: all_pkgs = spack.tag.packages_with_tags(None, True, skip)