"""Pro and Flash must judge clear versus fill against fresh input evidence.""" from types import SimpleNamespace from unittest.mock import AsyncMock, Mock import pytest from artemis.agents.validator.action_execution import exec_action from artemis.mcp.action_executor import McpActionExecutor from artemis.mcp.action_types import ActionResult, ObserveResult @pytest.mark.asyncio @pytest.mark.parametrize("mode", ["pro", "flash"]) @pytest.mark.parametrize( ("text", "clear", "observed", "success"), [ ("", True, "", True), ("", True, "AR", False), ("", True, None, False), ("new", True, "new", True), ("new", True, "", False), ("new", False, "oldnew", True), ("new", False, None, False), ("", False, "", True), ("", False, "old", True), ], ) async def test_input_verification_matches_intent(mode, text, clear, observed, success): ctx = SimpleNamespace( data_engine=None, device=SimpleNamespace(device_width=100, device_height=100) ) element = { "class": "android.widget.EditText", "text": observed, "focused": True, "bounds": "[0,0][100,100]", "center": [50, 50], } session = Mock() session.call = AsyncMock(return_value=ActionResult.success("input_text", "Dispatched")) session.ui_hierarchy = AsyncMock(return_value=[element]) if mode == "pro": action = { "action": "focus_and_input_text", "coordinates": [50, 50], "text": text, "clear_before_input": clear, } ok, _ = await exec_action(ctx, session, action) else: executor = McpActionExecutor(ctx, actuator=Mock()) executor._session_or_start = AsyncMock(return_value=session) executor._observe_after_action = AsyncMock( return_value=(ObserveResult(ok=True, elements=[element]), True) ) result = await executor._execute_device_action( "input_text", "input_text", {"text": text, "clear_exist": clear}, "input", SimpleNamespace(latest_screenshot=None, indexed_elements=[]), ) ok = result.status == "success" assert ok is success session.call.assert_awaited_once() @pytest.mark.asyncio @pytest.mark.parametrize("observation_ok", [True, False]) async def test_flash_cannot_confirm_clear_without_fresh_hierarchy(observation_ok): ctx = SimpleNamespace(data_engine=None, device=None) session = Mock() session.call = AsyncMock(return_value=ActionResult.success("input_text", "Dispatched")) executor = McpActionExecutor(ctx, actuator=Mock()) executor._session_or_start = AsyncMock(return_value=session) executor._observe_after_action = AsyncMock( return_value=( ObserveResult( ok=observation_ok, hierarchy_ok=False, elements=[{"focused": True, "input_value_state": "empty", "center": [1, 1]}], ), True, ) ) result = await executor._execute_device_action( "input_text", "input_text", {"text": "", "clear_exist": True}, "clear", SimpleNamespace(latest_screenshot=None, indexed_elements=[]), ) assert result.status == "error" assert result.metadata["input_verification"] == "unknown" session.call.assert_awaited_once()