diff --git a/lib/spack/spack/test/traverse.py b/lib/spack/spack/test/traverse.py index 663f323e67..b88bfdb95e 100644 --- a/lib/spack/spack/test/traverse.py +++ b/lib/spack/spack/test/traverse.py @@ -3,28 +3,102 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import pytest + import spack.traverse as traverse from spack.spec import Spec -def key_by_hash(spec): - return spec.dag_hash() +def create_dag(nodes, edges): + """ + Arguments: + nodes: list of package names + edges: list of tuples (from, to, deptype) + Returns: + dict: mapping from package name to abstract Spec with proper deps. + """ + specs = {name: Spec(name) for name in nodes} + for parent, child, deptype in edges: + specs[parent].add_dependency_edge(specs[child], deptype) + return specs -def test_breadth_first_traversal(config, mock_packages): +@pytest.fixture() +def abstract_specs_dtuse(): + nodes = [ + "dtbuild1", + "dtbuild2", + "dtbuild3", + "dtlink1", + "dtlink2", + "dtlink3", + "dtlink4", + "dtlink5", + "dtrun1", + "dtrun2", + "dtrun3", + "dttop", + "dtuse", + ] + edges = [ + ("dtbuild1", "dtbuild2", ("build")), + ("dtbuild1", "dtlink2", ("build", "link")), + ("dtbuild1", "dtrun2", ("run")), + ("dtlink1", "dtlink3", ("build", "link")), + ("dtlink3", "dtbuild2", ("build")), + ("dtlink3", "dtlink4", ("build", "link")), + ("dtrun1", "dtlink5", ("build", "link")), + ("dtrun1", "dtrun3", ("run")), + ("dtrun3", "dtbuild3", ("build")), + ("dttop", "dtbuild1", ("build",)), + ("dttop", "dtlink1", ("build", "link")), + ("dttop", "dtrun1", ("run")), + ("dtuse", "dttop", ("build", "link")), + ] + return create_dag(nodes, edges) + + +@pytest.fixture() +def abstract_specs_dt_diamond(): + nodes = ["dt-diamond", "dt-diamond-left", "dt-diamond-right", "dt-diamond-bottom"] + edges = [ + ("dt-diamond", "dt-diamond-left", ("build", "link")), + ("dt-diamond", "dt-diamond-right", ("build", "link")), + ("dt-diamond-right", "dt-diamond-bottom", ("build", "link", "run")), + ("dt-diamond-left", "dt-diamond-bottom", ("build")), + ] + return create_dag(nodes, edges) + + +@pytest.fixture() +def abstract_specs_chain(): + # Chain a -> b -> c -> d with skip connections + # from a -> c and a -> d. + nodes = ["chain-a", "chain-b", "chain-c", "chain-d"] + edges = [ + ("chain-a", "chain-b", ("build", "link")), + ("chain-b", "chain-c", ("build", "link")), + ("chain-c", "chain-d", ("build", "link")), + ("chain-a", "chain-c", ("build", "link")), + ("chain-a", "chain-d", ("build", "link")), + ] + return create_dag(nodes, edges) + + +def test_breadth_first_traversal(abstract_specs_dtuse): # That that depth of discovery is non-decreasing - s = Spec("dttop").concretized() + s = abstract_specs_dtuse["dttop"] depths = [ depth for (depth, _) in traverse.traverse_nodes( - [s], order="breadth", key=key_by_hash, depth=True + [s], order="breadth", key=lambda s: s.name, depth=True ) ] assert depths == sorted(depths) -def test_breadth_first_deptype_traversal(config, mock_packages): - s = Spec("dtuse").concretized() +def test_breadth_first_deptype_traversal(abstract_specs_dtuse): + s = abstract_specs_dtuse["dtuse"] names = [ "dtuse", @@ -37,25 +111,21 @@ def test_breadth_first_deptype_traversal(config, mock_packages): "dtlink4", ] - traversal = traverse.traverse_nodes( - [s], order="breadth", key=key_by_hash, deptype=("build", "link") - ) + traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype=("build", "link")) assert [x.name for x in traversal] == names -def test_breadth_firsrt_traversal_deptype_with_builddeps(config, mock_packages): - s = Spec("dttop").concretized() +def test_breadth_firsrt_traversal_deptype_with_builddeps(abstract_specs_dtuse): + s = abstract_specs_dtuse["dttop"] names = ["dttop", "dtbuild1", "dtlink1", "dtbuild2", "dtlink2", "dtlink3", "dtlink4"] - traversal = traverse.traverse_nodes( - [s], order="breadth", key=key_by_hash, deptype=("build", "link") - ) + traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype=("build", "link")) assert [x.name for x in traversal] == names -def test_breadth_first_traversal_deptype_full(config, mock_packages): - s = Spec("dttop").concretized() +def test_breadth_first_traversal_deptype_full(abstract_specs_dtuse): + s = abstract_specs_dtuse["dttop"] names = [ "dttop", @@ -72,21 +142,24 @@ def test_breadth_first_traversal_deptype_full(config, mock_packages): "dtbuild3", ] - traversal = traverse.traverse_nodes([s], order="breadth", key=key_by_hash, deptype="all") + traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype="all") assert [x.name for x in traversal] == names -def test_breadth_first_traversal_deptype_run(config, mock_packages): - s = Spec("dttop").concretized() +def test_breadth_first_traversal_deptype_run(abstract_specs_dtuse): + s = abstract_specs_dtuse["dttop"] names = ["dttop", "dtrun1", "dtrun3"] - traversal = traverse.traverse_nodes([s], order="breadth", key=key_by_hash, deptype="run") + traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype="run") assert [x.name for x in traversal] == names -def test_breadth_first_traversal_reverse(config, mock_packages): - s = Spec("dt-diamond").concretized() +def test_breadth_first_traversal_reverse(abstract_specs_dt_diamond): gen = traverse.traverse_nodes( - [s["dt-diamond-bottom"]], order="breadth", key=key_by_hash, direction="parents", depth=True + [abstract_specs_dt_diamond["dt-diamond-bottom"]], + order="breadth", + key=id, + direction="parents", + depth=True, ) assert [(depth, spec.name) for (depth, spec) in gen] == [ (0, "dt-diamond-bottom"), @@ -96,20 +169,22 @@ def test_breadth_first_traversal_reverse(config, mock_packages): ] -def test_breadth_first_traversal_multiple_roots(config, mock_packages): +def test_breadth_first_traversal_multiple_roots(abstract_specs_dt_diamond): # With DFS, the branch dt-diamond -> dt-diamond-left -> dt-diamond-bottom # is followed, with BFS, dt-diamond-bottom should be traced through the second # root dt-diamond-right at depth 1 instead. - s = Spec("dt-diamond").concretized() - roots = [s["dt-diamond"], s["dt-diamond-right"]] - gen = traverse.traverse_edges(roots, order="breadth", key=key_by_hash, depth=True, root=False) + roots = [ + abstract_specs_dt_diamond["dt-diamond"], + abstract_specs_dt_diamond["dt-diamond-right"], + ] + gen = traverse.traverse_edges(roots, order="breadth", key=id, depth=True, root=False) assert [(depth, edge.parent.name, edge.spec.name) for (depth, edge) in gen] == [ (1, "dt-diamond", "dt-diamond-left"), # edge from first root "to" depth 1 (1, "dt-diamond-right", "dt-diamond-bottom"), # edge from second root "to" depth 1 ] -def test_breadth_first_versus_depth_first_tree(config, mock_packages): +def test_breadth_first_versus_depth_first_tree(abstract_specs_chain): """ The packages chain-a, chain-b, chain-c, chain-d have the following DAG: a --> b --> c --> d # a chain @@ -117,7 +192,7 @@ def test_breadth_first_versus_depth_first_tree(config, mock_packages): a --> d Here we test at what depth the nodes are discovered when using BFS vs DFS. """ - s = Spec("chain-a").concretized() + s = abstract_specs_chain["chain-a"] # BFS should find all nodes as direct deps assert [ @@ -168,9 +243,9 @@ def test_breadth_first_versus_depth_first_tree(config, mock_packages): ] -def test_breadth_first_versus_depth_first_printing(config, mock_packages): +def test_breadth_first_versus_depth_first_printing(abstract_specs_chain): """Test breadth-first versus depth-first tree printing.""" - s = Spec("chain-a").concretized() + s = abstract_specs_chain["chain-a"] args = {"format": "{name}", "color": False} diff --git a/var/spack/repos/builtin.mock/packages/chain-a/package.py b/var/spack/repos/builtin.mock/packages/chain-a/package.py deleted file mode 100644 index 6dc7dc2c90..0000000000 --- a/var/spack/repos/builtin.mock/packages/chain-a/package.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -from spack.package import * - - -class ChainA(Package): - """ - Part of a collection of mock packages used for testing depth-first vs - breadth-first traversal. The DAG they form: - a --> b --> c --> d # a chain - a --> c # "skip" connection - a --> d # "skip" connection - Spack's edge order is based on the child package name. - In depth-first traversal we get a tree that looks like a chain: - a - b - c - d - In breadth-first we get the tree: - a - b - c - d - """ - - homepage = "https://example.com" - has_code = False - version("1.0") - depends_on("chain-b") - depends_on("chain-c") - depends_on("chain-d") diff --git a/var/spack/repos/builtin.mock/packages/chain-b/package.py b/var/spack/repos/builtin.mock/packages/chain-b/package.py deleted file mode 100644 index ede29c0e0f..0000000000 --- a/var/spack/repos/builtin.mock/packages/chain-b/package.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -from spack.package import * - - -class ChainB(Package): - """ - Part of a collection of mock packages used for testing depth-first vs - breadth-first traversal. The DAG they form: - a --> b --> c --> d # a chain - a --> c # "skip" connection - a --> d # "skip" connection - Spack's edge order is based on the child package name. - In depth-first traversal we get a tree that looks like a chain: - a - b - c - d - In breadth-first we get the tree: - a - b - c - d - """ - - homepage = "https://example.com" - has_code = False - version("1.0") - depends_on("chain-c") diff --git a/var/spack/repos/builtin.mock/packages/chain-c/package.py b/var/spack/repos/builtin.mock/packages/chain-c/package.py deleted file mode 100644 index e9d919f0ba..0000000000 --- a/var/spack/repos/builtin.mock/packages/chain-c/package.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -from spack.package import * - - -class ChainC(Package): - """ - Part of a collection of mock packages used for testing depth-first vs - breadth-first traversal. The DAG they form: - a --> b --> c --> d # a chain - a --> c # "skip" connection - a --> d # "skip" connection - Spack's edge order is based on the child package name. - In depth-first traversal we get a tree that looks like a chain: - a - b - c - d - In breadth-first we get the tree: - a - b - c - d - """ - - homepage = "https://example.com" - has_code = False - version("1.0") - depends_on("chain-d") diff --git a/var/spack/repos/builtin.mock/packages/chain-d/package.py b/var/spack/repos/builtin.mock/packages/chain-d/package.py deleted file mode 100644 index f2e04089ee..0000000000 --- a/var/spack/repos/builtin.mock/packages/chain-d/package.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -from spack.package import * - - -class ChainD(Package): - """ - Part of a collection of mock packages used for testing depth-first vs - breadth-first traversal. The DAG they form: - a --> b --> c --> d # a chain - a --> c # "skip" connection - a --> d # "skip" connection - Spack's edge order is based on the child package name. - In depth-first traversal we get a tree that looks like a chain: - a - b - c - d - In breadth-first we get the tree: - a - b - c - d - """ - - homepage = "https://example.com" - has_code = False - version("1.0")