60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
|
|
"""Tests for mine_global_lock: non-blocking cross-process lock semantics."""
|
||
|
|
import threading
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from mempalace.palace import MineAlreadyRunning, mine_global_lock
|
||
|
|
|
||
|
|
|
||
|
|
def test_mine_global_lock_acquired(tmp_path, monkeypatch):
|
||
|
|
"""Lock is acquired and released without error."""
|
||
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
||
|
|
with mine_global_lock():
|
||
|
|
pass # should not raise
|
||
|
|
|
||
|
|
|
||
|
|
def test_mine_global_lock_second_acquire_raises(tmp_path, monkeypatch):
|
||
|
|
"""Concurrent second acquire raises MineAlreadyRunning."""
|
||
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
||
|
|
results: list[str] = []
|
||
|
|
|
||
|
|
with mine_global_lock():
|
||
|
|
# While this lock is held, spawn a thread that tries to acquire.
|
||
|
|
def try_acquire():
|
||
|
|
try:
|
||
|
|
with mine_global_lock():
|
||
|
|
results.append("acquired")
|
||
|
|
except MineAlreadyRunning:
|
||
|
|
results.append("blocked")
|
||
|
|
|
||
|
|
t = threading.Thread(target=try_acquire)
|
||
|
|
t.start()
|
||
|
|
t.join(timeout=5)
|
||
|
|
|
||
|
|
assert results == ["blocked"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_mine_global_lock_reusable_after_release(tmp_path, monkeypatch):
|
||
|
|
"""Lock can be re-acquired after the context manager exits."""
|
||
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
||
|
|
|
||
|
|
with mine_global_lock():
|
||
|
|
pass # first acquire + release
|
||
|
|
|
||
|
|
# Second acquire must succeed; MineAlreadyRunning would propagate as failure.
|
||
|
|
with mine_global_lock():
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
def test_mine_global_lock_exception_still_releases(tmp_path, monkeypatch):
|
||
|
|
"""Lock is released even when the body raises."""
|
||
|
|
monkeypatch.setenv("HOME", str(tmp_path))
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
with mine_global_lock():
|
||
|
|
raise ValueError("boom")
|
||
|
|
|
||
|
|
# Must be acquirable again after the exception.
|
||
|
|
with mine_global_lock():
|
||
|
|
pass
|