Skip to content

Commit 5596ac4

Browse files
Add internal_filesystem/lib/mpos/ui/testing.py
1 parent b6869d5 commit 5596ac4

16 files changed

+106
-33
lines changed

tests/graphical_test_helper.py renamed to internal_filesystem/lib/mpos/ui/testing.py

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
"""
2-
Graphical testing helper module for MicroPythonOS.
2+
Graphical testing utilities for MicroPythonOS.
33
4-
This module provides utilities for graphical/visual testing that work on both
5-
desktop (unix/macOS) and device (ESP32).
4+
This module provides utilities for graphical/visual testing and UI automation
5+
that work on both desktop (unix/macOS) and device (ESP32). These functions can
6+
be used by:
7+
- Unit tests for verifying UI behavior
8+
- Apps that want to implement automation or testing features
9+
- Integration tests and end-to-end testing
610
7-
Important: Tests using this module should be run with boot and main files
8-
already executed (so display, theme, and UI infrastructure are initialized).
11+
Important: Functions in this module assume the display, theme, and UI
12+
infrastructure are already initialized (boot.py and main.py executed).
913
10-
Usage:
11-
from graphical_test_helper import wait_for_render, capture_screenshot
14+
Usage in tests:
15+
from mpos.ui.testing import wait_for_render, capture_screenshot
1216
1317
# Start your app
1418
mpos.apps.start_app("com.example.myapp")
@@ -22,8 +26,18 @@
2226
# Capture screenshot
2327
capture_screenshot("tests/screenshots/mytest.raw")
2428
25-
# Simulate click at coordinates
29+
# Simulate user interaction
2630
simulate_click(160, 120) # Click at center of 320x240 screen
31+
32+
Usage in apps:
33+
from mpos.ui.testing import simulate_click, find_label_with_text
34+
35+
# Automated demo mode
36+
label = find_label_with_text(self.screen, "Start")
37+
if label:
38+
area = lv.area_t()
39+
label.get_coords(area)
40+
simulate_click(area.x1 + 10, area.y1 + 10)
2741
"""
2842

2943
import lvgl as lv
@@ -41,9 +55,15 @@ def wait_for_render(iterations=10):
4155
4256
This processes the LVGL task handler multiple times to ensure
4357
all UI updates, animations, and layout changes are complete.
58+
Essential for tests to avoid race conditions.
4459
4560
Args:
4661
iterations: Number of task handler iterations to run (default: 10)
62+
63+
Example:
64+
mpos.apps.start_app("com.example.myapp")
65+
wait_for_render() # Ensure UI is ready
66+
assert verify_text_present(lv.screen_active(), "Welcome")
4767
"""
4868
import time
4969
for _ in range(iterations):
@@ -52,14 +72,19 @@ def wait_for_render(iterations=10):
5272

5373

5474
def capture_screenshot(filepath, width=320, height=240, color_format=lv.COLOR_FORMAT.RGB565):
55-
print(f"capture_screenshot writing to {filepath}")
5675
"""
5776
Capture screenshot of current screen using LVGL snapshot.
5877
5978
The screenshot is saved as raw binary data in the specified color format.
60-
To convert RGB565 to PNG, use:
79+
Useful for visual regression testing or documentation.
80+
81+
To convert RGB565 to PNG:
6182
ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb565 -s 320x240 -i file.raw file.png
6283
84+
Or use the conversion script:
85+
cd tests/screenshots
86+
./convert_to_png.sh
87+
6388
Args:
6489
filepath: Path where to save the raw screenshot data
6590
width: Screen width in pixels (default: 320)
@@ -71,7 +96,13 @@ def capture_screenshot(filepath, width=320, height=240, color_format=lv.COLOR_FO
7196
7297
Raises:
7398
Exception: If screenshot capture fails
99+
100+
Example:
101+
from mpos.ui.testing import capture_screenshot
102+
capture_screenshot("tests/screenshots/home.raw")
74103
"""
104+
print(f"capture_screenshot writing to {filepath}")
105+
75106
# Calculate buffer size based on color format
76107
if color_format == lv.COLOR_FORMAT.RGB565:
77108
bytes_per_pixel = 2
@@ -99,14 +130,19 @@ def get_all_labels(obj, labels=None):
99130
Recursively find all label widgets in the object hierarchy.
100131
101132
This traverses the entire widget tree starting from obj and
102-
collects all LVGL label objects.
133+
collects all LVGL label objects. Useful for comprehensive
134+
text verification or debugging.
103135
104136
Args:
105137
obj: LVGL object to search (typically lv.screen_active())
106138
labels: Internal accumulator list (leave as None)
107139
108140
Returns:
109141
list: List of all label objects found in the hierarchy
142+
143+
Example:
144+
labels = get_all_labels(lv.screen_active())
145+
print(f"Found {len(labels)} labels")
110146
"""
111147
if labels is None:
112148
labels = []
@@ -135,14 +171,20 @@ def find_label_with_text(obj, search_text):
135171
Find a label widget containing specific text.
136172
137173
Searches the entire widget hierarchy for a label whose text
138-
contains the search string (substring match).
174+
contains the search string (substring match). Returns the
175+
first match found.
139176
140177
Args:
141178
obj: LVGL object to search (typically lv.screen_active())
142179
search_text: Text to search for (can be substring)
143180
144181
Returns:
145182
LVGL label object if found, None otherwise
183+
184+
Example:
185+
label = find_label_with_text(lv.screen_active(), "Settings")
186+
if label:
187+
print(f"Found Settings label at {label.get_coords()}")
146188
"""
147189
labels = get_all_labels(obj)
148190
for label in labels:
@@ -160,12 +202,18 @@ def get_screen_text_content(obj):
160202
Extract all text content from all labels on screen.
161203
162204
Useful for debugging or comprehensive text verification.
205+
Returns a list of all text strings found in label widgets.
163206
164207
Args:
165208
obj: LVGL object to search (typically lv.screen_active())
166209
167210
Returns:
168211
list: List of all text strings found in labels
212+
213+
Example:
214+
texts = get_screen_text_content(lv.screen_active())
215+
assert "Welcome" in texts
216+
assert "Version 1.0" in texts
169217
"""
170218
labels = get_all_labels(obj)
171219
texts = []
@@ -184,14 +232,18 @@ def verify_text_present(obj, expected_text):
184232
Verify that expected text is present somewhere on screen.
185233
186234
This is the primary verification method for graphical tests.
187-
It searches all labels for the expected text.
235+
It searches all labels for the expected text (substring match).
188236
189237
Args:
190238
obj: LVGL object to search (typically lv.screen_active())
191239
expected_text: Text that should be present (can be substring)
192240
193241
Returns:
194242
bool: True if text found, False otherwise
243+
244+
Example:
245+
assert verify_text_present(lv.screen_active(), "Settings")
246+
assert verify_text_present(lv.screen_active(), "Version")
195247
"""
196248
return find_label_with_text(obj, expected_text) is not None
197249

@@ -201,9 +253,21 @@ def print_screen_labels(obj):
201253
Debug helper: Print all label text found on screen.
202254
203255
Useful for debugging tests to see what text is actually present.
256+
Prints to stdout with numbered list.
204257
205258
Args:
206259
obj: LVGL object to search (typically lv.screen_active())
260+
261+
Example:
262+
# When a test fails, use this to see what's on screen
263+
print_screen_labels(lv.screen_active())
264+
# Output:
265+
# Found 5 labels on screen:
266+
# 0: MicroPythonOS
267+
# 1: Version 0.3.3
268+
# 2: Settings
269+
# 3: About
270+
# 4: WiFi
207271
"""
208272
texts = get_screen_text_content(obj)
209273
print(f"Found {len(texts)} labels on screen:")
@@ -216,7 +280,7 @@ def _touch_read_cb(indev_drv, data):
216280
Internal callback for simulated touch input device.
217281
218282
This callback is registered with LVGL and provides touch state
219-
when simulate_click() is used.
283+
when simulate_click() is used. Not intended for direct use.
220284
221285
Args:
222286
indev_drv: Input device driver (LVGL internal)
@@ -237,6 +301,7 @@ def _ensure_touch_indev():
237301
238302
This is called automatically by simulate_click() on first use.
239303
Creates a pointer-type input device that uses _touch_read_cb.
304+
Not intended for direct use.
240305
"""
241306
global _touch_indev
242307
if _touch_indev is None:
@@ -255,7 +320,13 @@ def simulate_click(x, y, press_duration_ms=50):
255320
processed through LVGL's normal input handling, so it triggers
256321
click events, focus changes, scrolling, etc. just like real input.
257322
258-
To find object coordinates for clicking, use:
323+
Useful for:
324+
- Automated testing of UI interactions
325+
- Demo modes in apps
326+
- Accessibility automation
327+
- Integration testing
328+
329+
To find object coordinates for clicking:
259330
obj_area = lv.area_t()
260331
obj.get_coords(obj_area)
261332
center_x = (obj_area.x1 + obj_area.x2) // 2
@@ -268,13 +339,17 @@ def simulate_click(x, y, press_duration_ms=50):
268339
press_duration_ms: How long to hold the press (default: 50ms)
269340
270341
Example:
342+
from mpos.ui.testing import simulate_click, wait_for_render
343+
271344
# Click at screen center (320x240)
272345
simulate_click(160, 120)
346+
wait_for_render()
273347
274348
# Click on a specific button
275349
button_area = lv.area_t()
276-
button.get_coords(button_area)
350+
my_button.get_coords(button_area)
277351
simulate_click(button_area.x1 + 10, button_area.y1 + 10)
352+
wait_for_render()
278353
"""
279354
global _touch_x, _touch_y, _touch_pressed
280355

tests/test_graphical_abc_button_debug.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import unittest
1111
import lvgl as lv
1212
from mpos.ui.keyboard import MposKeyboard
13-
from graphical_test_helper import wait_for_render
13+
from mpos.ui.testing import wait_for_render
1414

1515

1616
class TestAbcButtonDebug(unittest.TestCase):

tests/test_graphical_about_app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import mpos.info
2222
import mpos.ui
2323
import os
24-
from graphical_test_helper import (
24+
from mpos.ui.testing import (
2525
wait_for_render,
2626
capture_screenshot,
2727
find_label_with_text,

tests/test_graphical_animation_deleted_widget.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import lvgl as lv
2020
import mpos.ui.anim
2121
import time
22-
from graphical_test_helper import wait_for_render
22+
from mpos.ui.testing import wait_for_render
2323

2424

2525
class TestAnimationDeletedWidget(unittest.TestCase):

tests/test_graphical_custom_keyboard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import sys
1515
import os
1616
from mpos.ui.keyboard import MposKeyboard
17-
from graphical_test_helper import (
17+
from mpos.ui.testing import (
1818
wait_for_render,
1919
capture_screenshot,
2020
)

tests/test_graphical_custom_keyboard_basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import unittest
1212
import lvgl as lv
1313
from mpos.ui.keyboard import MposKeyboard
14-
from graphical_test_helper import simulate_click, wait_for_render
14+
from mpos.ui.testing import simulate_click, wait_for_render
1515

1616

1717
class TestMposKeyboard(unittest.TestCase):

tests/test_graphical_keyboard_crash_reproduction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import unittest
1111
import lvgl as lv
1212
from mpos.ui.keyboard import MposKeyboard
13-
from graphical_test_helper import wait_for_render
13+
from mpos.ui.testing import wait_for_render
1414

1515

1616
class TestKeyboardCrash(unittest.TestCase):

tests/test_graphical_keyboard_default_vs_custom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import unittest
1212
import lvgl as lv
1313
from mpos.ui.keyboard import MposKeyboard
14-
from graphical_test_helper import wait_for_render
14+
from mpos.ui.testing import wait_for_render
1515

1616

1717
class TestDefaultVsCustomKeyboard(unittest.TestCase):

tests/test_graphical_keyboard_layout_switching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import unittest
1313
import lvgl as lv
1414
from mpos.ui.keyboard import MposKeyboard
15-
from graphical_test_helper import wait_for_render
15+
from mpos.ui.testing import wait_for_render
1616

1717

1818
class TestKeyboardLayoutSwitching(unittest.TestCase):

tests/test_graphical_keyboard_mode_switch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import unittest
1313
import lvgl as lv
1414
from mpos.ui.keyboard import MposKeyboard
15-
from graphical_test_helper import wait_for_render
15+
from mpos.ui.testing import wait_for_render
1616

1717

1818
class TestKeyboardModeSwitch(unittest.TestCase):

0 commit comments

Comments
 (0)