File size: 2,524 Bytes
24c5a49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b9b8c8
 
 
 
 
 
24c5a49
 
8b9b8c8
24c5a49
 
 
8b9b8c8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import pytest

from tiny_computer_agent import (
    Action, DryRunExecutor, Element, InputUnavailable, Observation,
    PyAutoGuiExecutor, resolve_point,
)

FRAME = Observation(elements=(Element("Save", x=320, y=120, width=80, height=24),))


def test_resolve_point_uses_pixels_when_given():
    assert resolve_point(Action("click", x=1, y=2), FRAME) == (1, 2)


def test_resolve_point_uses_the_element_index_for_labels():
    assert resolve_point(Action("click", label="Save"), FRAME) == (360, 132)


def test_resolve_point_is_none_for_unmatched_labels_and_other_verbs():
    assert resolve_point(Action("click", label="Quit"), FRAME) is None
    assert resolve_point(Action("type", text="hi"), FRAME) is None


def test_dry_run_records_instead_of_acting():
    executor = DryRunExecutor()
    result = executor.execute(Action("click", label="Save"), FRAME)
    assert result.ok
    assert "(dry-run)" in result.note
    assert "(360, 132)" in result.note
    assert len(executor.performed) == 1


def test_dry_run_fails_where_live_input_would_refuse():
    """A dry run must never give false confidence about a doomed click."""
    dry = DryRunExecutor().execute(Action("click", label="Quit"), FRAME)
    live = PyAutoGuiExecutor().execute(Action("click", label="Quit"), FRAME)
    assert dry.ok is False and live.ok is False
    assert "absent from the element index" in dry.note


def test_live_executor_refuses_to_guess_pixels():
    """The dangerous path: a label with no element behind it must never click."""
    result = PyAutoGuiExecutor().execute(Action("click", label="Quit"), FRAME)
    assert result.ok is False
    assert "refusing to guess pixels" in result.note


def test_live_executor_treats_done_as_a_no_op():
    result = PyAutoGuiExecutor().execute(Action("done", reason="finished"))
    assert result.ok and "goal reported met" in result.note


def test_live_executor_needs_pyautogui_installed():
    """Real input on a machine that cannot run pyautogui fails loudly, not silently.

    pyautogui installed is not the same as pyautogui usable: with no display it
    raises KeyError('DISPLAY') on import, so this must catch Exception rather than
    ImportError or the failure escapes as a traceback instead of InputUnavailable.
    """
    try:
        import pyautogui  # noqa: F401
    except Exception:
        with pytest.raises(InputUnavailable, match="pyautogui"):
            PyAutoGuiExecutor().execute(Action("type", text="hello"))
    else:
        pytest.skip("pyautogui is usable here")