@@ -179,3 +179,92 @@ async def _release(self) -> None: # type: ignore[override]
179179 await lock .release ()
180180 assert acquired
181181 assert released
182+
183+
184+ @pytest .mark .parametrize ("lock_type" , [AsyncFileLock , AsyncSoftFileLock ])
185+ @pytest .mark .asyncio
186+ async def test_wait_message_logged (
187+ lock_type : type [BaseAsyncFileLock ], tmp_path : Path , caplog : pytest .LogCaptureFixture
188+ ) -> None :
189+ caplog .set_level (logging .DEBUG )
190+ lock_path = tmp_path / "a"
191+ first_lock = lock_type (str (lock_path ))
192+ second_lock = lock_type (str (lock_path ), timeout = 0.2 )
193+
194+ # Hold the lock so second_lock has to wait
195+ await first_lock .acquire ()
196+ with pytest .raises (Timeout ):
197+ await second_lock .acquire ()
198+ assert any ("waiting" in msg for msg in caplog .messages )
199+
200+
201+ @pytest .mark .parametrize ("lock_type" , [AsyncSoftFileLock , AsyncFileLock ])
202+ @pytest .mark .asyncio
203+ async def test_attempting_to_acquire_branch (
204+ lock_type : type [BaseAsyncFileLock ], tmp_path : Path , caplog : pytest .LogCaptureFixture
205+ ) -> None :
206+ caplog .set_level (logging .DEBUG )
207+
208+ lock = lock_type (str (tmp_path / "a" ))
209+ await lock .acquire ()
210+ assert any ("Attempting to acquire lock" in m for m in caplog .messages )
211+ await lock .release ()
212+
213+
214+ @pytest .mark .asyncio
215+ async def test_thread_local_run_in_executor (tmp_path : Path ) -> None : # noqa: RUF029
216+ with pytest .raises (ValueError , match = "run_in_executor is not supported when thread_local is True" ):
217+ AsyncSoftFileLock (str (tmp_path / "a" ), thread_local = True , run_in_executor = True )
218+
219+
220+ @pytest .mark .parametrize ("lock_type" , [AsyncSoftFileLock , AsyncFileLock ])
221+ @pytest .mark .asyncio
222+ async def test_attempting_to_acquire (
223+ lock_type : type [BaseAsyncFileLock ], tmp_path : Path , caplog : pytest .LogCaptureFixture
224+ ) -> None :
225+ caplog .set_level (logging .DEBUG )
226+ lock = lock_type (str (tmp_path / "a.lock" ), run_in_executor = False )
227+ await lock .acquire (timeout = 0.1 )
228+ assert any ("Attempting to acquire lock" in m for m in caplog .messages )
229+ await lock .release ()
230+
231+
232+ @pytest .mark .parametrize ("lock_type" , [AsyncSoftFileLock , AsyncFileLock ])
233+ @pytest .mark .asyncio
234+ async def test_attempting_to_release (
235+ lock_type : type [BaseAsyncFileLock ], tmp_path : Path , caplog : pytest .LogCaptureFixture
236+ ) -> None :
237+ caplog .set_level (logging .DEBUG )
238+ lock = lock_type (str (tmp_path / "a.lock" ), run_in_executor = False )
239+
240+ await lock .acquire (timeout = 0.1 ) # lock_counter = 1, is_locked = True
241+ await lock .acquire (timeout = 0.1 ) # lock_counter = 2 (reentrant)
242+ await lock .release (force = True )
243+
244+ assert any ("Attempting to release lock" in m for m in caplog .messages )
245+ assert any ("released" in m for m in caplog .messages )
246+
247+
248+ @pytest .mark .parametrize ("lock_type" , [AsyncFileLock , AsyncSoftFileLock ])
249+ @pytest .mark .asyncio
250+ async def test_release_early_exit_when_unlocked (lock_type : type [BaseAsyncFileLock ], tmp_path : Path ) -> None :
251+ lock = lock_type (str (tmp_path / "a.lock" ), run_in_executor = False )
252+ assert not lock .is_locked
253+ await lock .release ()
254+ assert not lock .is_locked
255+
256+
257+ @pytest .mark .parametrize ("lock_type" , [AsyncFileLock , AsyncSoftFileLock ])
258+ @pytest .mark .asyncio
259+ async def test_release_nonzero_counter_exit (
260+ lock_type : type [BaseAsyncFileLock ], tmp_path : Path , caplog : pytest .LogCaptureFixture
261+ ) -> None :
262+ caplog .set_level (logging .DEBUG )
263+ lock = lock_type (str (tmp_path / "a.lock" ), run_in_executor = False )
264+ await lock .acquire ()
265+ await lock .acquire ()
266+ await lock .release () # counter goes 2→1
267+ assert lock .lock_counter == 1
268+ assert lock .is_locked
269+ assert not any ("Attempting to release" in m for m in caplog .messages )
270+ await lock .release ()
0 commit comments