""" Unit tests for dreader_hal.gesture module. Tests gesture detection state machine and classification logic. """ import pytest import time from dreader_hal.gesture import GestureDetector, TouchState from dreader_hal.types import GestureType class TestGestureDetector: """Tests for GestureDetector class.""" def setup_method(self): """Set up fresh detector for each test.""" self.detector = GestureDetector() def test_initial_state(self): """Test detector initial state.""" assert self.detector.state == TouchState.IDLE assert self.detector.start_pos is None assert self.detector.current_pos is None def test_tap_detection(self): """Test tap gesture detection.""" # Touch down self.detector.on_touch_down(100, 100) assert self.detector.state == TouchState.TOUCHING # Small delay (less than long press) time.sleep(0.1) # Touch up at same position gesture = self.detector.on_touch_up(105, 105) # 5px movement assert gesture == GestureType.TAP assert self.detector.state == TouchState.IDLE def test_long_press_detection(self): """Test long press gesture detection.""" # Touch down self.detector.on_touch_down(100, 100) # Wait for long press duration time.sleep(0.6) # Longer than 0.5s threshold # Touch up at same position gesture = self.detector.on_touch_up(105, 105) assert gesture == GestureType.LONG_PRESS assert self.detector.state == TouchState.IDLE def test_swipe_left_detection(self): """Test swipe left gesture detection.""" # Touch down self.detector.on_touch_down(200, 100) # Quick movement to the left time.sleep(0.1) gesture = self.detector.on_touch_up(100, 105) # 100px left, 5px down assert gesture == GestureType.SWIPE_LEFT def test_swipe_right_detection(self): """Test swipe right gesture detection.""" self.detector.on_touch_down(100, 100) time.sleep(0.1) gesture = self.detector.on_touch_up(200, 105) # 100px right assert gesture == GestureType.SWIPE_RIGHT def test_swipe_up_detection(self): """Test swipe up gesture detection.""" self.detector.on_touch_down(100, 200) time.sleep(0.1) gesture = self.detector.on_touch_up(105, 100) # 100px up assert gesture == GestureType.SWIPE_UP def test_swipe_down_detection(self): """Test swipe down gesture detection.""" self.detector.on_touch_down(100, 100) time.sleep(0.1) gesture = self.detector.on_touch_up(105, 200) # 100px down assert gesture == GestureType.SWIPE_DOWN def test_drag_start_detection(self): """Test drag start detection.""" # Touch down self.detector.on_touch_down(100, 100) # Move beyond threshold gesture = self.detector.on_touch_move(150, 100) # 50px movement assert gesture == GestureType.DRAG_START assert self.detector.state == TouchState.MOVING def test_drag_move_detection(self): """Test drag move events.""" # Start dragging self.detector.on_touch_down(100, 100) self.detector.on_touch_move(150, 100) # Start drag # Continue moving gesture = self.detector.on_touch_move(200, 100) assert gesture == GestureType.DRAG_MOVE assert self.detector.state == TouchState.MOVING def test_drag_end_detection(self): """Test drag end.""" # Start dragging self.detector.on_touch_down(100, 100) self.detector.on_touch_move(150, 100) # Touch up time.sleep(0.6) # Wait long enough to not be a swipe gesture = self.detector.on_touch_up(200, 100) assert gesture == GestureType.DRAG_END def test_reset(self): """Test detector reset.""" # Set some state self.detector.on_touch_down(100, 100) assert self.detector.state == TouchState.TOUCHING # Reset self.detector.reset() # Check clean state assert self.detector.state == TouchState.IDLE assert self.detector.start_pos is None assert self.detector.current_pos is None def test_custom_thresholds(self): """Test creating detector with custom thresholds.""" detector = GestureDetector( tap_threshold=50.0, long_press_duration=1.0 ) assert detector.tap_threshold == 50.0 assert detector.long_press_duration == 1.0 def test_pinch_detection(self): """Test two-finger pinch detection.""" # First finger down self.detector.on_touch_down(100, 100, finger=0) # Second finger down self.detector.on_touch_down(200, 100, finger=1) # Move fingers apart (pinch out) self.detector.on_touch_move(90, 100, finger=0) self.detector.on_touch_move(210, 100, finger=1) # Second finger up gesture = self.detector.on_touch_up(210, 100, finger=1) assert gesture == GestureType.PINCH_OUT def test_check_long_press(self): """Test long press check during touch.""" # Touch down self.detector.on_touch_down(100, 100) # Check before threshold time.sleep(0.2) assert not self.detector.check_long_press() # Check after threshold time.sleep(0.4) # Total 0.6s assert self.detector.check_long_press() assert self.detector.state == TouchState.LONG_PRESS_DETECTED def test_no_gesture_on_small_movement(self): """Test that small movements during long delay don't trigger swipe.""" self.detector.on_touch_down(100, 100) time.sleep(0.6) # Wait long # Small movement gesture = self.detector.on_touch_up(110, 110) # Only 14px diagonal # Should be long press, not swipe assert gesture == GestureType.LONG_PRESS class TestTouchState: """Tests for TouchState enum.""" def test_states_defined(self): """Test all required states are defined.""" assert TouchState.IDLE assert TouchState.TOUCHING assert TouchState.MOVING assert TouchState.LONG_PRESS_DETECTED def test_state_values(self): """Test state enum values.""" assert TouchState.IDLE.value == "idle" assert TouchState.TOUCHING.value == "touching" assert TouchState.MOVING.value == "moving" assert TouchState.LONG_PRESS_DETECTED.value == "long_press_detected"