hkblockchaintechnology commited on
Commit
92b12f2
·
verified ·
1 Parent(s): 8b9b8c8

Publish tests/ from marimo

Browse files
Files changed (1) hide show
  1. tests/test_live_desktop.py +53 -22
tests/test_live_desktop.py CHANGED
@@ -6,7 +6,7 @@ synthetic screenshot. It is opt-in because it moves the real mouse and clicks:
6
  TINY_COMPUTER_AGENT_LIVE_TEST=1 python -m pytest tests/test_live_desktop.py
7
 
8
  Run it on a throwaway desktop (VM, container with Xvfb), never your working
9
- session. With Xvfb it needs no hardware:
10
 
11
  Xvfb :99 -screen 0 1280x800x24 &
12
  DISPLAY=:99 TINY_COMPUTER_AGENT_LIVE_TEST=1 python -m pytest tests/test_live_desktop.py
@@ -21,10 +21,11 @@ import time
21
  import pytest
22
 
23
  from tiny_computer_agent.act import PyAutoGuiExecutor
24
- from tiny_computer_agent.actions import Action
25
  from tiny_computer_agent.desktop import display_available
26
  from tiny_computer_agent.observe import GrabScreen
27
- from tiny_computer_agent.perception import OcrDetector, detector_available
 
 
28
 
29
  OPT_IN = os.environ.get("TINY_COMPUTER_AGENT_LIVE_TEST") == "1"
30
 
@@ -33,15 +34,18 @@ pytestmark = pytest.mark.skipif(
33
  reason="moves the real mouse: set TINY_COMPUTER_AGENT_LIVE_TEST=1 on a throwaway desktop",
34
  )
35
 
36
- APP = '''import pathlib, tkinter as tk
37
-
38
- MARKER = pathlib.Path("%(marker)s")
39
  WINDOW = (120, 120)
40
  BUTTON = (20, 155, 110, 40) # x, y, width, height inside the window
 
 
 
 
 
 
41
 
42
  root = tk.Tk()
43
  root.title("Document")
44
- root.geometry("420x230+%%d+%%d" %% WINDOW)
45
  root.configure(bg="#f5f6f8")
46
  title = tk.Label(root, text="Untitled document - unsaved changes", bg="#f5f6f8")
47
  title.place(x=20, y=18)
@@ -57,8 +61,9 @@ def chosen(what):
57
  widget.place_forget()
58
 
59
  buttons = [tk.Button(root, text="Save", command=lambda: chosen("Save"))]
60
- buttons[0].place(x=BUTTON[0], y=BUTTON[1], width=BUTTON[2], height=BUTTON[3])
61
  root.update()
 
62
  root.mainloop()
63
  '''
64
 
@@ -70,43 +75,69 @@ def save_dialog(tmp_path):
70
  pytest.skip("no display session to drive")
71
  if not detector_available("ocr")[0]:
72
  pytest.skip("this test needs OCR to find the button")
 
73
  marker = tmp_path / "clicked.txt"
 
74
  app = tmp_path / "app.py"
75
- app.write_text(APP % {"marker": marker})
 
76
  process = subprocess.Popen([sys.executable, str(app)])
77
  try:
78
- time.sleep(2.5) # let Tk map the window
79
- yield marker, (120, 120), (20, 155, 110, 40)
 
 
 
 
 
80
  finally:
 
 
81
  process.terminate()
 
 
 
 
 
 
 
 
 
 
 
82
 
83
 
84
  def test_ocr_finds_a_real_button_at_the_right_pixels(save_dialog):
85
  """Pixels in, a label whose coordinates really are the button's."""
86
- marker, window, (bx, by, bw, bh) = save_dialog
87
- observation = GrabScreen(monitor=1).capture()
88
- elements = OcrDetector().detect(observation.frame)
89
 
90
  save = [e for e in elements if e.matches("Save")]
91
  assert save, f"OCR found no Save button, only {[e.label for e in elements]}"
92
- centre = save[0].center
93
- assert window[0] + bx <= centre[0] <= window[0] + bx + bw, f"{centre} outside the button"
94
- assert window[1] + by <= centre[1] <= window[1] + by + bh, f"{centre} outside the button"
95
 
96
 
97
  def test_a_real_click_lands_on_the_button(save_dialog):
98
  """The whole loop: see it, choose it, click it, and the app reacts."""
99
  from tiny_computer_agent import AgentLoop, PlannerBrain, Policy
100
 
101
- marker, _, _ = save_dialog
 
 
 
 
 
 
102
  episode = AgentLoop(
103
- screen=GrabScreen(monitor=1),
104
  brain=PlannerBrain(),
105
  executor=PyAutoGuiExecutor(),
106
  policy=Policy(dry_run=False, step_budget=2),
107
  verify=lambda observation: "saved" in observation.text.lower(),
108
  ).run("click 'Save' to save the document")
109
 
110
- assert marker.exists(), "no click landed on the button"
111
- assert marker.read_text() == "Save"
112
- assert episode.success, episode.render()
 
 
6
  TINY_COMPUTER_AGENT_LIVE_TEST=1 python -m pytest tests/test_live_desktop.py
7
 
8
  Run it on a throwaway desktop (VM, container with Xvfb), never your working
9
+ session. With Xvfb it needs no hardware at all:
10
 
11
  Xvfb :99 -screen 0 1280x800x24 &
12
  DISPLAY=:99 TINY_COMPUTER_AGENT_LIVE_TEST=1 python -m pytest tests/test_live_desktop.py
 
21
  import pytest
22
 
23
  from tiny_computer_agent.act import PyAutoGuiExecutor
 
24
  from tiny_computer_agent.desktop import display_available
25
  from tiny_computer_agent.observe import GrabScreen
26
+ from tiny_computer_agent.perception import (
27
+ IndexedScreen, OcrDetector, detector_available,
28
+ )
29
 
30
  OPT_IN = os.environ.get("TINY_COMPUTER_AGENT_LIVE_TEST") == "1"
31
 
 
34
  reason="moves the real mouse: set TINY_COMPUTER_AGENT_LIVE_TEST=1 on a throwaway desktop",
35
  )
36
 
 
 
 
37
  WINDOW = (120, 120)
38
  BUTTON = (20, 155, 110, 40) # x, y, width, height inside the window
39
+ READY_TIMEOUT = 20.0
40
+
41
+ APP = '''import pathlib, tkinter as tk
42
+
43
+ MARKER = pathlib.Path(r"__MARKER__")
44
+ READY = pathlib.Path(str(MARKER) + ".ready")
45
 
46
  root = tk.Tk()
47
  root.title("Document")
48
+ root.geometry("420x230+120+120")
49
  root.configure(bg="#f5f6f8")
50
  title = tk.Label(root, text="Untitled document - unsaved changes", bg="#f5f6f8")
51
  title.place(x=20, y=18)
 
61
  widget.place_forget()
62
 
63
  buttons = [tk.Button(root, text="Save", command=lambda: chosen("Save"))]
64
+ buttons[0].place(x=20, y=155, width=110, height=40)
65
  root.update()
66
+ READY.write_text("mapped")
67
  root.mainloop()
68
  '''
69
 
 
75
  pytest.skip("no display session to drive")
76
  if not detector_available("ocr")[0]:
77
  pytest.skip("this test needs OCR to find the button")
78
+
79
  marker = tmp_path / "clicked.txt"
80
+ ready = pathlib.Path(str(marker) + ".ready")
81
  app = tmp_path / "app.py"
82
+ app.write_text(APP.replace("__MARKER__", str(marker)))
83
+
84
  process = subprocess.Popen([sys.executable, str(app)])
85
  try:
86
+ deadline = time.time() + READY_TIMEOUT
87
+ while time.time() < deadline and not ready.exists():
88
+ if process.poll() is not None:
89
+ pytest.fail(f"the test window exited immediately (code {process.returncode})")
90
+ time.sleep(0.2)
91
+ assert ready.exists(), "the test window never mapped"
92
+ yield marker
93
  finally:
94
+ # Wait for the window to actually disappear: a ghost window left on the
95
+ # display makes the *next* test's OCR see two identical buttons.
96
  process.terminate()
97
+ try:
98
+ process.wait(timeout=10)
99
+ except subprocess.TimeoutExpired: # pragma: no cover
100
+ process.kill()
101
+ process.wait(timeout=5)
102
+
103
+
104
+ def _look() -> tuple:
105
+ """(observation, elements) - one real capture, one real OCR pass."""
106
+ observation = GrabScreen(monitor=1).capture()
107
+ return observation, OcrDetector().detect(observation.frame)
108
 
109
 
110
  def test_ocr_finds_a_real_button_at_the_right_pixels(save_dialog):
111
  """Pixels in, a label whose coordinates really are the button's."""
112
+ _, elements = _look()
 
 
113
 
114
  save = [e for e in elements if e.matches("Save")]
115
  assert save, f"OCR found no Save button, only {[e.label for e in elements]}"
116
+ x, y = save[0].center
117
+ assert WINDOW[0] + BUTTON[0] <= x <= WINDOW[0] + BUTTON[0] + BUTTON[2], f"{x} outside the button"
118
+ assert WINDOW[1] + BUTTON[1] <= y <= WINDOW[1] + BUTTON[1] + BUTTON[3], f"{y} outside the button"
119
 
120
 
121
  def test_a_real_click_lands_on_the_button(save_dialog):
122
  """The whole loop: see it, choose it, click it, and the app reacts."""
123
  from tiny_computer_agent import AgentLoop, PlannerBrain, Policy
124
 
125
+ _, elements = _look()
126
+ labels = [e.label for e in elements]
127
+ save = [e for e in elements if e.matches("Save")]
128
+ assert save, f"nothing named Save on screen before clicking: {labels}"
129
+
130
+ # IndexedScreen, not a bare GrabScreen: capture gives pixels, the detector
131
+ # gives labels, and without labels there is nothing for a brain to choose.
132
  episode = AgentLoop(
133
+ screen=IndexedScreen(GrabScreen(monitor=1), OcrDetector()),
134
  brain=PlannerBrain(),
135
  executor=PyAutoGuiExecutor(),
136
  policy=Policy(dry_run=False, step_budget=2),
137
  verify=lambda observation: "saved" in observation.text.lower(),
138
  ).run("click 'Save' to save the document")
139
 
140
+ detail = episode.render() + f"\nclicked at {save[0].center}, labels were {labels}"
141
+ assert save_dialog.exists(), f"no click landed on the button\n{detail}"
142
+ assert save_dialog.read_text() == "Save"
143
+ assert episode.success, detail