Skip to content

Commit caf60a2

Browse files
Improve instrumentation
1 parent d14f554 commit caf60a2

4 files changed

Lines changed: 91 additions & 15 deletions

File tree

internal_filesystem/lib/mpos/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
click_button, click_label, click_keyboard_button, find_button_with_text,
4242
get_all_widgets_with_text, find_setting_value_label, get_setting_value_text,
4343
verify_setting_value_text, find_dropdown_widget, get_dropdown_options,
44-
find_dropdown_option_index, select_dropdown_option_by_text
44+
find_dropdown_option_index, select_dropdown_option_by_text,
45+
get_all_children, simulate_long_press
4546
)
4647

4748
# UI utility functions
@@ -102,6 +103,7 @@
102103
"get_all_widgets_with_text", "find_setting_value_label", "get_setting_value_text",
103104
"verify_setting_value_text", "find_dropdown_widget", "get_dropdown_options",
104105
"find_dropdown_option_index", "select_dropdown_option_by_text",
106+
"get_all_children", "simulate_long_press",
105107
# Submodules
106108
"ui", "config", "net", "content", "time", "sensor_manager",
107109
"camera_manager", "sdcard", "audio", "hardware",

internal_filesystem/lib/mpos/ui/testing.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,3 +1688,17 @@ def click_keyboard_button(keyboard, button_text, use_direct=True):
16881688
return False
16891689

16901690
return True
1691+
1692+
1693+
def get_all_children(parent):
1694+
result = []
1695+
count = parent.get_child_count()
1696+
for i in range(count):
1697+
child = parent.get_child(i)
1698+
result.append(child)
1699+
result.extend(get_all_children(child))
1700+
return result
1701+
1702+
1703+
def simulate_long_press(x, y, duration_ms=1000):
1704+
simulate_click(x, y, press_duration_ms=duration_ms)

scripts/mpos_controller.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,13 @@ def press(self, x, y):
425425
"wait_for_render()".format(x, y)
426426
)
427427

428+
def long_press(self, x, y, duration_ms=1000):
429+
self.exec(
430+
"from mpos.ui.testing import simulate_click, wait_for_render; "
431+
"simulate_click({}, {}, press_duration_ms={}); "
432+
"wait_for_render()".format(x, y, duration_ms)
433+
)
434+
428435
def drag(self, x1, y1, x2, y2):
429436
self.exec_multiline("""
430437
from mpos.ui.testing import simulate_click, wait_for_render
@@ -640,6 +647,19 @@ def press(self, x, y):
640647
"wait_for_render()".format(tx, ty)
641648
)
642649

650+
def long_press(self, x, y, duration_ms=1000):
651+
rot = getattr(self, "_rotation", 0)
652+
if rot == 3: # DISPLAY_ROTATION._270
653+
tx = self._height - 1 - y
654+
ty = x
655+
else:
656+
tx, ty = x, y
657+
self.exec(
658+
"from mpos.ui.testing import simulate_click, wait_for_render; "
659+
"simulate_click({}, {}, press_duration_ms={}); "
660+
"wait_for_render()".format(tx, ty, duration_ms)
661+
)
662+
643663
def drag(self, x1, y1, x2, y2):
644664
rot = getattr(self, "_rotation", 0)
645665
if rot == 3:
@@ -765,6 +785,9 @@ def backscreen(self):
765785
def press(self, x, y):
766786
self._backend.press(x, y)
767787

788+
def long_press(self, x, y, duration_ms=1000):
789+
self._backend.long_press(x, y, duration_ms)
790+
768791
def drag(self, x1, y1, x2, y2):
769792
self._backend.drag(x1, y1, x2, y2)
770793

@@ -805,7 +828,7 @@ def main():
805828
parser = argparse.ArgumentParser(description="MicroPythonOS Controller")
806829
parser.add_argument(
807830
"action", nargs="?", default="exec",
808-
help="Action: exec, eval, screenshot, startapp, freespace, backscreen, installapp, listapps, deleteapp, click, drag (default: exec)",
831+
help="Action: exec, eval, screenshot, startapp, freespace, backscreen, installapp, listapps, deleteapp, click, longpress, drag (default: exec)",
809832
)
810833
parser.add_argument("args", nargs="*", help="Arguments")
811834
parser.add_argument("--binary", help="Path to lvgl_micropy_unix binary")
@@ -927,6 +950,14 @@ def main():
927950
with ctrl:
928951
ctrl.press(x, y)
929952
print("Clicked ({}, {})".format(x, y))
953+
elif args.action == "longpress":
954+
if len(args.args) < 2:
955+
print("error: X Y required", file=sys.stderr)
956+
return 1
957+
x, y = int(args.args[0]), int(args.args[1])
958+
with ctrl:
959+
ctrl.long_press(x, y)
960+
print("Long-pressed ({}, {})".format(x, y))
930961
elif args.action == "drag":
931962
if len(args.args) < 4:
932963
print("error: X1 Y1 X2 Y2 required", file=sys.stderr)

tests/cpython_mpos_controller.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import os
2222
import time
2323
import argparse
24+
import subprocess
2425

2526
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
2627
from scripts.mpos_controller import MPOSController
@@ -57,12 +58,13 @@ def find_buttons(tree, results=None):
5758
return results
5859

5960

60-
def run_tests(mpos, only=None, is_serial=False):
61+
def run_tests(mpos, only=None, is_serial=False, cli_binary=None, serial_port=None):
6162
sections = {
6263
"basic": test_basic,
6364
"ui": test_ui_introspection,
6465
"interaction": test_interaction,
6566
"drag": test_drag,
67+
"cli": test_cli_longpress,
6668
"sessions": test_multiple_sessions,
6769
"navigation": test_app_navigation,
6870
"appmanagement": test_app_management,
@@ -71,13 +73,18 @@ def run_tests(mpos, only=None, is_serial=False):
7173
names = [s.strip() for s in only.split(",")]
7274
for n in names:
7375
if n in sections:
74-
sections[n](mpos, is_serial=is_serial)
76+
sections[n](
77+
mpos,
78+
is_serial=is_serial,
79+
cli_binary=cli_binary,
80+
serial_port=serial_port,
81+
)
7582
else:
7683
for name, fn in sections.items():
77-
fn(mpos, is_serial=is_serial)
84+
fn(mpos, is_serial=is_serial, cli_binary=cli_binary, serial_port=serial_port)
7885

7986

80-
def test_basic(mpos, is_serial=False):
87+
def test_basic(mpos, is_serial=False, cli_binary=None, serial_port=None):
8188
section("Basic exec / eval / multiline")
8289

8390
out = mpos.exec("print('hello from mpos')")
@@ -111,7 +118,7 @@ def test_basic(mpos, is_serial=False):
111118
check(val == i * 10, f"interleaved exec/eval {i}: x == {val}")
112119

113120

114-
def test_ui_introspection(mpos, is_serial=False):
121+
def test_ui_introspection(mpos, is_serial=False, cli_binary=None, serial_port=None):
115122
section("UI creation / screenshot / widget tree / visible text")
116123

117124
mpos.exec("""
@@ -150,7 +157,7 @@ def test_ui_introspection(mpos, is_serial=False):
150157
check(not mpos.find_text("NonexistentXYZ12345"), "find_text rejects nonexistent")
151158

152159

153-
def test_interaction(mpos, is_serial=False):
160+
def test_interaction(mpos, is_serial=False, cli_binary=None, serial_port=None):
154161
section("Button interaction (press_key / press)")
155162

156163
mpos.exec("""
@@ -202,7 +209,7 @@ def cb(e):
202209
check("clicked!" in texts, f"send_event fallback: {texts}")
203210

204211

205-
def test_drag(mpos, is_serial=False):
212+
def test_drag(mpos, is_serial=False, cli_binary=None, serial_port=None):
206213
section("Drag (slider interaction)")
207214

208215
mpos.exec("""
@@ -225,7 +232,29 @@ def test_drag(mpos, is_serial=False):
225232
check(val > 20, f"drag moved slider from 0 to {val}")
226233

227234

228-
def test_multiple_sessions(mpos, is_serial=False):
235+
def test_cli_longpress(mpos, is_serial=False, cli_binary=None, serial_port=None):
236+
section("CLI longpress action")
237+
if is_serial:
238+
check(True, "skipped (serial backend)")
239+
return
240+
241+
script_path = os.path.abspath(
242+
os.path.join(os.path.dirname(__file__), "..", "scripts", "mpos_controller.py")
243+
)
244+
cmd = ["python3", script_path]
245+
if cli_binary:
246+
cmd.extend(["--binary", cli_binary])
247+
cmd.extend(["longpress", "0", "0"])
248+
249+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
250+
check(result.returncode == 0, f"CLI longpress exits 0 (got {result.returncode})")
251+
check(
252+
"Long-pressed (0, 0)" in result.stdout,
253+
f"CLI longpress prints confirmation: {result.stdout.strip()!r}",
254+
)
255+
256+
257+
def test_multiple_sessions(mpos, is_serial=False, cli_binary=None, serial_port=None):
229258
section("Multiple sessions")
230259
if is_serial:
231260
check(True, "skipped (serial backend)")
@@ -237,7 +266,7 @@ def test_multiple_sessions(mpos, is_serial=False):
237266
check(True, "all 3 sessions OK")
238267

239268

240-
def test_app_navigation(mpos, is_serial=False):
269+
def test_app_navigation(mpos, is_serial=False, cli_binary=None, serial_port=None):
241270
section("App navigation (startapp / backscreen / freespace)")
242271

243272
mpos.exec("""
@@ -269,7 +298,7 @@ def test_app_navigation(mpos, is_serial=False):
269298
check(isinstance(free, int) and free > 0, f"free space: {free} bytes")
270299

271300

272-
def test_app_management(mpos, is_serial=False):
301+
def test_app_management(mpos, is_serial=False, cli_binary=None, serial_port=None):
273302
section("App management (install / list / remove)")
274303
if not is_serial:
275304
check(True, "skipped (desktop backend)")
@@ -319,7 +348,7 @@ def test_app_management(mpos, is_serial=False):
319348
def main():
320349
parser = argparse.ArgumentParser(description="Test MPOSController backends")
321350
parser.add_argument("--serial", help="Serial port for device backend")
322-
parser.add_argument("--only", help="Comma-separated test sections: basic,ui,interaction,drag,sessions,navigation,appmanagement")
351+
parser.add_argument("--only", help="Comma-separated test sections: basic,ui,interaction,drag,cli,sessions,navigation,appmanagement")
323352
parser.add_argument("--binary", help="Path to lvgl_micropy_unix binary")
324353
args = parser.parse_args()
325354

@@ -332,15 +361,15 @@ def main():
332361
ctrl = MPOSController(backend="serial", port=args.serial, baudrate=115200, reset=True)
333362
try:
334363
ctrl.start()
335-
run_tests(ctrl, only=args.only, is_serial=True)
364+
run_tests(ctrl, only=args.only, is_serial=True, cli_binary=args.binary, serial_port=args.serial)
336365
finally:
337366
ctrl.stop()
338367
else:
339368
print(f"\n{'#'*60}")
340369
print(f" Testing DESKTOP (process) backend")
341370
print(f"{'#'*60}")
342371
with MPOSController(binary=args.binary) as mpos:
343-
run_tests(mpos, only=args.only)
372+
run_tests(mpos, only=args.only, cli_binary=args.binary, serial_port=args.serial)
344373

345374
print(f"\n{'='*60}")
346375
print(f" Results: {PASS} passed, {FAIL} failed")

0 commit comments

Comments
 (0)