diff --git a/pyproject.toml b/pyproject.toml index 8148c1b..9508386 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta' [project] name = 'locklib' -version = '0.0.25' +version = '0.0.26' authors = [ { name='Evgeniy Blinov', email='zheni-b@yandex.ru' }, ] diff --git a/tests/units/locks/smart_lock/test_abstract.py b/tests/units/locks/smart_lock/test_abstract.py index 3a438f7..f780a2e 100644 --- a/tests/units/locks/smart_lock/test_abstract.py +++ b/tests/units/locks/smart_lock/test_abstract.py @@ -66,34 +66,34 @@ def test_abstract_smart_lock_subclass_cleans_wait_for_graph_after_contention(sma """ Every public AbstractSmartLock subclass cleans up its wait-for graph after contention. - While the test thread holds the lock, a queued waiter creates the expected waiter-to-owner edge. After the owner releases and the waiter finishes, the lock queue and dedicated wait-for graph are empty. + With the lock held, a waiter creates a waiter-to-owner edge. Releasing the lock lets the waiter finish and leaves the queue and dedicated graph empty. """ graph = LocksGraph() lock = smartlock_class(local_graph=graph) unexpected_errors: List[Exception] = [] - def waiter() -> None: + def acquire_lock() -> None: try: with lock: pass except Exception as error: # noqa: BLE001 unexpected_errors.append(error) - waiter_thread = Thread(target=waiter, daemon=True) + waiter_thread = Thread(target=acquire_lock, daemon=True) + contention_observed = False try: with lock: waiter_thread.start() - deadline = monotonic() + 1 + deadline = monotonic() + 2.5 - while True: + while monotonic() < deadline: with lock.lock, graph.lock: if len(lock.deque) == 2: waiter_thread_id, owner_thread_id = lock.deque assert graph.links == {waiter_thread_id: {owner_thread_id}} + contention_observed = True break - if monotonic() >= deadline: - raise AssertionError('Waiter did not enter the lock queue.') sleep(0.001) finally: if waiter_thread.ident is not None: @@ -102,6 +102,7 @@ def waiter() -> None: assert not waiter_thread.is_alive() if unexpected_errors: raise AssertionError(f'Unexpected worker exceptions: {unexpected_errors!r}') from unexpected_errors[0] + assert contention_observed, 'Waiter did not enter the lock queue.' with lock.lock: assert not lock.deque assert graph.links == {} diff --git a/tests/units/locks/smart_lock/test_graph.py b/tests/units/locks/smart_lock/test_graph.py index 4dc7103..60c2a77 100644 --- a/tests/units/locks/smart_lock/test_graph.py +++ b/tests/units/locks/smart_lock/test_graph.py @@ -4,11 +4,11 @@ from locklib.locks.smart_lock.graph import LocksGraph -def test_multiple_set_and_get(): +def test_get_links_from_returns_links_without_creating_entry_for_absent_node(): """ - LocksGraph returns all outgoing links for a source and empty sets for nodes without outgoing links. + get_links_from returns outgoing links without creating an entry for an absent node. - After adding 1 -> {2, 3, 4}, get_links_from returns all three targets for 1 and empty sets for destination-only node 2 and absent node 5. Looking up 5 leaves the adjacency map unchanged. + After adding 1 -> {2, 3, 4}, it returns those targets for 1 and an empty set for 5, leaving the adjacency map unchanged. """ graph = LocksGraph() @@ -17,17 +17,15 @@ def test_multiple_set_and_get(): graph.add_link(1, 4) assert graph.get_links_from(1) == {2, 3, 4} - - assert graph.get_links_from(2) == set() assert graph.get_links_from(5) == set() assert graph.links == {1: {2, 3, 4}} def test_get_links_from_destination_only_node_does_not_create_entry(): """ - Looking up outgoing links for a node that only appears as an edge destination does not create an adjacency entry. + Looking up links for a destination-only node does not create an adjacency entry. - After adding 1 -> 2, get_links_from returns an empty set for 2 while preserving 1 -> 2 as the graph's only stored adjacency entry. + After adding 1 -> 2, the lookup returns an empty set and leaves the adjacency map unchanged. """ graph = LocksGraph() @@ -37,7 +35,7 @@ def test_get_links_from_destination_only_node_does_not_create_entry(): assert graph.links == {1: {2}} -def test_search_cycles_does_not_create_empty_nodes(): +def test_search_cycles_does_not_create_entry_for_missing_source(): """ Searching from a missing source does not create an adjacency entry.