From 022a2d2eafb04850b6a2b80b77642627e2878658 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Fri, 11 Nov 2022 21:32:40 +0100 Subject: Speed-up unit tests by caching default mock concretization (#33755) --- lib/spack/spack/test/architecture.py | 39 +++++++------ lib/spack/spack/test/build_systems.py | 92 ++++++++++++++++-------------- lib/spack/spack/test/ci.py | 33 +++++------ lib/spack/spack/test/conftest.py | 24 ++++++++ lib/spack/spack/test/database.py | 20 +++---- lib/spack/spack/test/directory_layout.py | 11 ++-- lib/spack/spack/test/git_fetch.py | 47 ++++++++++----- lib/spack/spack/test/packaging.py | 18 ++++-- lib/spack/spack/test/spec_semantics.py | 98 +++++++++++++------------------- lib/spack/spack/test/url_fetch.py | 16 ++++-- 10 files changed, 218 insertions(+), 180 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py index 92914be685..ef95e4b383 100644 --- a/lib/spack/spack/test/architecture.py +++ b/lib/spack/spack/test/architecture.py @@ -17,7 +17,7 @@ import spack.spec import spack.target -@pytest.fixture +@pytest.fixture(scope="module") def current_host_platform(): """Return the platform of the current host as detected by the 'platform' stdlib package. @@ -34,23 +34,23 @@ def current_host_platform(): # Valid keywords for os=xxx or target=xxx -valid_keywords = ["fe", "be", "frontend", "backend"] +VALID_KEYWORDS = ["fe", "be", "frontend", "backend"] +TEST_PLATFORM = spack.platforms.Test() -@pytest.fixture( - params=([x for x in spack.platforms.Test().targets] + valid_keywords + ["default_target"]) -) + +@pytest.fixture(params=([str(x) for x in TEST_PLATFORM.targets] + VALID_KEYWORDS), scope="module") def target_str(request): """All the possible strings that can be used for targets""" - return str(request.param) + return request.param @pytest.fixture( - params=([x for x in spack.platforms.Test().operating_sys] + valid_keywords + ["default_os"]) + params=([str(x) for x in TEST_PLATFORM.operating_sys] + VALID_KEYWORDS), scope="module" ) def os_str(request): """All the possible strings that can be used for operating systems""" - return str(request.param) + return request.param def test_platform(current_host_platform): @@ -64,16 +64,19 @@ def test_user_input_combination(config, target_str, os_str): """Test for all the valid user input combinations that both the target and the operating system match. """ - platform = spack.platforms.Test() - spec_str = "libelf" - if os_str != "default_os": - spec_str += " os={0}".format(os_str) - if target_str != "default_target": - spec_str += " target={0}".format(target_str) - spec = spack.spec.Spec(spec_str).concretized() - - assert spec.architecture.os == str(platform.operating_system(os_str)) - assert spec.architecture.target == platform.target(target_str) + spec_str = "libelf os={} target={}".format(os_str, target_str) + spec = spack.spec.Spec(spec_str) + assert spec.architecture.os == str(TEST_PLATFORM.operating_system(os_str)) + assert spec.architecture.target == TEST_PLATFORM.target(target_str) + + +def test_default_os_and_target(config): + """Test that is we don't specify `os=` or `target=` we get the default values + after concretization. + """ + spec = spack.spec.Spec("libelf").concretized() + assert spec.architecture.os == str(TEST_PLATFORM.operating_system("default_os")) + assert spec.architecture.target == TEST_PLATFORM.target("default_target") def test_operating_system_conversion_to_dict(): diff --git a/lib/spack/spack/test/build_systems.py b/lib/spack/spack/test/build_systems.py index 03a920a28e..5202a7f58d 100644 --- a/lib/spack/spack/test/build_systems.py +++ b/lib/spack/spack/test/build_systems.py @@ -27,9 +27,9 @@ pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="does not run on @pytest.fixture() -def concretize_and_setup(): +def concretize_and_setup(default_mock_concretization): def _func(spec_str): - s = Spec("mpich").concretized() + s = default_mock_concretization(spec_str) setup_package(s.package, False) return s @@ -95,8 +95,8 @@ class TestTargets(object): @pytest.mark.usefixtures("config", "mock_packages") class TestAutotoolsPackage(object): - def test_with_or_without(self): - s = Spec("a").concretized() + def test_with_or_without(self, default_mock_concretization): + s = default_mock_concretization("a") options = s.package.with_or_without("foo") # Ensure that values that are not representing a feature @@ -127,8 +127,8 @@ class TestAutotoolsPackage(object): options = s.package.with_or_without("lorem-ipsum", variant="lorem_ipsum") assert "--without-lorem-ipsum" in options - def test_none_is_allowed(self): - s = Spec("a foo=none").concretized() + def test_none_is_allowed(self, default_mock_concretization): + s = default_mock_concretization("a foo=none") options = s.package.with_or_without("foo") # Ensure that values that are not representing a feature @@ -138,9 +138,11 @@ class TestAutotoolsPackage(object): assert "--without-baz" in options assert "--no-fee" in options - def test_libtool_archive_files_are_deleted_by_default(self, mutable_database): + def test_libtool_archive_files_are_deleted_by_default( + self, default_mock_concretization, mutable_database + ): # Install a package that creates a mock libtool archive - s = Spec("libtool-deletion").concretized() + s = default_mock_concretization("libtool-deletion") s.package.do_install(explicit=True) # Assert the libtool archive is not there and we have @@ -151,24 +153,25 @@ class TestAutotoolsPackage(object): assert libtool_deletion_log def test_libtool_archive_files_might_be_installed_on_demand( - self, mutable_database, monkeypatch + self, mutable_database, monkeypatch, default_mock_concretization ): # Install a package that creates a mock libtool archive, # patch its package to preserve the installation - s = Spec("libtool-deletion").concretized() + s = default_mock_concretization("libtool-deletion") monkeypatch.setattr(type(s.package.builder), "install_libtool_archives", True) s.package.do_install(explicit=True) # Assert libtool archives are installed assert os.path.exists(s.package.builder.libtool_archive_file) - def test_autotools_gnuconfig_replacement(self, mutable_database): + def test_autotools_gnuconfig_replacement(self, default_mock_concretization, mutable_database): """ Tests whether only broken config.sub and config.guess are replaced with files from working alternatives from the gnuconfig package. """ - s = Spec("autotools-config-replacement +patch_config_files +gnuconfig") - s.concretize() + s = default_mock_concretization( + "autotools-config-replacement +patch_config_files +gnuconfig" + ) s.package.do_install() with open(os.path.join(s.prefix.broken, "config.sub")) as f: @@ -183,12 +186,15 @@ class TestAutotoolsPackage(object): with open(os.path.join(s.prefix.working, "config.guess")) as f: assert "gnuconfig version of config.guess" not in f.read() - def test_autotools_gnuconfig_replacement_disabled(self, mutable_database): + def test_autotools_gnuconfig_replacement_disabled( + self, default_mock_concretization, mutable_database + ): """ Tests whether disabling patch_config_files """ - s = Spec("autotools-config-replacement ~patch_config_files +gnuconfig") - s.concretize() + s = default_mock_concretization( + "autotools-config-replacement ~patch_config_files +gnuconfig" + ) s.package.do_install() with open(os.path.join(s.prefix.broken, "config.sub")) as f: @@ -252,29 +258,29 @@ spack: @pytest.mark.usefixtures("config", "mock_packages") class TestCMakePackage(object): - def test_cmake_std_args(self): + def test_cmake_std_args(self, default_mock_concretization): # Call the function on a CMakePackage instance - s = Spec("cmake-client").concretized() + s = default_mock_concretization("cmake-client") expected = spack.build_systems.cmake.CMakeBuilder.std_args(s.package) assert s.package.builder.std_cmake_args == expected # Call it on another kind of package - s = Spec("mpich").concretized() + s = default_mock_concretization("mpich") assert spack.build_systems.cmake.CMakeBuilder.std_args(s.package) - def test_cmake_bad_generator(self, monkeypatch): - s = Spec("cmake-client").concretized() + def test_cmake_bad_generator(self, monkeypatch, default_mock_concretization): + s = default_mock_concretization("cmake-client") monkeypatch.setattr(type(s.package), "generator", "Yellow Sticky Notes", raising=False) with pytest.raises(spack.package_base.InstallError): s.package.builder.std_cmake_args - def test_cmake_secondary_generator(config, mock_packages): - s = Spec("cmake-client").concretized() + def test_cmake_secondary_generator(self, default_mock_concretization): + s = default_mock_concretization("cmake-client") s.package.generator = "CodeBlocks - Unix Makefiles" assert s.package.builder.std_cmake_args - def test_define(self): - s = Spec("cmake-client").concretized() + def test_define(self, default_mock_concretization): + s = default_mock_concretization("cmake-client") define = s.package.define for cls in (list, tuple): @@ -324,8 +330,8 @@ class TestDownloadMixins(object): ), ], ) - def test_attributes_defined(self, spec_str, expected_url): - s = Spec(spec_str).concretized() + def test_attributes_defined(self, default_mock_concretization, spec_str, expected_url): + s = default_mock_concretization(spec_str) assert s.package.urls[0] == expected_url @pytest.mark.parametrize( @@ -344,33 +350,33 @@ class TestDownloadMixins(object): ("mirror-xorg-broken", r"{0} must define a `xorg_mirror_path` attribute"), ], ) - def test_attributes_missing(self, spec_str, error_fmt): - s = Spec(spec_str).concretized() + def test_attributes_missing(self, default_mock_concretization, spec_str, error_fmt): + s = default_mock_concretization(spec_str) error_msg = error_fmt.format(type(s.package).__name__) with pytest.raises(AttributeError, match=error_msg): s.package.urls -def test_cmake_define_from_variant_conditional(config, mock_packages): +def test_cmake_define_from_variant_conditional(default_mock_concretization): """Test that define_from_variant returns empty string when a condition on a variant is not met. When this is the case, the variant is not set in the spec.""" - s = Spec("cmake-conditional-variants-test").concretized() + s = default_mock_concretization("cmake-conditional-variants-test") assert "example" not in s.variants assert s.package.define_from_variant("EXAMPLE", "example") == "" -def test_autotools_args_from_conditional_variant(config, mock_packages): +def test_autotools_args_from_conditional_variant(default_mock_concretization): """Test that _activate_or_not returns an empty string when a condition on a variant is not met. When this is the case, the variant is not set in the spec.""" - s = Spec("autotools-conditional-variants-test").concretized() + s = default_mock_concretization("autotools-conditional-variants-test") assert "example" not in s.variants assert len(s.package.builder._activate_or_not("example", "enable", "disable")) == 0 -def test_autoreconf_search_path_args_multiple(config, mock_packages, tmpdir): +def test_autoreconf_search_path_args_multiple(default_mock_concretization, tmpdir): """autoreconf should receive the right -I flags with search paths for m4 files for build deps.""" - spec = Spec("dttop").concretized() + spec = default_mock_concretization("dttop") aclocal_fst = str(tmpdir.mkdir("fst").mkdir("share").mkdir("aclocal")) aclocal_snd = str(tmpdir.mkdir("snd").mkdir("share").mkdir("aclocal")) build_dep_one, build_dep_two = spec.dependencies(deptype="build") @@ -384,11 +390,11 @@ def test_autoreconf_search_path_args_multiple(config, mock_packages, tmpdir): ] -def test_autoreconf_search_path_args_skip_automake(config, mock_packages, tmpdir): +def test_autoreconf_search_path_args_skip_automake(default_mock_concretization, tmpdir): """automake's aclocal dir should not be added as -I flag as it is a default 3rd party dir search path, and if it's a system version it usually includes m4 files shadowing spack deps.""" - spec = Spec("dttop").concretized() + spec = default_mock_concretization("dttop") tmpdir.mkdir("fst").mkdir("share").mkdir("aclocal") aclocal_snd = str(tmpdir.mkdir("snd").mkdir("share").mkdir("aclocal")) build_dep_one, build_dep_two = spec.dependencies(deptype="build") @@ -398,9 +404,9 @@ def test_autoreconf_search_path_args_skip_automake(config, mock_packages, tmpdir assert spack.build_systems.autotools._autoreconf_search_path_args(spec) == ["-I", aclocal_snd] -def test_autoreconf_search_path_args_external_order(config, mock_packages, tmpdir): +def test_autoreconf_search_path_args_external_order(default_mock_concretization, tmpdir): """When a build dep is external, its -I flag should occur last""" - spec = Spec("dttop").concretized() + spec = default_mock_concretization("dttop") aclocal_fst = str(tmpdir.mkdir("fst").mkdir("share").mkdir("aclocal")) aclocal_snd = str(tmpdir.mkdir("snd").mkdir("share").mkdir("aclocal")) build_dep_one, build_dep_two = spec.dependencies(deptype="build") @@ -414,18 +420,18 @@ def test_autoreconf_search_path_args_external_order(config, mock_packages, tmpdi ] -def test_autoreconf_search_path_skip_nonexisting(config, mock_packages, tmpdir): +def test_autoreconf_search_path_skip_nonexisting(default_mock_concretization, tmpdir): """Skip -I flags for non-existing directories""" - spec = Spec("dttop").concretized() + spec = default_mock_concretization("dttop") build_dep_one, build_dep_two = spec.dependencies(deptype="build") build_dep_one.prefix = str(tmpdir.join("fst")) build_dep_two.prefix = str(tmpdir.join("snd")) assert spack.build_systems.autotools._autoreconf_search_path_args(spec) == [] -def test_autoreconf_search_path_dont_repeat(config, mock_packages, tmpdir): +def test_autoreconf_search_path_dont_repeat(default_mock_concretization, tmpdir): """Do not add the same -I flag twice to keep things readable for humans""" - spec = Spec("dttop").concretized() + spec = default_mock_concretization("dttop") aclocal = str(tmpdir.mkdir("prefix").mkdir("share").mkdir("aclocal")) build_dep_one, build_dep_two = spec.dependencies(deptype="build") build_dep_one.external_path = str(tmpdir.join("prefix")) diff --git a/lib/spack/spack/test/ci.py b/lib/spack/spack/test/ci.py index 2a4786d51b..a55f5841eb 100644 --- a/lib/spack/spack/test/ci.py +++ b/lib/spack/spack/test/ci.py @@ -18,7 +18,6 @@ import spack.config as cfg import spack.environment as ev import spack.error import spack.paths as spack_paths -import spack.spec as spec import spack.util.gpg import spack.util.spack_yaml as syaml @@ -145,13 +144,12 @@ def test_download_and_extract_artifacts(tmpdir, monkeypatch, working_env): ci.download_and_extract_artifacts(url, working_dir) -def test_ci_copy_stage_logs_to_artifacts_fail(tmpdir, config, mock_packages, monkeypatch, capfd): +def test_ci_copy_stage_logs_to_artifacts_fail(tmpdir, default_mock_concretization, capfd): """The copy will fail because the spec is not concrete so does not have a package.""" log_dir = tmpdir.join("log_dir") - s = spec.Spec("printing-package").concretized() - - ci.copy_stage_logs_to_artifacts(s, log_dir) + concrete_spec = default_mock_concretization("printing-package") + ci.copy_stage_logs_to_artifacts(concrete_spec, log_dir) _, err = capfd.readouterr() assert "Unable to copy files" in err assert "No such file or directory" in err @@ -456,17 +454,16 @@ def test_get_spec_filter_list(mutable_mock_env_path, config, mutable_mock_repo): assert affected_pkg_names == expected_affected_pkg_names -@pytest.mark.maybeslow @pytest.mark.regression("29947") -def test_affected_specs_on_first_concretization(mutable_mock_env_path, config): +def test_affected_specs_on_first_concretization(mutable_mock_env_path, mock_packages, config): e = ev.create("first_concretization") - e.add("hdf5~mpi~szip") - e.add("hdf5~mpi+szip") + e.add("mpileaks~shared") + e.add("mpileaks+shared") e.concretize() - affected_specs = spack.ci.get_spec_filter_list(e, ["zlib"]) - hdf5_specs = [s for s in affected_specs if s.name == "hdf5"] - assert len(hdf5_specs) == 2 + affected_specs = spack.ci.get_spec_filter_list(e, ["callpath"]) + mpileaks_specs = [s for s in affected_specs if s.name == "mpileaks"] + assert len(mpileaks_specs) == 2, e.all_specs() @pytest.mark.skipif( @@ -515,14 +512,14 @@ def test_ci_create_buildcache(tmpdir, working_env, config, mock_packages, monkey def test_ci_run_standalone_tests_missing_requirements( - tmpdir, working_env, config, mock_packages, capfd + tmpdir, working_env, default_mock_concretization, capfd ): """This test case checks for failing prerequisite checks.""" ci.run_standalone_tests() err = capfd.readouterr()[1] assert "Job spec is required" in err - args = {"job_spec": spec.Spec("printing-package").concretized()} + args = {"job_spec": default_mock_concretization("printing-package")} ci.run_standalone_tests(**args) err = capfd.readouterr()[1] assert "Reproduction directory is required" in err @@ -532,12 +529,12 @@ def test_ci_run_standalone_tests_missing_requirements( sys.platform == "win32", reason="Reliance on bash script not supported on Windows" ) def test_ci_run_standalone_tests_not_installed_junit( - tmpdir, working_env, config, mock_packages, mock_test_stage, capfd + tmpdir, working_env, default_mock_concretization, mock_test_stage, capfd ): log_file = tmpdir.join("junit.xml").strpath args = { "log_file": log_file, - "job_spec": spec.Spec("printing-package").concretized(), + "job_spec": default_mock_concretization("printing-package"), "repro_dir": tmpdir.join("repro_dir").strpath, "fail_fast": True, } @@ -553,13 +550,13 @@ def test_ci_run_standalone_tests_not_installed_junit( sys.platform == "win32", reason="Reliance on bash script not supported on Windows" ) def test_ci_run_standalone_tests_not_installed_cdash( - tmpdir, working_env, config, mock_packages, mock_test_stage, capfd + tmpdir, working_env, default_mock_concretization, mock_test_stage, capfd ): """Test run_standalone_tests with cdash and related options.""" log_file = tmpdir.join("junit.xml").strpath args = { "log_file": log_file, - "job_spec": spec.Spec("printing-package").concretized(), + "job_spec": default_mock_concretization("printing-package"), "repro_dir": tmpdir.join("repro_dir").strpath, } os.makedirs(args["repro_dir"]) diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py index 6516c9c01f..c19c0c5528 100644 --- a/lib/spack/spack/test/conftest.py +++ b/lib/spack/spack/test/conftest.py @@ -1852,3 +1852,27 @@ def binary_with_rpaths(prefix_tmpdir): return executable return _factory + + +@pytest.fixture(scope="session") +def concretized_specs_cache(): + """Cache for mock concrete specs""" + return {} + + +@pytest.fixture +def default_mock_concretization(config, mock_packages, concretized_specs_cache): + """Return the default mock concretization of a spec literal, obtained using the mock + repository and the mock configuration. + + This fixture is unsafe to call in a test when either the default configuration or mock + repository are not used or have been modified. + """ + + def _func(spec_str, tests=False): + key = spec_str, tests + if key not in concretized_specs_cache: + concretized_specs_cache[key] = spack.spec.Spec(spec_str).concretized(tests=tests) + return concretized_specs_cache[key].copy() + + return _func diff --git a/lib/spack/spack/test/database.py b/lib/spack/spack/test/database.py index bd06e5eb2c..99e1b5c470 100644 --- a/lib/spack/spack/test/database.py +++ b/lib/spack/spack/test/database.py @@ -835,7 +835,7 @@ def test_clear_failure_keep(mutable_database, monkeypatch, capfd): @pytest.mark.db -def test_clear_failure_forced(mutable_database, monkeypatch, capfd): +def test_clear_failure_forced(default_mock_concretization, mutable_database, monkeypatch, capfd): """Add test coverage for clear_failure operation when force.""" def _is(db, spec): @@ -846,7 +846,7 @@ def test_clear_failure_forced(mutable_database, monkeypatch, capfd): # Ensure raise OSError when try to remove the non-existent marking monkeypatch.setattr(spack.database.Database, "prefix_failure_marked", _is) - s = spack.spec.Spec("a").concretized() + s = default_mock_concretization("a") spack.store.db.clear_failure(s, force=True) out = capfd.readouterr()[1] assert "Removing failure marking despite lock" in out @@ -854,7 +854,7 @@ def test_clear_failure_forced(mutable_database, monkeypatch, capfd): @pytest.mark.db -def test_mark_failed(mutable_database, monkeypatch, tmpdir, capsys): +def test_mark_failed(default_mock_concretization, mutable_database, monkeypatch, tmpdir, capsys): """Add coverage to mark_failed.""" def _raise_exc(lock): @@ -864,7 +864,7 @@ def test_mark_failed(mutable_database, monkeypatch, tmpdir, capsys): monkeypatch.setattr(lk.Lock, "acquire_write", _raise_exc) with tmpdir.as_cwd(): - s = spack.spec.Spec("a").concretized() + s = default_mock_concretization("a") spack.store.db.mark_failed(s) out = str(capsys.readouterr()[1]) @@ -876,13 +876,13 @@ def test_mark_failed(mutable_database, monkeypatch, tmpdir, capsys): @pytest.mark.db -def test_prefix_failed(mutable_database, monkeypatch): +def test_prefix_failed(default_mock_concretization, mutable_database, monkeypatch): """Add coverage to prefix_failed operation.""" def _is(db, spec): return True - s = spack.spec.Spec("a").concretized() + s = default_mock_concretization("a") # Confirm the spec is not already marked as failed assert not spack.store.db.prefix_failed(s) @@ -900,13 +900,13 @@ def test_prefix_failed(mutable_database, monkeypatch): assert spack.store.db.prefix_failed(s) -def test_prefix_read_lock_error(mutable_database, monkeypatch): +def test_prefix_read_lock_error(default_mock_concretization, mutable_database, monkeypatch): """Cover the prefix read lock exception.""" def _raise(db, spec): raise lk.LockError("Mock lock error") - s = spack.spec.Spec("a").concretized() + s = default_mock_concretization("a") # Ensure subsequent lock operations fail monkeypatch.setattr(lk.Lock, "acquire_read", _raise) @@ -916,13 +916,13 @@ def test_prefix_read_lock_error(mutable_database, monkeypatch): assert False -def test_prefix_write_lock_error(mutable_database, monkeypatch): +def test_prefix_write_lock_error(default_mock_concretization, mutable_database, monkeypatch): """Cover the prefix write lock exception.""" def _raise(db, spec): raise lk.LockError("Mock lock error") - s = spack.spec.Spec("a").concretized() + s = default_mock_concretization("a") # Ensure subsequent lock operations fail monkeypatch.setattr(lk.Lock, "acquire_write", _raise) diff --git a/lib/spack/spack/test/directory_layout.py b/lib/spack/spack/test/directory_layout.py index 32e0a340c6..86d160c5cc 100644 --- a/lib/spack/spack/test/directory_layout.py +++ b/lib/spack/spack/test/directory_layout.py @@ -24,11 +24,10 @@ from spack.util.path import path_to_os_path max_packages = 10 -def test_yaml_directory_layout_parameters(tmpdir, config): +def test_yaml_directory_layout_parameters(tmpdir, default_mock_concretization): """This tests the various parameters that can be used to configure the install location""" - spec = Spec("python") - spec.concretize() + spec = default_mock_concretization("python") # Ensure default layout matches expected spec format layout_default = DirectoryLayout(str(tmpdir)) @@ -215,11 +214,9 @@ def test_find(temporary_store, config, mock_packages): assert found_specs[name].eq_dag(spec) -def test_yaml_directory_layout_build_path(tmpdir, config): +def test_yaml_directory_layout_build_path(tmpdir, default_mock_concretization): """This tests build path method.""" - spec = Spec("python") - spec.concretize() - + spec = default_mock_concretization("python") layout = DirectoryLayout(str(tmpdir)) rel_path = os.path.join(layout.metadata_dir, layout.packages_dir) assert layout.build_packages_path(spec) == os.path.join(spec.prefix, rel_path) diff --git a/lib/spack/spack/test/git_fetch.py b/lib/spack/spack/test/git_fetch.py index 0f9f587421..678a30a4fd 100644 --- a/lib/spack/spack/test/git_fetch.py +++ b/lib/spack/spack/test/git_fetch.py @@ -83,7 +83,13 @@ def test_bad_git(tmpdir, mock_bad_git): @pytest.mark.parametrize("type_of_test", ["default", "branch", "tag", "commit"]) @pytest.mark.parametrize("secure", [True, False]) def test_fetch( - type_of_test, secure, mock_git_repository, config, mutable_mock_repo, git_version, monkeypatch + type_of_test, + secure, + mock_git_repository, + default_mock_concretization, + mutable_mock_repo, + git_version, + monkeypatch, ): """Tries to: @@ -104,7 +110,7 @@ def test_fetch( monkeypatch.delattr(pkg_class, "git") # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") monkeypatch.setitem(s.package.versions, ver("git"), t.args) # Enter the stage directory and check some properties @@ -136,7 +142,7 @@ def test_fetch( @pytest.mark.disable_clean_stage_check def test_fetch_pkg_attr_submodule_init( - mock_git_repository, config, mutable_mock_repo, monkeypatch, mock_stage + mock_git_repository, default_mock_concretization, mutable_mock_repo, monkeypatch, mock_stage ): """In this case the version() args do not contain a 'git' URL, so the fetcher must be assembled using the Package-level 'git' attribute. @@ -151,7 +157,7 @@ def test_fetch_pkg_attr_submodule_init( monkeypatch.setattr(pkg_class, "git", mock_git_repository.url) # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") monkeypatch.setitem(s.package.versions, ver("git"), t.args) s.package.do_stage() @@ -193,13 +199,15 @@ def test_adhoc_version_submodules( @pytest.mark.parametrize("type_of_test", ["branch", "commit"]) -def test_debug_fetch(mock_packages, type_of_test, mock_git_repository, config, monkeypatch): +def test_debug_fetch( + mock_packages, type_of_test, mock_git_repository, default_mock_concretization, monkeypatch +): """Fetch the repo with debug enabled.""" # Retrieve the right test parameters t = mock_git_repository.checks[type_of_test] # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") monkeypatch.setitem(s.package.versions, ver("git"), t.args) # Fetch then ensure source path exists @@ -231,7 +239,12 @@ def test_needs_stage(): @pytest.mark.parametrize("get_full_repo", [True, False]) def test_get_full_repo( - get_full_repo, git_version, mock_git_repository, config, mutable_mock_repo, monkeypatch + get_full_repo, + git_version, + mock_git_repository, + default_mock_concretization, + mutable_mock_repo, + monkeypatch, ): """Ensure that we can clone a full repository.""" @@ -243,7 +256,7 @@ def test_get_full_repo( t = mock_git_repository.checks[type_of_test] - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") args = copy.copy(t.args) args["get_full_repo"] = get_full_repo monkeypatch.setitem(s.package.versions, ver("git"), args) @@ -273,7 +286,9 @@ def test_get_full_repo( @pytest.mark.disable_clean_stage_check @pytest.mark.parametrize("submodules", [True, False]) -def test_gitsubmodule(submodules, mock_git_repository, config, mutable_mock_repo, monkeypatch): +def test_gitsubmodule( + submodules, mock_git_repository, default_mock_concretization, mutable_mock_repo, monkeypatch +): """ Test GitFetchStrategy behavior with submodules. This package has a `submodules` property which is always True: when a specific @@ -286,7 +301,7 @@ def test_gitsubmodule(submodules, mock_git_repository, config, mutable_mock_repo t = mock_git_repository.checks[type_of_test] # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") args = copy.copy(t.args) args["submodules"] = submodules monkeypatch.setitem(s.package.versions, ver("git"), args) @@ -304,7 +319,9 @@ def test_gitsubmodule(submodules, mock_git_repository, config, mutable_mock_repo @pytest.mark.disable_clean_stage_check -def test_gitsubmodules_callable(mock_git_repository, config, mutable_mock_repo, monkeypatch): +def test_gitsubmodules_callable( + mock_git_repository, default_mock_concretization, mutable_mock_repo, monkeypatch +): """ Test GitFetchStrategy behavior with submodules selected after concretization """ @@ -317,7 +334,7 @@ def test_gitsubmodules_callable(mock_git_repository, config, mutable_mock_repo, t = mock_git_repository.checks[type_of_test] # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") args = copy.copy(t.args) args["submodules"] = submodules_callback monkeypatch.setitem(s.package.versions, ver("git"), args) @@ -330,7 +347,9 @@ def test_gitsubmodules_callable(mock_git_repository, config, mutable_mock_repo, @pytest.mark.disable_clean_stage_check -def test_gitsubmodules_delete(mock_git_repository, config, mutable_mock_repo, monkeypatch): +def test_gitsubmodules_delete( + mock_git_repository, default_mock_concretization, mutable_mock_repo, monkeypatch +): """ Test GitFetchStrategy behavior with submodules_delete """ @@ -338,7 +357,7 @@ def test_gitsubmodules_delete(mock_git_repository, config, mutable_mock_repo, mo t = mock_git_repository.checks[type_of_test] # Construct the package under test - s = Spec("git-test").concretized() + s = default_mock_concretization("git-test") args = copy.copy(t.args) args["submodules"] = True args["submodules_delete"] = ["third_party/submodule0", "third_party/submodule1"] diff --git a/lib/spack/spack/test/packaging.py b/lib/spack/spack/test/packaging.py index f0294f5a74..f889cd5384 100644 --- a/lib/spack/spack/test/packaging.py +++ b/lib/spack/spack/test/packaging.py @@ -570,7 +570,9 @@ def mock_download(): "manual,instr", [(False, False), (False, True), (True, False), (True, True)] ) @pytest.mark.disable_clean_stage_check -def test_manual_download(install_mockery, mock_download, monkeypatch, manual, instr): +def test_manual_download( + install_mockery, mock_download, default_mock_concretization, monkeypatch, manual, instr +): """ Ensure expected fetcher fail message based on manual download and instr. """ @@ -579,7 +581,7 @@ def test_manual_download(install_mockery, mock_download, monkeypatch, manual, in def _instr(pkg): return "Download instructions for {0}".format(pkg.spec.name) - spec = Spec("a").concretized() + spec = default_mock_concretization("a") pkg = spec.package pkg.manual_download = manual @@ -605,16 +607,20 @@ def fetching_not_allowed(monkeypatch): monkeypatch.setattr(spack.package_base.PackageBase, "fetcher", fetcher) -def test_fetch_without_code_is_noop(install_mockery, fetching_not_allowed): +def test_fetch_without_code_is_noop( + default_mock_concretization, install_mockery, fetching_not_allowed +): """do_fetch for packages without code should be a no-op""" - pkg = Spec("a").concretized().package + pkg = default_mock_concretization("a").package pkg.has_code = False pkg.do_fetch() -def test_fetch_external_package_is_noop(install_mockery, fetching_not_allowed): +def test_fetch_external_package_is_noop( + default_mock_concretization, install_mockery, fetching_not_allowed +): """do_fetch for packages without code should be a no-op""" - spec = Spec("a").concretized() + spec = default_mock_concretization("a") spec.external_path = "/some/where" assert spec.external spec.package.do_fetch() diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py index 04aa82797c..e2cbf706da 100644 --- a/lib/spack/spack/test/spec_semantics.py +++ b/lib/spack/spack/test/spec_semantics.py @@ -719,9 +719,8 @@ class TestSpecSematics(object): with pytest.raises(ValueError): Spec("libelf foo") - def test_spec_formatting(self): - spec = Spec("multivalue-variant cflags=-O2") - spec.concretize() + def test_spec_formatting(self, default_mock_concretization): + spec = default_mock_concretization("multivalue-variant cflags=-O2") # Since the default is the full spec see if the string rep of # spec is the same as the output of spec.format() @@ -797,9 +796,8 @@ class TestSpecSematics(object): actual = spec.format(named_str) assert expected == actual - def test_spec_formatting_escapes(self): - spec = Spec("multivalue-variant cflags=-O2") - spec.concretize() + def test_spec_formatting_escapes(self, default_mock_concretization): + spec = default_mock_concretization("multivalue-variant cflags=-O2") sigil_mismatches = [ "{@name}", @@ -881,7 +879,7 @@ class TestSpecSematics(object): # different orderings for repeated concretizations of the same # spec and config spec_str = "libelf %gcc@11.1.0 os=redhat6" - for _ in range(25): + for _ in range(3): s = Spec(spec_str).concretized() assert all( s.compiler_flags[x] == ["-O0", "-g"] for x in ("cflags", "cxxflags", "fflags") @@ -954,13 +952,11 @@ class TestSpecSematics(object): assert spec.target < "broadwell" @pytest.mark.parametrize("transitive", [True, False]) - def test_splice(self, transitive): + def test_splice(self, transitive, default_mock_concretization): # Tests the new splice function in Spec using a somewhat simple case # with a variant with a conditional dependency. - spec = Spec("splice-t") - dep = Spec("splice-h+foo") - spec.concretize() - dep.concretize() + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") # Sanity checking that these are not the same thing. assert dep.dag_hash() != spec["splice-h"].dag_hash() @@ -993,11 +989,9 @@ class TestSpecSematics(object): assert out.spliced @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_with_cached_hashes(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-h+foo") - spec.concretize() - dep.concretize() + def test_splice_with_cached_hashes(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") # monkeypatch hashes so we can test that they are cached spec._hash = "aaaaaa" @@ -1014,9 +1008,9 @@ class TestSpecSematics(object): assert out["splice-z"].dag_hash() == out_z_expected.dag_hash() @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_input_unchanged(self, transitive): - spec = Spec("splice-t").concretized() - dep = Spec("splice-h+foo").concretized() + def test_splice_input_unchanged(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") orig_spec_hash = spec.dag_hash() orig_dep_hash = dep.dag_hash() spec.splice(dep, transitive) @@ -1026,16 +1020,13 @@ class TestSpecSematics(object): assert dep.dag_hash() == orig_dep_hash @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_subsequent(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-h+foo") - spec.concretize() - dep.concretize() + def test_splice_subsequent(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") out = spec.splice(dep, transitive) # Now we attempt a second splice. - dep = Spec("splice-z+bar") - dep.concretize() + dep = default_mock_concretization("splice-z+bar") # Transitivity shouldn't matter since Splice Z has no dependencies. out2 = out.splice(dep, transitive) @@ -1046,11 +1037,9 @@ class TestSpecSematics(object): assert out2.spliced @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_dict(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-h+foo") - spec.concretize() - dep.concretize() + def test_splice_dict(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") out = spec.splice(dep, transitive) # Sanity check all hashes are unique... @@ -1065,11 +1054,9 @@ class TestSpecSematics(object): assert len(build_spec_nodes) == 1 @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_dict_roundtrip(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-h+foo") - spec.concretize() - dep.concretize() + def test_splice_dict_roundtrip(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-h+foo") out = spec.splice(dep, transitive) # Sanity check all hashes are unique... @@ -1132,21 +1119,17 @@ class TestSpecSematics(object): assert s.satisfies("mpileaks ^zmpi ^fake", strict=True) @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_swap_names(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-a+foo") - spec.concretize() - dep.concretize() + def test_splice_swap_names(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-a+foo") out = spec.splice(dep, transitive) assert dep.name in out assert transitive == ("+foo" in out["splice-z"]) @pytest.mark.parametrize("transitive", [True, False]) - def test_splice_swap_names_mismatch_virtuals(self, transitive): - spec = Spec("splice-t") - dep = Spec("splice-vh+foo") - spec.concretize() - dep.concretize() + def test_splice_swap_names_mismatch_virtuals(self, default_mock_concretization, transitive): + spec = default_mock_concretization("splice-t") + dep = default_mock_concretization("splice-vh+foo") with pytest.raises(spack.spec.SpliceError, match="will not provide the same virtuals."): spec.splice(dep, transitive) @@ -1166,12 +1149,11 @@ class TestSpecSematics(object): @pytest.mark.regression("3887") -@pytest.mark.parametrize("spec_str", ["git", "hdf5", "py-flake8"]) -def test_is_extension_after_round_trip_to_dict(config, spec_str): +@pytest.mark.parametrize("spec_str", ["py-extension2", "extension1", "perl-extension"]) +def test_is_extension_after_round_trip_to_dict(config, mock_packages, spec_str): # x is constructed directly from string, y from a # round-trip to dict representation - x = Spec(spec_str) - x.concretize() + x = Spec(spec_str).concretized() y = Spec.from_dict(x.to_dict()) # Using 'y' since the round-trip make us lose build dependencies @@ -1231,7 +1213,7 @@ def test_merge_anonymous_spec_with_named_spec(anonymous, named, expected): assert s == Spec(expected) -def test_spec_installed(install_mockery, database): +def test_spec_installed(default_mock_concretization, database): """Test whether Spec.installed works.""" # a known installed spec should say that it's installed specs = database.query() @@ -1244,14 +1226,14 @@ def test_spec_installed(install_mockery, database): assert not spec.installed # 'a' is not in the mock DB and is not installed - spec = Spec("a").concretized() + spec = default_mock_concretization("a") assert not spec.installed @pytest.mark.regression("30678") -def test_call_dag_hash_on_old_dag_hash_spec(mock_packages, config): +def test_call_dag_hash_on_old_dag_hash_spec(mock_packages, default_mock_concretization): # create a concrete spec - a = Spec("a").concretized() + a = default_mock_concretization("a") dag_hashes = {spec.name: spec.dag_hash() for spec in a.traverse()} # make it look like an old DAG hash spec with no package hash on the spec. @@ -1298,9 +1280,9 @@ def test_unsupported_compiler(): Spec("gcc%fake-compiler").validate_or_raise() -def test_package_hash_affects_dunder_and_dag_hash(mock_packages, config): - a1 = Spec("a").concretized() - a2 = Spec("a").concretized() +def test_package_hash_affects_dunder_and_dag_hash(mock_packages, default_mock_concretization): + a1 = default_mock_concretization("a") + a2 = default_mock_concretization("a") assert hash(a1) == hash(a2) assert a1.dag_hash() == a2.dag_hash() diff --git a/lib/spack/spack/test/url_fetch.py b/lib/spack/spack/test/url_fetch.py index da13611585..024974c132 100644 --- a/lib/spack/spack/test/url_fetch.py +++ b/lib/spack/spack/test/url_fetch.py @@ -136,18 +136,22 @@ if sys.platform != "win32": @pytest.mark.parametrize("secure", [True, False]) @pytest.mark.parametrize("_fetch_method", ["curl", "urllib"]) @pytest.mark.parametrize("mock_archive", files, indirect=True) -def test_fetch(mock_archive, secure, _fetch_method, checksum_type, config, mutable_mock_repo): +def test_fetch( + mock_archive, + secure, + _fetch_method, + checksum_type, + default_mock_concretization, + mutable_mock_repo, +): """Fetch an archive and make sure we can checksum it.""" - mock_archive.url - mock_archive.path - algo = crypto.hash_fun_for_algo(checksum_type)() with open(mock_archive.archive_file, "rb") as f: algo.update(f.read()) checksum = algo.hexdigest() - # Get a spec and tweak the test package with new chcecksum params - s = Spec("url-test").concretized() + # Get a spec and tweak the test package with new checksum params + s = default_mock_concretization("url-test") s.package.url = mock_archive.url s.package.versions[ver("test")] = {checksum_type: checksum, "url": s.package.url} -- cgit v1.2.3-60-g2f50