Speed up traverse unit tests (#33840)
This commit is contained in:
		@@ -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}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
@@ -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")
 | 
			
		||||
@@ -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")
 | 
			
		||||
@@ -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")
 | 
			
		||||
		Reference in New Issue
	
	Block a user