From b8e060b8505d8743e43e1614f6dee1f59f591f68 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Thu, 26 Mar 2026 15:43:27 +0100 Subject: [PATCH] Fix fprintd AlreadyInUse error by respecting done flag VerifyStart was called unconditionally on retry/no-match statuses, ignoring the done parameter from fprintd's VerifyStatus signal. When done=False the verify session is still active, causing AlreadyInUse errors. Now only restarts verification when done=True. --- src/moonlock/fingerprint.py | 8 +++++--- tests/test_fingerprint.py | 39 +++++++++++++++++++++++++++++++++---- tests/test_integration.py | 21 +++++++++++++++++--- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/moonlock/fingerprint.py b/src/moonlock/fingerprint.py index 5d4ea3e..cf081ee 100644 --- a/src/moonlock/fingerprint.py +++ b/src/moonlock/fingerprint.py @@ -143,13 +143,15 @@ class FingerprintListener: return if status in _RETRY_STATUSES: - # Retry silently — finger wasn't read properly - self._device_proxy.VerifyStart("(s)", "any") + # Retry — finger wasn't read properly + if done: + self._device_proxy.VerifyStart("(s)", "any") return if status == "verify-no-match": if self._on_failure: self._on_failure() # Restart verification for another attempt - self._device_proxy.VerifyStart("(s)", "any") + if done: + self._device_proxy.VerifyStart("(s)", "any") return diff --git a/tests/test_fingerprint.py b/tests/test_fingerprint.py index 9a76d0d..74c046c 100644 --- a/tests/test_fingerprint.py +++ b/tests/test_fingerprint.py @@ -90,7 +90,22 @@ class TestFingerprintSignalHandling: on_success.assert_called_once() - def test_verify_no_match_calls_on_failure_and_retries(self): + def test_verify_no_match_calls_on_failure_and_retries_when_done(self): + listener = FingerprintListener.__new__(FingerprintListener) + listener._device_proxy = MagicMock() + listener._running = True + on_success = MagicMock() + on_failure = MagicMock() + listener._on_success = on_success + listener._on_failure = on_failure + + listener._on_verify_status("verify-no-match", True) + + on_failure.assert_called_once() + # Should restart verification when done=True + listener._device_proxy.VerifyStart.assert_called_once_with("(s)", "any") + + def test_verify_no_match_calls_on_failure_without_restart_when_not_done(self): listener = FingerprintListener.__new__(FingerprintListener) listener._device_proxy = MagicMock() listener._running = True @@ -102,10 +117,10 @@ class TestFingerprintSignalHandling: listener._on_verify_status("verify-no-match", False) on_failure.assert_called_once() - # Should restart verification for retry - listener._device_proxy.VerifyStart.assert_called_once_with("(s)", "any") + # Should NOT restart verification when done=False (still in progress) + listener._device_proxy.VerifyStart.assert_not_called() - def test_verify_swipe_too_short_retries_without_failure(self): + def test_verify_swipe_too_short_retries_when_done(self): listener = FingerprintListener.__new__(FingerprintListener) listener._device_proxy = MagicMock() listener._running = True @@ -119,3 +134,19 @@ class TestFingerprintSignalHandling: on_success.assert_not_called() on_failure.assert_not_called() listener._device_proxy.VerifyStart.assert_called_once_with("(s)", "any") + + def test_retry_status_does_not_restart_when_not_done(self): + listener = FingerprintListener.__new__(FingerprintListener) + listener._device_proxy = MagicMock() + listener._running = True + on_success = MagicMock() + on_failure = MagicMock() + listener._on_success = on_success + listener._on_failure = on_failure + + listener._on_verify_status("verify-swipe-too-short", False) + + on_success.assert_not_called() + on_failure.assert_not_called() + # Should NOT restart — verification still in progress + listener._device_proxy.VerifyStart.assert_not_called() diff --git a/tests/test_integration.py b/tests/test_integration.py index 48d06c4..73f2bb3 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -74,8 +74,23 @@ class TestFingerprintAuthFlow: listener._on_verify_status("verify-match", False) assert len(unlock_called) == 1 - def test_fingerprint_no_match_retries(self): - """verify-no-match should retry verification.""" + def test_fingerprint_no_match_retries_when_done(self): + """verify-no-match with done=True should call on_failure and restart verification.""" + from moonlock.fingerprint import FingerprintListener + + listener = FingerprintListener.__new__(FingerprintListener) + listener._device_proxy = MagicMock() + listener._running = True + listener._on_success = MagicMock() + listener._on_failure = MagicMock() + + listener._on_verify_status("verify-no-match", True) + + listener._on_failure.assert_called_once() + listener._device_proxy.VerifyStart.assert_called_once() + + def test_fingerprint_no_match_no_restart_when_not_done(self): + """verify-no-match with done=False should call on_failure but not restart.""" from moonlock.fingerprint import FingerprintListener listener = FingerprintListener.__new__(FingerprintListener) @@ -87,7 +102,7 @@ class TestFingerprintAuthFlow: listener._on_verify_status("verify-no-match", False) listener._on_failure.assert_called_once() - listener._device_proxy.VerifyStart.assert_called_once() + listener._device_proxy.VerifyStart.assert_not_called() def test_fingerprint_and_password_independent(self): """Both auth methods should work independently."""