Skip to content

Commit cd692ef

Browse files
committed
test: add retry logic tests for VisionAgent
- Introduced tests for locating, clicking, and mouse moving with retry logic
1 parent d092b12 commit cd692ef

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

tests/e2e/agent/test_retry.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from typing import Optional, Tuple, Union
2+
3+
import pytest
4+
5+
from askui import ConfigurableRetry, LocateModel, VisionAgent
6+
from askui.exceptions import ElementNotFoundError
7+
from askui.locators.locators import Locator
8+
from askui.models import ModelComposition
9+
from askui.tools.toolbox import AgentToolbox
10+
from askui.utils.image_utils import ImageSource
11+
12+
13+
class FailingLocateModel(LocateModel):
14+
def __init__(
15+
self, fail_times: int, succeed_point: Optional[Tuple[int, int]] = None
16+
) -> None:
17+
self.fail_times = fail_times
18+
self.calls = 0
19+
if succeed_point is None:
20+
self.succeed_point = (10, 10)
21+
else:
22+
self.succeed_point = succeed_point
23+
24+
def locate(
25+
self,
26+
locator: Union[str, Locator],
27+
image: ImageSource, # noqa: ARG002
28+
model_choice: Union[ModelComposition, str], # noqa: ARG002
29+
) -> Tuple[int, int]:
30+
self.calls += 1
31+
if self.calls <= self.fail_times:
32+
raise ElementNotFoundError(locator, locator)
33+
return self.succeed_point
34+
35+
36+
@pytest.fixture
37+
def failing_model() -> FailingLocateModel:
38+
return FailingLocateModel(fail_times=2)
39+
40+
41+
@pytest.fixture
42+
def always_failing_model() -> FailingLocateModel:
43+
return FailingLocateModel(fail_times=10)
44+
45+
46+
@pytest.fixture
47+
def vision_agent_with_retry(
48+
failing_model: FailingLocateModel, agent_toolbox_mock: AgentToolbox
49+
) -> VisionAgent:
50+
return VisionAgent(
51+
models={"failing-locate": failing_model}, tools=agent_toolbox_mock
52+
)
53+
54+
55+
@pytest.fixture
56+
def vision_agent_always_fail(
57+
always_failing_model: FailingLocateModel, agent_toolbox_mock: AgentToolbox
58+
) -> VisionAgent:
59+
return VisionAgent(
60+
models={"always-fail": always_failing_model},
61+
tools=agent_toolbox_mock,
62+
retry=ConfigurableRetry(
63+
on_exception_types=(ElementNotFoundError,),
64+
strategy="Fixed",
65+
retry_count=3,
66+
base_delay=1,
67+
),
68+
)
69+
70+
71+
def test_locate_retries_and_succeeds(
72+
vision_agent_with_retry: VisionAgent, failing_model: FailingLocateModel
73+
) -> None:
74+
result = vision_agent_with_retry.locate(
75+
"something", screenshot=None, model="failing-locate"
76+
)
77+
assert result == (10, 10)
78+
assert failing_model.calls == 3 # 2 fails + 1 success
79+
80+
81+
def test_locate_retries_and_fails(
82+
vision_agent_always_fail: VisionAgent, always_failing_model: FailingLocateModel
83+
) -> None:
84+
with pytest.raises(ElementNotFoundError):
85+
vision_agent_always_fail.locate(
86+
"something", screenshot=None, model="always-fail"
87+
)
88+
assert always_failing_model.calls == 3 # Only 3 attempts
89+
90+
91+
def test_click_retries(
92+
vision_agent_with_retry: VisionAgent, failing_model: FailingLocateModel
93+
) -> None:
94+
vision_agent_with_retry.click("something", model="failing-locate")
95+
assert failing_model.calls == 3
96+
97+
98+
def test_mouse_move_retries(
99+
vision_agent_with_retry: VisionAgent, failing_model: FailingLocateModel
100+
) -> None:
101+
vision_agent_with_retry.mouse_move("something", model="failing-locate")
102+
assert failing_model.calls == 3

0 commit comments

Comments
 (0)