import sys import unittest from pathlib import Path from types import SimpleNamespace import numpy as np PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT)) import config from helpers import filter_drone_candidates, verified_drone_track_box class LowConfidenceTrackingProfileTests(unittest.TestCase): def test_best_pt_profile_requires_temporal_confirmation(self): self.assertGreaterEqual(config.YOLO_CONF_EFFECTIVE, 0.03) self.assertLessEqual(config.BT_LOW, config.YOLO_CONF_EFFECTIVE) self.assertGreaterEqual(config.BT_NEW, 0.05) self.assertGreaterEqual(config.TRACK_SCORE_MIN_ACQUIRE, config.BT_NEW) self.assertEqual(config.CONFIRM_HITS, 3) self.assertEqual(config.UNVERIFIED_TARGET_SWITCH_CONFIRM_HITS, 3) self.assertGreaterEqual(config.TARGET_SWITCH_CONFIRM_HITS, 8) self.assertFalse(config.FAST_HANDOFF_ENABLE) def test_weak_candidate_needs_motion(self): weak = np.array([10, 10, 20, 20, 0.05], dtype=np.float32) still = np.zeros((100, 100), dtype=np.uint8) moving = still.copy() moving[12:16, 12:16] = 255 self.assertEqual(filter_drone_candidates([weak], still, 100, 100), []) self.assertEqual(len(filter_drone_candidates([weak], moving, 100, 100)), 1) def test_strong_candidate_does_not_require_motion(self): strong = np.array([10, 10, 20, 20, 0.13], dtype=np.float32) self.assertEqual( len(filter_drone_candidates([strong], None, 100, 100)), 1, ) def test_red_box_requires_fresh_verified_track(self): track = SimpleNamespace( tlbr=np.array([10, 10, 20, 20], dtype=np.float32), score=0.11, hits=1, time_since_update=0, ) self.assertIsNotNone(verified_drone_track_box(track, None, 100, 100)) track.time_since_update = 1 self.assertIsNone(verified_drone_track_box(track, None, 100, 100)) track.time_since_update = 0 track.hits = 0 self.assertIsNone(verified_drone_track_box(track, None, 100, 100)) if __name__ == "__main__": unittest.main()