70 lines
4.0 KiB
Python
70 lines
4.0 KiB
Python
import importlib.util
|
|
from pathlib import Path
|
|
import unittest
|
|
|
|
SPEC = importlib.util.spec_from_file_location("ble_visibility_probe", Path(__file__).resolve().parents[1] / "tools/hil/ble_visibility_probe.py")
|
|
probe = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(probe)
|
|
TARGET = "D2:90:05:46:31:01"
|
|
|
|
|
|
class VisibilityTests(unittest.TestCase):
|
|
def test_info_not_available_is_not_a_cached_device(self):
|
|
for code in (0, 1):
|
|
self.assertFalse(probe.is_cached_device(f"Device {TARGET} not available\n", TARGET, code))
|
|
self.assertFalse(probe.is_cached_device(f"Device {TARGET}\n", TARGET, 1))
|
|
self.assertFalse(probe.is_cached_device(f"Name: Device {TARGET}\n", TARGET, 0))
|
|
|
|
def test_successful_info_requires_exact_header(self):
|
|
for header in (f"Device {TARGET}", f"Device {TARGET} (random)", f"Device {TARGET} (public)"):
|
|
self.assertTrue(probe.is_cached_device(f"\x1b[0;92m{header}\x1b[0m\n\tName: test\n", TARGET, 0))
|
|
|
|
def test_cache_query_errors_are_not_verified_absence(self):
|
|
self.assertIsNone(probe.device_cache_state("org.bluez failed", TARGET, 1))
|
|
self.assertIsNone(probe.device_cache_state(f"Device {TARGET}\n", TARGET, 1))
|
|
self.assertIsNone(probe.device_cache_state(f"Device {TARGET}\nDevice {TARGET} not available\n", TARGET, 0))
|
|
self.assertIs(probe.device_cache_state(f"Device {TARGET} not available\n", TARGET, 1), False)
|
|
|
|
def test_new_cache_record_is_evidence_without_fabricated_events_or_rssi(self):
|
|
scan = probe.parse_discovery(f"[NEW] Device {TARGET} name\nDiscovery started\n", TARGET)
|
|
result = probe.with_cache_evidence(scan, False, True)
|
|
self.assertTrue(result["advertisement_evidence"])
|
|
self.assertTrue(result["new_target_record_during_scan"])
|
|
self.assertFalse(result["post_start_event_evidence"])
|
|
self.assertEqual(result["target_events"], 0)
|
|
self.assertEqual(result["rssi_dbm"], [])
|
|
self.assertEqual(result["evidence_sources"], ["absent_before_present_after_successful_discovery"])
|
|
|
|
def test_existing_or_unknown_cache_and_failed_discovery_not_new_evidence(self):
|
|
scan = probe.parse_discovery("Discovery started\n", TARGET)
|
|
for before, after in ((True, True), (None, True), (False, None), (False, False)):
|
|
result = probe.with_cache_evidence(scan, before, after)
|
|
self.assertFalse(result["advertisement_evidence"])
|
|
failed = probe.parse_discovery("Failed to start discovery\n", TARGET)
|
|
self.assertFalse(probe.with_cache_evidence(failed, False, True)["advertisement_evidence"])
|
|
|
|
def test_fresh_rssi_remains_separate_from_cache_transition(self):
|
|
scan = probe.parse_discovery(f"Discovery started\n[CHG] Device {TARGET} RSSI: -40\n", TARGET)
|
|
result = probe.with_cache_evidence(scan, True, True)
|
|
self.assertTrue(result["post_start_event_evidence"])
|
|
self.assertFalse(result["new_target_record_during_scan"])
|
|
self.assertEqual(result["evidence_sources"], ["post_start_rssi_update"])
|
|
|
|
def test_cached_enumeration_is_not_actual_discovery(self):
|
|
result = probe.parse_discovery(f"[NEW] Device {TARGET} cached\nDiscovery started\n", TARGET)
|
|
self.assertFalse(result["advertisement_evidence"])
|
|
self.assertEqual(result["target_events"], 0)
|
|
|
|
def test_filters_other_devices_and_parses_target_rssi(self):
|
|
result = probe.parse_discovery(f"Discovery started\n[CHG] Device AA:00:00:00:00:01 RSSI: -12\n\x1b[0;92m[CHG]\x1b[0m Device {TARGET} RSSI: -54\n[CHG] Device {TARGET} RSSI: 0xffffffcb (-53)\n", TARGET)
|
|
self.assertTrue(result["advertisement_evidence"])
|
|
self.assertEqual(result["rssi_dbm"], [-54, -53])
|
|
self.assertEqual(result["target_events"], 2)
|
|
|
|
def test_scan_start_failure_does_not_use_stale_events(self):
|
|
self.assertFalse(probe.parse_discovery(f"Failed to start discovery\n[NEW] Device {TARGET} cached\n", TARGET)["advertisement_evidence"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|