Skip to content

Commit 49d57ff

Browse files
committed
python(feat): Add more measurement functions for test result utils.
1 parent da23f2d commit 49d57ff

2 files changed

Lines changed: 340 additions & 4 deletions

File tree

python/lib/sift_client/_tests/util/test_test_results_utils.py

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from datetime import datetime, timezone
22

3+
import numpy as np
4+
import pandas as pd
35
import pytest
46

57
from sift_client.sift_types.test_report import (
8+
NumericBounds,
69
TestMeasurementCreate,
710
TestMeasurementType,
811
TestMeasurementUpdate,
@@ -152,6 +155,243 @@ def test_measurement_update(self, report_context):
152155
assert test_step.measurements[2].boolean_value == True
153156
assert test_step.measurements[2].measurement_type == TestMeasurementType.BOOLEAN
154157

158+
def test_measure_avg_list_within_bounds(self, report_context):
159+
"""Test measure_avg with a list of values where average is within bounds."""
160+
with report_context.new_step(
161+
"Test Measure Avg List", "Test measure_avg with list"
162+
) as new_step:
163+
result = new_step.measure_avg(
164+
name="Avg Temperature",
165+
values=[10.0, 20.0, 30.0], # avg = 20.0
166+
bounds={"min": 15.0, "max": 25.0},
167+
)
168+
assert result == True
169+
test_step = new_step.current_step
170+
assert len(test_step.measurements) == 1
171+
assert test_step.measurements[0].name == "Avg Temperature"
172+
assert test_step.measurements[0].numeric_value == 20.0
173+
assert test_step.measurements[0].passed == True
174+
175+
def test_measure_avg_list_outside_bounds(self, report_context, step):
176+
"""Test measure_avg with a list where average is outside bounds."""
177+
# Capture initial state to restore after test
178+
current_step_path = step.current_step.step_path
179+
initial_open_step_result = report_context.open_step_results.get(current_step_path, True)
180+
initial_any_failures = report_context.any_failures
181+
182+
with report_context.new_step(
183+
"Test Measure Avg Outside", "Test measure_avg outside bounds"
184+
) as new_step:
185+
result = new_step.measure_avg(
186+
name="Avg Temperature Fail",
187+
values=[50.0, 60.0, 70.0], # avg = 60.0
188+
bounds={"min": 15.0, "max": 25.0},
189+
)
190+
assert result == False
191+
test_step = new_step.current_step
192+
assert len(test_step.measurements) == 1
193+
assert test_step.measurements[0].numeric_value == 60.0
194+
assert test_step.measurements[0].passed == False
195+
196+
# Restore state
197+
if initial_open_step_result:
198+
report_context.open_step_results[current_step_path] = True
199+
if not initial_any_failures:
200+
report_context.any_failures = False
201+
202+
def test_measure_avg_numpy_array(self, report_context):
203+
"""Test measure_avg with a numpy array."""
204+
with report_context.new_step(
205+
"Test Measure Avg Numpy", "Test measure_avg with numpy array"
206+
) as new_step:
207+
result = new_step.measure_avg(
208+
name="Avg Pressure",
209+
values=np.array([100.0, 200.0, 300.0]), # avg = 200.0
210+
bounds={"min": 150.0, "max": 250.0},
211+
)
212+
assert result == True
213+
test_step = new_step.current_step
214+
assert test_step.measurements[0].numeric_value == 200.0
215+
assert test_step.measurements[0].passed == True
216+
217+
def test_measure_avg_pandas_dataframe(self, report_context):
218+
"""Test measure_avg with a pandas DataFrame."""
219+
with report_context.new_step(
220+
"Test Measure Avg DataFrame", "Test measure_avg with DataFrame"
221+
) as new_step:
222+
df = pd.DataFrame({"values": [5.0, 10.0, 15.0]}) # avg = 10.0
223+
result = new_step.measure_avg(
224+
name="Avg Voltage",
225+
values=df,
226+
bounds={"min": 5.0, "max": 15.0},
227+
)
228+
assert result == True
229+
test_step = new_step.current_step
230+
assert test_step.measurements[0].numeric_value == 10.0
231+
assert test_step.measurements[0].passed == True
232+
233+
def test_measure_avg_with_numeric_bounds_object(self, report_context):
234+
"""Test measure_avg with NumericBounds object instead of dict."""
235+
with report_context.new_step(
236+
"Test Measure Avg NumericBounds", "Test measure_avg with NumericBounds"
237+
) as new_step:
238+
result = new_step.measure_avg(
239+
name="Avg Current",
240+
values=[1.0, 2.0, 3.0], # avg = 2.0
241+
bounds=NumericBounds(min=1.0, max=3.0),
242+
)
243+
assert result == True
244+
test_step = new_step.current_step
245+
assert test_step.measurements[0].numeric_value == 2.0
246+
assert test_step.measurements[0].passed == True
247+
248+
def test_measure_avg_invalid_type(self, report_context):
249+
"""Test measure_avg raises ValueError for invalid value type."""
250+
with report_context.new_step(
251+
"Test Measure Avg Invalid", "Test measure_avg with invalid type"
252+
) as new_step:
253+
with pytest.raises(ValueError, match="Invalid value type"):
254+
new_step.measure_avg(
255+
name="Invalid",
256+
values="not a list", # type: ignore
257+
bounds={"min": 0.0, "max": 10.0},
258+
)
259+
260+
def test_measure_avg_with_integers(self, report_context):
261+
"""Test measure_avg with integer values in list."""
262+
with report_context.new_step(
263+
"Test Measure Avg Integers", "Test measure_avg with integers"
264+
) as new_step:
265+
result = new_step.measure_avg(
266+
name="Avg Count",
267+
values=[1, 2, 3, 4, 5], # avg = 3.0
268+
bounds={"min": 2.0, "max": 4.0},
269+
)
270+
assert result == True
271+
test_step = new_step.current_step
272+
assert test_step.measurements[0].numeric_value == 3.0
273+
assert test_step.measurements[0].passed == True
274+
275+
def test_measure_all_list_within_bounds(self, report_context):
276+
"""Test measure_all with a list of values all within bounds."""
277+
with report_context.new_step(
278+
"Test Measure All List", "Test measure_all with list"
279+
) as new_step:
280+
result = new_step.measure_all(
281+
name="All Temperatures",
282+
values=[10.0, 15.0, 20.0],
283+
bounds={"min": 5.0, "max": 25.0},
284+
)
285+
assert result == True
286+
287+
def test_measure_all_list_some_outside_bounds(self, report_context, step):
288+
"""Test measure_all with a list where some values are outside bounds."""
289+
# Capture initial state to restore after test
290+
current_step_path = step.current_step.step_path
291+
initial_open_step_result = report_context.open_step_results.get(current_step_path, True)
292+
initial_any_failures = report_context.any_failures
293+
294+
with report_context.new_step(
295+
"Test Measure All Outside", "Test measure_all with values outside bounds"
296+
) as new_step:
297+
result = new_step.measure_all(
298+
name="All Temperatures Fail",
299+
values=[10.0, 50.0, 20.0, 60.0], # 50.0 and 60.0 are outside
300+
bounds={"min": 5.0, "max": 25.0},
301+
)
302+
assert result == False
303+
304+
# Restore state
305+
if initial_open_step_result:
306+
report_context.open_step_results[current_step_path] = True
307+
if not initial_any_failures:
308+
report_context.any_failures = False
309+
310+
def test_measure_all_numpy_array(self, report_context):
311+
"""Test measure_all with a numpy array."""
312+
with report_context.new_step(
313+
"Test Measure All Numpy", "Test measure_all with numpy array"
314+
) as new_step:
315+
result = new_step.measure_all(
316+
name="All Pressures",
317+
values=np.array([100.0, 150.0, 200.0]),
318+
bounds={"min": 50.0, "max": 250.0},
319+
)
320+
assert result == True
321+
322+
def test_measure_all_pandas_dataframe(self, report_context):
323+
"""Test measure_all with a pandas DataFrame."""
324+
with report_context.new_step(
325+
"Test Measure All DataFrame", "Test measure_all with DataFrame"
326+
) as new_step:
327+
df = pd.DataFrame({"values": [5.0, 10.0, 15.0]})
328+
result = new_step.measure_all(
329+
name="All Voltages",
330+
values=df,
331+
bounds={"min": 0.0, "max": 20.0},
332+
)
333+
assert result == True
334+
335+
def test_measure_all_with_numeric_bounds_object(self, report_context):
336+
"""Test measure_all with NumericBounds object instead of dict."""
337+
with report_context.new_step(
338+
"Test Measure All NumericBounds", "Test measure_all with NumericBounds"
339+
) as new_step:
340+
result = new_step.measure_all(
341+
name="All Currents",
342+
values=[1.0, 2.0, 3.0],
343+
bounds=NumericBounds(min=0.0, max=5.0),
344+
)
345+
assert result == True
346+
347+
def test_measure_all_invalid_type(self, report_context):
348+
"""Test measure_all raises ValueError for invalid value type."""
349+
with report_context.new_step(
350+
"Test Measure All Invalid", "Test measure_all with invalid type"
351+
) as new_step:
352+
with pytest.raises(ValueError, match="Invalid value type"):
353+
new_step.measure_all(
354+
name="Invalid",
355+
values="not a list", # type: ignore
356+
bounds={"min": 0.0, "max": 10.0},
357+
)
358+
359+
def test_measure_all_no_bounds(self, report_context):
360+
"""Test measure_all raises ValueError when no bounds provided."""
361+
with report_context.new_step(
362+
"Test Measure All No Bounds", "Test measure_all with no bounds"
363+
) as new_step:
364+
with pytest.raises(ValueError, match="No bounds provided"):
365+
new_step.measure_all(
366+
name="No Bounds",
367+
values=[1.0, 2.0, 3.0],
368+
bounds={}, # Empty bounds dict
369+
)
370+
371+
def test_measure_all_min_only(self, report_context):
372+
"""Test measure_all with only minimum bound."""
373+
with report_context.new_step(
374+
"Test Measure All Min Only", "Test measure_all with min only"
375+
) as new_step:
376+
result = new_step.measure_all(
377+
name="Min Only",
378+
values=[10.0, 20.0, 30.0],
379+
bounds={"min": 5.0},
380+
)
381+
assert result == True
382+
383+
def test_measure_all_max_only(self, report_context):
384+
"""Test measure_all with only maximum bound."""
385+
with report_context.new_step(
386+
"Test Measure All Max Only", "Test measure_all with max only"
387+
) as new_step:
388+
result = new_step.measure_all(
389+
name="Max Only",
390+
values=[10.0, 20.0, 30.0],
391+
bounds={"max": 50.0},
392+
)
393+
assert result == True
394+
155395
def test_report_outcome(self, report_context, step):
156396
# Capture current state of report context's failures so we can keep things passed at a high level if the test's induced failures happen as expected.
157397
current_step_path = step.current_step.step_path

0 commit comments

Comments
 (0)