Skip to content

Commit 1d7e750

Browse files
committed
test coverage
1 parent 527f718 commit 1d7e750

31 files changed

Lines changed: 1516 additions & 41 deletions

docs/reference/api-full.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Quick links:
1212
- [CLI Reference](cli.md)
1313
- [DSL Reference](dsl.md)
1414

15-
Generated from source code on: August 15, 2025 at 04:00 UTC
15+
Generated from source code on: August 15, 2025 at 11:31 UTC
1616

1717
Modules auto-discovered: 72
1818

@@ -2451,22 +2451,22 @@ convenience to run both.
24512451

24522452
## ngraph.workflow.analysis.capacity_matrix
24532453

2454-
Capacity matrix analysis for MaxFlow results.
2454+
Capacity matrix analysis.
24552455

2456-
Consumes ``flow_results`` from a MaxFlow step and builds a node->node capacity
2457-
matrix using the maximum placed value observed per pair across iterations
2458-
(capacity ceiling under the tested failure set). Provides statistics and a
2459-
heatmap for quick visual inspection.
2456+
Consumes `flow_results` (from MaxFlow step). Builds node→node capacity matrix
2457+
using the *maximum placed value observed* per pair across iterations (i.e., the
2458+
capacity ceiling under the tested failure set). Provides stats and a heatmap.
24602459

24612460
### CapacityMatrixAnalyzer
24622461

2463-
Analyze max-flow capacities into matrices, statistics, and plots.
2462+
Analyze max-flow capacities into matrices/statistics/plots.
24642463

24652464
**Methods:**
24662465

2467-
- `analyze(self, results: 'dict[str, Any]', **kwargs) -> 'dict[str, Any]'` - Compute capacity matrix for a MaxFlow step.
2466+
- `analyze(self, results: 'Dict[str, Any]', **kwargs) -> 'Dict[str, Any]'` - Return analysis outputs for a given results document.
24682467
- `analyze_and_display(self, results: 'dict[str, Any]', **kwargs) -> 'None'` - Analyze results and render them in notebook format.
2469-
- `display_analysis(self, analysis: 'dict[str, Any]', **kwargs) -> 'None'` - Render capacity matrix statistics and heatmap.
2468+
- `analyze_and_display_step(self, results: 'Dict[str, Any]', **kwargs) -> 'None'` - Analyze and render capacity matrix for a single workflow step.
2469+
- `display_analysis(self, analysis: 'Dict[str, Any]', **kwargs) -> 'None'` - Render analysis outputs in notebook format.
24702470
- `get_description(self) -> 'str'` - Return a concise description of the analyzer purpose.
24712471

24722472
---
@@ -2628,8 +2628,8 @@ Includes bindings for ``NetworkStats``, ``MaximumSupportedDemand``,
26282628
High-level summary analyzer for results documents.
26292629

26302630
Provides quick counts of steps and basic categorisation by presence of
2631-
``flow_results`` in the new schema. Also contains convenience helpers for
2632-
``NetworkStats`` sections.
2631+
``flow_results`` in the new schema. Also contains a small helper for
2632+
``NetworkStats`` sections aimed at notebook usage.
26332633

26342634
### SummaryAnalyzer
26352635

ngraph/demand/manager/manager.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
from ngraph.graph.strict_multidigraph import StrictMultiDiGraph
2121
from ngraph.model.network import Network
2222

23-
if TYPE_CHECKING:
24-
from ngraph.demand.matrix import TrafficMatrixSet
25-
from ngraph.model.view import NetworkView
23+
if TYPE_CHECKING: # pragma: no cover - typing-only imports
24+
from ngraph.demand.matrix import TrafficMatrixSet # pragma: no cover
25+
from ngraph.model.view import NetworkView # pragma: no cover
2626

2727

2828
def _new_td_map() -> Dict[str, List[Demand]]:

ngraph/workflow/analysis/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import itables.options as itables_opt
1313
import matplotlib.pyplot as plt
14-
from itables import show
14+
from itables import show # pragma: no cover - display-only binding
1515

1616
from .bac import BACAnalyzer
1717
from .base import AnalysisContext, NotebookAnalyzer

ngraph/workflow/analysis/bac.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,22 @@ def availability_curve(series: pd.Series):
122122
return xs, avail
123123

124124
x, a = availability_curve(s)
125-
plt.figure(figsize=(9, 5.5))
126-
sns.lineplot(x=x, y=a, drawstyle="steps-post", label=mode.capitalize())
125+
plt.figure(figsize=(9, 5.5)) # pragma: no cover - display-only
126+
sns.lineplot(
127+
x=x, y=a, drawstyle="steps-post", label=mode.capitalize()
128+
) # pragma: no cover - display-only
127129
if overlay is not None and len(overlay) == len(s):
128130
xo, ao = availability_curve(overlay)
129131
sns.lineplot(
130132
x=xo, y=ao, drawstyle="steps-post", label=overlay_label or "overlay"
131-
)
133+
) # pragma: no cover - display-only
132134

133135
plt.xlabel("Delivered bandwidth (Gbps)")
134136
plt.ylabel("Availability (≥x)")
135137
plt.title(f"Bandwidth-Availability Curve - {name}")
136-
plt.grid(True, linestyle=":", linewidth=0.5)
137-
plt.tight_layout()
138-
plt.show()
138+
plt.grid(True, linestyle=":", linewidth=0.5) # pragma: no cover - display-only
139+
plt.tight_layout() # pragma: no cover - display-only
140+
plt.show() # pragma: no cover - display-only
139141

140142
# ---------- helpers ----------
141143

ngraph/workflow/analysis/capacity_matrix.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ def display_analysis(self, analysis: Dict[str, Any], **kwargs) -> None:
118118

119119
# Heatmap
120120
# Scale figure size with matrix dimensions; bias toward readability
121-
plt.figure(
121+
plt.figure( # pragma: no cover - display-only
122122
figsize=(
123123
min(16, 2 + 0.35 * max(3, matrix.shape[1])),
124124
min(12, 2 + 0.35 * max(3, matrix.shape[0])),
125125
)
126126
)
127-
sns.heatmap(
127+
sns.heatmap( # pragma: no cover - display-only
128128
matrix.replace(0.0, np.nan),
129129
annot=False,
130130
fmt=".0f",
@@ -135,8 +135,8 @@ def display_analysis(self, analysis: Dict[str, Any], **kwargs) -> None:
135135
plt.title(f"Node→Node Capacity (Max over iterations) — {step}")
136136
plt.xlabel("Destination")
137137
plt.ylabel("Source")
138-
plt.tight_layout()
139-
plt.show()
138+
plt.tight_layout() # pragma: no cover - display-only
139+
plt.show() # pragma: no cover - display-only
140140

141141
# ---------- helpers ----------
142142

ngraph/workflow/analysis/latency.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,16 @@ def display_analysis(self, analysis: dict[str, Any], **kwargs) -> None:
123123

124124
print(f"✅ Latency/Stretch for {name} — iterations={len(df)}")
125125

126-
fig, ax = plt.subplots(figsize=(9, 5.5))
127-
sns.scatterplot(data=df, x="mean_km_per_gbps", y="stretch", s=60)
126+
fig, ax = plt.subplots(figsize=(9, 5.5)) # pragma: no cover - display-only
127+
sns.scatterplot(
128+
data=df, x="mean_km_per_gbps", y="stretch", s=60
129+
) # pragma: no cover - display-only
128130
ax.set_xlabel("Mean distance per Gbps (km/Gbps)")
129131
ax.set_ylabel("Latency stretch (≈avg path cost / baseline LB)")
130132
ax.set_title(f"Distance & Stretch by Failure Iteration - {name}")
131133
ax.grid(True, linestyle=":", linewidth=0.5)
132-
plt.tight_layout()
133-
plt.show()
134+
plt.tight_layout() # pragma: no cover - display-only
135+
plt.show() # pragma: no cover - display-only
134136

135137
print(" Summary:")
136138
print(

ngraph/workflow/analysis/msd.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,31 @@ def display_analysis(self, analysis: dict[str, Any], **kwargs) -> None:
6060
print(" No probe trace available.")
6161
return
6262

63-
plt.figure(figsize=(8, 5))
63+
plt.figure(figsize=(8, 5)) # pragma: no cover - display-only
6464
sns.lineplot(
6565
data=trace,
6666
x="alpha",
6767
y="min_placement_ratio",
6868
marker="o",
6969
label="min placement ratio",
70-
)
70+
) # pragma: no cover - display-only
7171
sns.scatterplot(
7272
data=trace,
7373
x="alpha",
7474
y="min_placement_ratio",
7575
hue=trace["feasible"].map({True: "feasible", False: "infeasible"}),
7676
legend=True,
77-
)
78-
plt.axvline(alpha_star, linestyle="--", linewidth=1.0, label="alpha*")
79-
plt.xlabel("Alpha")
80-
plt.ylabel("Min placement ratio across pairs")
81-
plt.title(f"MSD bracketing/bisection trace — {name}")
82-
plt.grid(True, linestyle=":", linewidth=0.5)
83-
plt.tight_layout()
84-
plt.show()
77+
) # pragma: no cover - display-only
78+
plt.axvline(
79+
alpha_star, linestyle="--", linewidth=1.0, label="alpha*"
80+
) # pragma: no cover - display-only
81+
plt.xlabel("Alpha") # pragma: no cover - display-only
82+
plt.ylabel(
83+
"Min placement ratio across pairs"
84+
) # pragma: no cover - display-only
85+
plt.title(
86+
f"MSD bracketing/bisection trace — {name}"
87+
) # pragma: no cover - display-only
88+
plt.grid(True, linestyle=":", linewidth=0.5) # pragma: no cover - display-only
89+
plt.tight_layout() # pragma: no cover - display-only
90+
plt.show() # pragma: no cover - display-only

ngraph/workflow/analysis/placement_matrix.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def fmt(x: float) -> str:
180180
print("No placement data available")
181181
return
182182

183-
for prio in sorted(matrices.keys()):
183+
for prio in sorted(matrices.keys()): # pragma: no cover - display-only
184184
print(f"\nPriority {prio}")
185185
stats = stats_by_prio.get(prio, {"has_data": False})
186186
if not stats.get("has_data"):
@@ -195,7 +195,7 @@ def fmt(x: float) -> str:
195195
matrix_display = matrices[prio].copy()
196196
matrix_display.index.name = "Source"
197197
matrix_display.columns.name = "Destination"
198-
if not matrix_display.empty:
198+
if not matrix_display.empty: # pragma: no cover - display-only
199199
md = matrix_display.applymap(fmt)
200200
show(
201201
md,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from pathlib import Path
5+
6+
from ngraph import cli
7+
8+
9+
def test_cli_verbose_and_quiet_switch_levels(
10+
caplog, tmp_path: Path, monkeypatch
11+
) -> None:
12+
# Minimal scenario
13+
scenario = tmp_path / "t.yaml"
14+
scenario.write_text(
15+
"""
16+
network:
17+
nodes:
18+
A: {}
19+
workflow:
20+
- step_type: BuildGraph
21+
"""
22+
)
23+
24+
# run in temp directory to avoid polluting repo
25+
monkeypatch.chdir(tmp_path)
26+
27+
# verbose enables debug
28+
with caplog.at_level(logging.DEBUG, logger="ngraph"):
29+
cli.main(
30+
["--verbose", "run", str(scenario), "--no-results"]
31+
) # avoid writing results
32+
assert any("Debug logging enabled" in r.message for r in caplog.records)
33+
34+
# quiet suppresses info
35+
caplog.clear()
36+
with caplog.at_level(logging.INFO, logger="ngraph"):
37+
cli.main(
38+
["--quiet", "run", str(scenario), "--no-results"]
39+
) # avoid writing results
40+
assert not any(r.levelno == logging.INFO for r in caplog.records)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
5+
from ngraph.demand.manager.builder import (
6+
_coerce_flow_policy_config,
7+
build_traffic_matrix_set,
8+
)
9+
from ngraph.demand.matrix import TrafficMatrixSet
10+
from ngraph.flows.policy import FlowPolicyConfig
11+
12+
13+
def test_coerce_flow_policy_config_variants() -> None:
14+
assert _coerce_flow_policy_config(None) is None
15+
assert (
16+
_coerce_flow_policy_config(FlowPolicyConfig.SHORTEST_PATHS_ECMP)
17+
== FlowPolicyConfig.SHORTEST_PATHS_ECMP
18+
)
19+
assert (
20+
_coerce_flow_policy_config(int(FlowPolicyConfig.SHORTEST_PATHS_ECMP))
21+
== FlowPolicyConfig.SHORTEST_PATHS_ECMP
22+
)
23+
assert (
24+
_coerce_flow_policy_config(str(int(FlowPolicyConfig.SHORTEST_PATHS_ECMP)))
25+
== FlowPolicyConfig.SHORTEST_PATHS_ECMP
26+
)
27+
assert (
28+
_coerce_flow_policy_config("shortest_paths_ecmp")
29+
== FlowPolicyConfig.SHORTEST_PATHS_ECMP
30+
)
31+
with pytest.raises(ValueError):
32+
_coerce_flow_policy_config("not-an-enum")
33+
34+
35+
def test_build_traffic_matrix_set_happy_and_errors() -> None:
36+
raw = {
37+
"default": [
38+
{
39+
"source_path": "A",
40+
"sink_path": "B",
41+
"demand": 10.0,
42+
"priority": 0,
43+
"flow_policy_config": "shortest_paths_ecmp",
44+
}
45+
]
46+
}
47+
tms = build_traffic_matrix_set(raw)
48+
assert isinstance(tms, TrafficMatrixSet)
49+
m = tms.get_default_matrix()
50+
assert m[0].flow_policy_config == FlowPolicyConfig.SHORTEST_PATHS_ECMP
51+
52+
with pytest.raises(ValueError):
53+
build_traffic_matrix_set([1, 2, 3]) # type: ignore[arg-type]
54+
with pytest.raises(ValueError):
55+
build_traffic_matrix_set({"x": 1}) # type: ignore[arg-type]
56+
with pytest.raises(ValueError):
57+
build_traffic_matrix_set({"x": [1]}) # type: ignore[arg-type]

0 commit comments

Comments
 (0)