diff --git a/scripts/verify-frame-smoke-result.py b/scripts/verify-frame-smoke-result.py index 83c8c80..0d13c38 100644 --- a/scripts/verify-frame-smoke-result.py +++ b/scripts/verify-frame-smoke-result.py @@ -2,6 +2,7 @@ """Check a locked Frame smoke result against the exact release commit.""" import json +import math import re import sys from pathlib import Path @@ -40,7 +41,8 @@ def verify(result, commit): state = result.get('preflight') or {} if (not isinstance(state, dict) or state.get('activity') != 3 or not isinstance(state.get('proximity'), list) or not state['proximity'] or - any(type(value) not in (int, float) or value < 0 or value > 10 + any(type(value) not in (int, float) or not math.isfinite(value) or + value < 0 or value > 10 for value in state['proximity']) or not isinstance(state.get('battery'), list) or not state['battery'] or any(type(value) is not int or value < 20 or value > 100 diff --git a/tests/smoke/frame_smoke.py b/tests/smoke/frame_smoke.py index 4ba78e9..0b8a388 100644 --- a/tests/smoke/frame_smoke.py +++ b/tests/smoke/frame_smoke.py @@ -33,6 +33,7 @@ headset isn't reachable. A skipped launch is not a release smoke pass. """ import argparse import json +import math import os import platform import re @@ -126,10 +127,13 @@ def preflight(): proximity = state.get('proximity') if (not isinstance(proximity, list) or not proximity or any(not isinstance(value, (int, float)) or isinstance(value, bool) or - value < 0 or value > 10 for value in proximity)): + not math.isfinite(value) or value < 0 or value > 10 + for value in proximity)): raise RuntimeError('Frame proximity is active or unknown; active session untouched') battery = state.get('battery') - if not battery or min(battery) < 20: + if (not isinstance(battery, list) or not battery or + any(type(value) is not int or value < 20 or value > 100 + for value in battery)): raise RuntimeError('Frame battery is unavailable or below the smoke threshold') return state diff --git a/tests/test_smoke_cleanup.py b/tests/test_smoke_cleanup.py index c60a726..6a0337e 100644 --- a/tests/test_smoke_cleanup.py +++ b/tests/test_smoke_cleanup.py @@ -108,7 +108,8 @@ class Safety(unittest.TestCase): self.assertEqual(frame_smoke.source_revision(), {'commit': None, 'clean': False}) def test_locked_preflight_rejects_active_or_unknown_proximity_before_titles(self): - for proximity in ([], [88.0625], [4.6875, 88.0625], [None]): + for proximity in ([], [88.0625], [4.6875, 88.0625], [None], + [float('nan')], [float('inf')], [float('-inf')]): with self.subTest(proximity=proximity), mock.patch.object( frame_smoke, 'remote', return_value=mock.Mock( returncode=0, stdout=json.dumps({ @@ -261,7 +262,10 @@ class SharedSmokeLock(unittest.TestCase): for state in ({'activity':1,'proximity':[1],'battery':[100]}, {'activity':None,'proximity':[],'battery':[100]}, {'activity':3,'proximity':[],'battery':[19]}, - {'activity':3,'proximity':[],'battery':[]}): + {'activity':3,'proximity':[],'battery':[]}, + {'activity':3,'proximity':[0],'battery':[True]}, + {'activity':3,'proximity':[0],'battery':[float('nan')]}, + {'activity':3,'proximity':[0],'battery':[101]}): with self.subTest(state=state), mock.patch.object(frame_smoke, 'remote', return_value=mock.Mock(returncode=0, stdout=json.dumps(state))): with self.assertRaises(RuntimeError): diff --git a/tests/test_verify_frame_smoke_result.py b/tests/test_verify_frame_smoke_result.py index ef8a1c9..4c9e3be 100644 --- a/tests/test_verify_frame_smoke_result.py +++ b/tests/test_verify_frame_smoke_result.py @@ -1,6 +1,7 @@ """The release smoke gate requires a complete result from its exact checkout.""" import importlib.util +import math import unittest from pathlib import Path @@ -51,6 +52,20 @@ class VerifyFrameSmokeResult(unittest.TestCase): with self.assertRaisesRegex(ValueError, 'preflight'): gate.verify(data, SHA) + def test_rejects_nonfinite_proximity_and_invalid_battery(self): + for proximity in (math.nan, math.inf, -math.inf): + with self.subTest(proximity=proximity): + data = result() + data['preflight']['proximity'] = [proximity] + with self.assertRaisesRegex(ValueError, 'preflight'): + gate.verify(data, SHA) + for battery in ([True], [math.nan], [101], [19], ['99']): + with self.subTest(battery=battery): + data = result() + data['preflight']['battery'] = battery + with self.assertRaisesRegex(ValueError, 'preflight'): + gate.verify(data, SHA) + def test_rejects_missing_lock_release_record(self): data = result() data['lock_released'] = False