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