feat: release 2.2.0 with PC-only tests and execution reliability fixes
Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
This commit is contained in:
1 parent
fac4ccb760
commit
be6e8c2abd
79 files changed
+2263
-277
No files matched your search
@@ -86,6 +86,28 @@ def test_healthy_helper_never_constructs_uiautomator(composite):
|
||||
factory.assert_not_called()
|
||||
|
||||
|
||||
def test_clear_reply_loss_never_replays_on_fallback_backend(composite):
|
||||
client, helper, u2, factory, _ = composite
|
||||
client.connect()
|
||||
helper.clear_text.side_effect = HelperUnavailable("reply lost after clear")
|
||||
with pytest.raises(HelperUnavailable):
|
||||
client.clear_text()
|
||||
helper.clear_text.assert_called_once_with()
|
||||
factory.assert_not_called()
|
||||
u2.clear_text.assert_not_called()
|
||||
|
||||
|
||||
def test_clear_uses_the_backend_that_observed_the_field(composite):
|
||||
client, helper, u2, _, clock = composite
|
||||
helper.connect.side_effect = HelperUnavailable("offline")
|
||||
client.connect()
|
||||
clock["now"] = 100.0 # Helper recovery is due, but must wait for observation.
|
||||
u2.clear_text.return_value = True
|
||||
assert client.clear_text() is True
|
||||
helper.clear_text.assert_not_called()
|
||||
u2.clear_text.assert_called_once_with()
|
||||
|
||||
|
||||
def test_connect_failure_degrades_to_uiautomator(composite):
|
||||
client, helper, u2, factory, _ = composite
|
||||
helper.connect.side_effect = HelperUnavailable("not installed")
|
||||
|
||||
@@ -12,6 +12,7 @@ from uiautomator2 import base, core
|
||||
from artemis.clients import ui_automator_client
|
||||
from artemis.clients.accessibility_client import HelperUnavailable
|
||||
from artemis.clients.screen_client_factory import FallbackScreenClient
|
||||
from artemis.runtime import observation_budget
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -128,6 +129,53 @@ def test_each_failed_launch_is_closed_before_retry_and_final_failure(server):
|
||||
assert "3" in str(caught.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("helper_seconds", [0, 18, 59])
|
||||
def test_helper_and_uiautomator_share_observation_deadline(server, monkeypatch, helper_seconds):
|
||||
monkeypatch.setattr(observation_budget, "monotonic", server.clock.time)
|
||||
helper = MagicMock()
|
||||
|
||||
def unavailable():
|
||||
server.clock.sleep(helper_seconds)
|
||||
raise HelperUnavailable("helper snapshot timed out")
|
||||
|
||||
helper.get_screen_data.side_effect = unavailable
|
||||
client = FallbackScreenClient(
|
||||
"isolated-startup-device",
|
||||
helper=helper,
|
||||
state_probe=lambda _serial: "device",
|
||||
)
|
||||
budget = observation_budget.ObservationBudget(deadline=60)
|
||||
token = observation_budget.current_observation.set(budget)
|
||||
try:
|
||||
with pytest.raises(TimeoutError, match="预算耗尽"):
|
||||
client.get_screen_data()
|
||||
finally:
|
||||
observation_budget.current_observation.reset(token)
|
||||
client.disconnect()
|
||||
assert server.clock.now == 60
|
||||
assert len(server.connections) <= 2
|
||||
assert all(c.close_calls == 1 for c in server.connections)
|
||||
assert client.active_backend is None
|
||||
|
||||
|
||||
def test_disconnect_during_connect_discards_and_stops_late_device(server, monkeypatch):
|
||||
client = ui_automator_client.UIAutomatorClient("isolated-startup-device")
|
||||
candidate = MagicMock()
|
||||
|
||||
def late_connection(_device_id):
|
||||
client.disconnect(stop_server=True)
|
||||
return candidate
|
||||
|
||||
factory = MagicMock(side_effect=late_connection)
|
||||
monkeypatch.setattr(ui_automator_client, "_ManagedUIAutomatorDevice", factory)
|
||||
with pytest.raises(RuntimeError, match="连接已取消"):
|
||||
client.connect()
|
||||
factory.assert_called_once()
|
||||
candidate.stop_uiautomator.assert_called_once()
|
||||
assert client._device is None
|
||||
assert client._awake_strategy is None
|
||||
|
||||
|
||||
def test_retry_can_connect_after_failed_launch_was_stopped(server):
|
||||
server.modes = ["not_ready", "ready"]
|
||||
client = ui_automator_client.UIAutomatorClient("isolated-startup-device")
|
||||
|
||||
Reference in new issue
Block a user