"""Cancellation drains input threads, while read-only work cannot hold the deadline.""" import asyncio from threading import Event from types import SimpleNamespace import pytest from artemis.drivers.android.adb_driver import AndroidAdbDriver @pytest.mark.asyncio @pytest.mark.parametrize("command", ["control_action", "touch_gesture", "inspect_control"]) async def test_cancel_keeps_input_owned_until_transport_finishes(command): started, release, finished, cancel_seen = Event(), Event(), Event(), Event() def request(name, args, *, timeout): if name == "cancel_gesture": cancel_seen.set() return {"success": True, "execution": "observed", "code": "CANCEL_RECORDED"} started.set() try: if not release.wait(2): raise RuntimeError("test transport was not released") return {"success": True, "execution": "completed", "code": "OK"} finally: finished.set() driver = object.__new__(AndroidAdbDriver) driver._ui_adb_client = SimpleNamespace(native_request=request) operation = asyncio.create_task(driver.native_request(command, {"operation_id": "test-cancel"})) try: assert await asyncio.to_thread(started.wait, 1) operation.cancel() done, _ = await asyncio.wait({operation}, timeout=0.05) if command == "inspect_control": assert operation in done and not finished.is_set() else: assert operation not in done and not finished.is_set() operation.cancel() done, _ = await asyncio.wait({operation}, timeout=0.02) assert not done assert cancel_seen.is_set() == (command == "touch_gesture") finally: release.set() with pytest.raises(asyncio.CancelledError): await operation assert await asyncio.to_thread(finished.wait, 1) @pytest.mark.asyncio async def test_cancelled_input_transport_failure_does_not_replace_cancellation(): started, release = Event(), Event() def request(*args, **kwargs): started.set() release.wait(2) raise OSError("response lost") driver = object.__new__(AndroidAdbDriver) driver._ui_adb_client = SimpleNamespace(native_request=request) operation = asyncio.create_task(driver.native_request("control_action", {})) try: assert await asyncio.to_thread(started.wait, 1) operation.cancel() await asyncio.sleep(0) finally: release.set() with pytest.raises(asyncio.CancelledError): await operation