@@ -23,6 +23,31 @@ def mock_scenario():
2323 return scenario
2424
2525
26+ @pytest .fixture
27+ def mock_scenario_with_disabled ():
28+ """Scenario with disabled nodes and links for testing include_disabled parameter."""
29+ scenario = MagicMock ()
30+ scenario .network = Network ()
31+ scenario .results = MagicMock ()
32+ scenario .results .put = MagicMock ()
33+
34+ # Add nodes - some enabled, some disabled
35+ scenario .network .add_node (Node ("A" )) # enabled
36+ scenario .network .add_node (Node ("B" )) # enabled
37+ scenario .network .add_node (Node ("C" , disabled = True )) # disabled
38+ scenario .network .add_node (Node ("D" )) # enabled
39+
40+ # Add links - some enabled, some disabled
41+ scenario .network .add_link (Link ("A" , "B" , capacity = 10 )) # enabled
42+ scenario .network .add_link (Link ("A" , "C" , capacity = 5 )) # enabled (to disabled node)
43+ scenario .network .add_link (
44+ Link ("C" , "A" , capacity = 7 )
45+ ) # enabled (from disabled node)
46+ scenario .network .add_link (Link ("B" , "D" , capacity = 15 , disabled = True )) # disabled
47+ scenario .network .add_link (Link ("D" , "B" , capacity = 20 )) # enabled
48+ return scenario
49+
50+
2651def test_network_stats_collects_statistics (mock_scenario ):
2752 step = NetworkStats (name = "stats" )
2853
@@ -50,3 +75,112 @@ def test_network_stats_collects_statistics(mock_scenario):
5075 if call .args [1 ] == "per_node"
5176 )
5277 assert set (per_node .keys ()) == {"A" , "B" , "C" }
78+
79+
80+ def test_network_stats_excludes_disabled_by_default (mock_scenario_with_disabled ):
81+ """Test that disabled nodes and links are excluded by default."""
82+ step = NetworkStats (name = "stats" )
83+
84+ step .run (mock_scenario_with_disabled )
85+
86+ # Get the collected data
87+ calls = {
88+ call .args [1 ]: call .args [2 ]
89+ for call in mock_scenario_with_disabled .results .put .call_args_list
90+ }
91+
92+ # Link capacity should exclude disabled link (capacity=15)
93+ link_data = calls ["link_capacity" ]
94+ # Should include capacities: 10, 5, 7, 20 (excluding disabled link with capacity=15)
95+ assert sorted (link_data ["values" ]) == [5 , 7 , 10 , 20 ]
96+ assert link_data ["min" ] == 5
97+ assert link_data ["max" ] == 20
98+ assert link_data ["mean" ] == pytest .approx ((5 + 7 + 10 + 20 ) / 4 )
99+
100+ # Per-node stats should exclude disabled node C
101+ per_node = calls ["per_node" ]
102+ # Should only include enabled nodes: A, B, D (excluding disabled node C)
103+ assert set (per_node .keys ()) == {"A" , "B" , "D" }
104+
105+ # Node A should have degree 2 (links to B and C, both enabled)
106+ assert per_node ["A" ]["degree" ] == 2
107+ assert per_node ["A" ]["capacity_sum" ] == 15 # 10 + 5
108+
109+ # Node B should have degree 0 (link to D is disabled)
110+ assert per_node ["B" ]["degree" ] == 0
111+ assert per_node ["B" ]["capacity_sum" ] == 0
112+
113+ # Node D should have degree 1 (link to B is enabled)
114+ assert per_node ["D" ]["degree" ] == 1
115+ assert per_node ["D" ]["capacity_sum" ] == 20
116+
117+
118+ def test_network_stats_includes_disabled_when_enabled (mock_scenario_with_disabled ):
119+ """Test that disabled nodes and links are included when include_disabled=True."""
120+ step = NetworkStats (name = "stats" , include_disabled = True )
121+
122+ step .run (mock_scenario_with_disabled )
123+
124+ # Get the collected data
125+ calls = {
126+ call .args [1 ]: call .args [2 ]
127+ for call in mock_scenario_with_disabled .results .put .call_args_list
128+ }
129+
130+ # Link capacity should include all links including disabled one
131+ link_data = calls ["link_capacity" ]
132+ # Should include all capacities: 10, 5, 7, 15, 20
133+ assert sorted (link_data ["values" ]) == [5 , 7 , 10 , 15 , 20 ]
134+ assert link_data ["min" ] == 5
135+ assert link_data ["max" ] == 20
136+ assert link_data ["mean" ] == pytest .approx ((5 + 7 + 10 + 15 + 20 ) / 5 )
137+
138+ # Per-node stats should include disabled node C
139+ per_node = calls ["per_node" ]
140+ # Should include all nodes: A, B, C, D
141+ assert set (per_node .keys ()) == {"A" , "B" , "C" , "D" }
142+
143+ # Node A should have degree 2 (links to B and C)
144+ assert per_node ["A" ]["degree" ] == 2
145+ assert per_node ["A" ]["capacity_sum" ] == 15 # 10 + 5
146+
147+ # Node B should have degree 1 (link to D, now included)
148+ assert per_node ["B" ]["degree" ] == 1
149+ assert per_node ["B" ]["capacity_sum" ] == 15 # disabled link now included
150+
151+ # Node C should have degree 1 (link to A)
152+ assert per_node ["C" ]["degree" ] == 1
153+ assert per_node ["C" ]["capacity_sum" ] == 7
154+
155+ # Node D should have degree 1 (link to B)
156+ assert per_node ["D" ]["degree" ] == 1
157+ assert per_node ["D" ]["capacity_sum" ] == 20
158+
159+
160+ def test_network_stats_parameter_backward_compatibility (mock_scenario ):
161+ """Test that the new parameter maintains backward compatibility."""
162+ # Test with explicit default
163+ step_explicit = NetworkStats (name = "stats" , include_disabled = False )
164+ step_explicit .run (mock_scenario )
165+
166+ # Capture results from explicit test
167+ explicit_calls = {
168+ call .args [1 ]: call .args [2 ] for call in mock_scenario .results .put .call_args_list
169+ }
170+
171+ # Reset mock for second test
172+ mock_scenario .results .put .reset_mock ()
173+
174+ # Test with implicit default
175+ step_implicit = NetworkStats (name = "stats" )
176+ step_implicit .run (mock_scenario )
177+
178+ # Capture results from implicit test
179+ implicit_calls = {
180+ call .args [1 ]: call .args [2 ] for call in mock_scenario .results .put .call_args_list
181+ }
182+
183+ # Results should be identical
184+ assert explicit_calls .keys () == implicit_calls .keys ()
185+ for key in explicit_calls :
186+ assert explicit_calls [key ] == implicit_calls [key ]
0 commit comments