Speed up traverse unit tests (#33840)

This commit is contained in:
Harmen Stoppels 2022-11-14 09:34:14 +01:00 committed by GitHub
parent 3812edd0db
commit a4cec82841
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 161 deletions

View File

@ -3,28 +3,102 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import pytest
import spack.traverse as traverse import spack.traverse as traverse
from spack.spec import Spec from spack.spec import Spec
def key_by_hash(spec): def create_dag(nodes, edges):
return spec.dag_hash() """
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 # That that depth of discovery is non-decreasing
s = Spec("dttop").concretized() s = abstract_specs_dtuse["dttop"]
depths = [ depths = [
depth depth
for (depth, _) in traverse.traverse_nodes( 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) assert depths == sorted(depths)
def test_breadth_first_deptype_traversal(config, mock_packages): def test_breadth_first_deptype_traversal(abstract_specs_dtuse):
s = Spec("dtuse").concretized() s = abstract_specs_dtuse["dtuse"]
names = [ names = [
"dtuse", "dtuse",
@ -37,25 +111,21 @@ def test_breadth_first_deptype_traversal(config, mock_packages):
"dtlink4", "dtlink4",
] ]
traversal = traverse.traverse_nodes( traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype=("build", "link"))
[s], order="breadth", key=key_by_hash, deptype=("build", "link")
)
assert [x.name for x in traversal] == names assert [x.name for x in traversal] == names
def test_breadth_firsrt_traversal_deptype_with_builddeps(config, mock_packages): def test_breadth_firsrt_traversal_deptype_with_builddeps(abstract_specs_dtuse):
s = Spec("dttop").concretized() s = abstract_specs_dtuse["dttop"]
names = ["dttop", "dtbuild1", "dtlink1", "dtbuild2", "dtlink2", "dtlink3", "dtlink4"] names = ["dttop", "dtbuild1", "dtlink1", "dtbuild2", "dtlink2", "dtlink3", "dtlink4"]
traversal = traverse.traverse_nodes( traversal = traverse.traverse_nodes([s], order="breadth", key=id, deptype=("build", "link"))
[s], order="breadth", key=key_by_hash, deptype=("build", "link")
)
assert [x.name for x in traversal] == names assert [x.name for x in traversal] == names
def test_breadth_first_traversal_deptype_full(config, mock_packages): def test_breadth_first_traversal_deptype_full(abstract_specs_dtuse):
s = Spec("dttop").concretized() s = abstract_specs_dtuse["dttop"]
names = [ names = [
"dttop", "dttop",
@ -72,21 +142,24 @@ def test_breadth_first_traversal_deptype_full(config, mock_packages):
"dtbuild3", "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 assert [x.name for x in traversal] == names
def test_breadth_first_traversal_deptype_run(config, mock_packages): def test_breadth_first_traversal_deptype_run(abstract_specs_dtuse):
s = Spec("dttop").concretized() s = abstract_specs_dtuse["dttop"]
names = ["dttop", "dtrun1", "dtrun3"] 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 assert [x.name for x in traversal] == names
def test_breadth_first_traversal_reverse(config, mock_packages): def test_breadth_first_traversal_reverse(abstract_specs_dt_diamond):
s = Spec("dt-diamond").concretized()
gen = traverse.traverse_nodes( 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] == [ assert [(depth, spec.name) for (depth, spec) in gen] == [
(0, "dt-diamond-bottom"), (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 # 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 # is followed, with BFS, dt-diamond-bottom should be traced through the second
# root dt-diamond-right at depth 1 instead. # root dt-diamond-right at depth 1 instead.
s = Spec("dt-diamond").concretized() roots = [
roots = [s["dt-diamond"], s["dt-diamond-right"]] abstract_specs_dt_diamond["dt-diamond"],
gen = traverse.traverse_edges(roots, order="breadth", key=key_by_hash, depth=True, root=False) 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] == [ 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", "dt-diamond-left"), # edge from first root "to" depth 1
(1, "dt-diamond-right", "dt-diamond-bottom"), # edge from second 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: The packages chain-a, chain-b, chain-c, chain-d have the following DAG:
a --> b --> c --> d # a chain a --> b --> c --> d # a chain
@ -117,7 +192,7 @@ def test_breadth_first_versus_depth_first_tree(config, mock_packages):
a --> d a --> d
Here we test at what depth the nodes are discovered when using BFS vs DFS. 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 # BFS should find all nodes as direct deps
assert [ 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.""" """Test breadth-first versus depth-first tree printing."""
s = Spec("chain-a").concretized() s = abstract_specs_chain["chain-a"]
args = {"format": "{name}", "color": False} args = {"format": "{name}", "color": False}

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")