Skip to content

Commit be99f6e

Browse files
Fix tests
1 parent 08d1b28 commit be99f6e

File tree

3 files changed

+191
-75
lines changed

3 files changed

+191
-75
lines changed

internal_filesystem/lib/mpos/ui/testing.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,40 @@ def verify_text_present(obj, expected_text):
279279
return find_label_with_text(obj, expected_text) is not None
280280

281281

282+
def text_to_hex(text):
283+
"""
284+
Convert text to hex representation for debugging.
285+
286+
Useful for identifying Unicode symbols like lv.SYMBOL.SETTINGS
287+
which may not display correctly in terminal output.
288+
289+
Args:
290+
text: String to convert
291+
292+
Returns:
293+
str: Hex representation of the text bytes (UTF-8 encoded)
294+
295+
Example:
296+
>>> text_to_hex("⚙") # lv.SYMBOL.SETTINGS
297+
'e29a99'
298+
"""
299+
try:
300+
return text.encode('utf-8').hex()
301+
except:
302+
return "<encoding error>"
303+
304+
282305
def print_screen_labels(obj):
283306
"""
284307
Debug helper: Print all text found on screen from any widget.
285308
286309
Useful for debugging tests to see what text is actually present.
287310
Prints to stdout with numbered list. Includes text from labels,
288311
checkboxes, buttons, and any other widgets with text.
312+
313+
For each text, also prints the hex representation to help identify
314+
Unicode symbols (like lv.SYMBOL.SETTINGS) that may not display
315+
correctly in terminal output.
289316
290317
Args:
291318
obj: LVGL object to search (typically lv.screen_active())
@@ -295,16 +322,17 @@ def print_screen_labels(obj):
295322
print_screen_labels(lv.screen_active())
296323
# Output:
297324
# Found 5 text widgets on screen:
298-
# 0: MicroPythonOS
299-
# 1: Version 0.3.3
300-
# 2: Settings
301-
# 3: Force Update (checkbox)
302-
# 4: WiFi
325+
# 0: MicroPythonOS (hex: 4d6963726f507974686f6e4f53)
326+
# 1: Version 0.3.3 (hex: 56657273696f6e20302e332e33)
327+
# 2: ⚙ (hex: e29a99) <- lv.SYMBOL.SETTINGS
328+
# 3: Force Update (hex: 466f7263652055706461746)
329+
# 4: WiFi (hex: 57694669)
303330
"""
304331
texts = get_screen_text_content(obj)
305332
print(f"Found {len(texts)} text widgets on screen:")
306333
for i, text in enumerate(texts):
307-
print(f" {i}: {text}")
334+
hex_repr = text_to_hex(text)
335+
print(f" {i}: {text} (hex: {hex_repr})")
308336

309337

310338
def get_widget_coords(widget):

tests/test_graphical_camera_settings.py

Lines changed: 155 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,32 @@ def tearDown(self):
7272
except:
7373
pass # Already on launcher or error
7474

75+
def _find_and_click_settings_button(self, screen, use_send_event=True):
76+
"""Find and click the settings button using lv.SYMBOL.SETTINGS.
77+
78+
Args:
79+
screen: LVGL screen object to search
80+
use_send_event: If True (default), use send_event() which is more reliable.
81+
If False, use simulate_click() with coordinates.
82+
83+
Returns True if button was found and clicked, False otherwise.
84+
"""
85+
settings_button = find_button_with_text(screen, lv.SYMBOL.SETTINGS)
86+
if settings_button:
87+
coords = get_widget_coords(settings_button)
88+
print(f"Found settings button at ({coords['center_x']}, {coords['center_y']})")
89+
if use_send_event:
90+
# Use send_event for more reliable button triggering
91+
settings_button.send_event(lv.EVENT.CLICKED, None)
92+
print("Clicked settings button using send_event()")
93+
else:
94+
simulate_click(coords['center_x'], coords['center_y'], press_duration_ms=100)
95+
print("Clicked settings button using simulate_click()")
96+
return True
97+
else:
98+
print("Settings button not found via lv.SYMBOL.SETTINGS")
99+
return False
100+
75101
def test_settings_button_click_no_crash(self):
76102
"""
77103
Test that clicking the settings button doesn't cause a segfault.
@@ -83,7 +109,7 @@ def test_settings_button_click_no_crash(self):
83109
1. Start camera app
84110
2. Wait for camera to initialize
85111
3. Capture initial screenshot
86-
4. Click settings button (top-right corner)
112+
4. Click settings button (found dynamically by lv.SYMBOL.SETTINGS)
87113
5. Verify settings dialog opened
88114
6. If we get here without crash, test passes
89115
"""
@@ -108,18 +134,12 @@ def test_settings_button_click_no_crash(self):
108134
print(f"\nCapturing initial screenshot: {screenshot_path}")
109135
capture_screenshot(screenshot_path, width=320, height=240)
110136

111-
# Find and click settings button
112-
# The settings button is positioned at TOP_RIGHT with offset (0, 60)
113-
# On a 320x240 screen, this is approximately x=260, y=90
114-
# We'll click slightly inside the button to ensure we hit it
115-
settings_x = 300 # Right side of screen, inside the 60px button
116-
settings_y = 100 # 60px down from top, center of 60px button
137+
# Find and click settings button dynamically
138+
found = self._find_and_click_settings_button(screen)
139+
self.assertTrue(found, "Settings button with lv.SYMBOL.SETTINGS not found on screen")
117140

118-
print(f"\nClicking settings button at ({settings_x}, {settings_y})")
119-
simulate_click(settings_x, settings_y, press_duration_ms=100)
120-
121-
# Wait for settings dialog to appear
122-
wait_for_render(iterations=20)
141+
# Wait for settings dialog to appear - needs more time for Activity transition
142+
wait_for_render(iterations=50)
123143

124144
# Get screen again (might have changed after navigation)
125145
screen = lv.screen_active()
@@ -128,19 +148,26 @@ def test_settings_button_click_no_crash(self):
128148
print("\nScreen labels after clicking settings:")
129149
print_screen_labels(screen)
130150

131-
# Verify settings screen opened
132-
# Look for "Camera Settings" or "resolution" text
133-
has_settings_ui = (
134-
verify_text_present(screen, "Camera Settings") or
135-
verify_text_present(screen, "Resolution") or
136-
verify_text_present(screen, "resolution") or
137-
verify_text_present(screen, "Save") or
138-
verify_text_present(screen, "Cancel")
139-
)
151+
# Verify settings screen opened by looking for the Save button
152+
# This is more reliable than text search since buttons are always present
153+
save_button = find_button_with_text(screen, "Save")
154+
cancel_button = find_button_with_text(screen, "Cancel")
155+
156+
has_settings_ui = save_button is not None or cancel_button is not None
157+
158+
# Also try text-based verification as fallback
159+
if not has_settings_ui:
160+
has_settings_ui = (
161+
verify_text_present(screen, "Camera Settings") or
162+
verify_text_present(screen, "Resolution") or
163+
verify_text_present(screen, "resolution") or
164+
verify_text_present(screen, "Basic") or # Tab name
165+
verify_text_present(screen, "Color Mode") # Setting name
166+
)
140167

141168
self.assertTrue(
142169
has_settings_ui,
143-
"Settings screen did not open (no expected UI elements found)"
170+
"Settings screen did not open (no Save/Cancel buttons or expected UI elements found)"
144171
)
145172

146173
# Capture screenshot of settings dialog
@@ -151,15 +178,68 @@ def test_settings_button_click_no_crash(self):
151178
# If we got here without segfault, the test passes!
152179
print("\n✓ Settings button clicked successfully without crash!")
153180

181+
def _find_and_click_button(self, screen, text, use_send_event=True):
182+
"""Find and click a button by its text label.
183+
184+
Args:
185+
screen: LVGL screen object to search
186+
text: Text to search for in button labels
187+
use_send_event: If True (default), use send_event() which is more reliable.
188+
If False, use simulate_click() with coordinates.
189+
190+
Returns True if button was found and clicked, False otherwise.
191+
"""
192+
button = find_button_with_text(screen, text)
193+
if button:
194+
coords = get_widget_coords(button)
195+
print(f"Found '{text}' button at ({coords['center_x']}, {coords['center_y']})")
196+
if use_send_event:
197+
# Use send_event for more reliable button triggering
198+
button.send_event(lv.EVENT.CLICKED, None)
199+
print(f"Clicked '{text}' button using send_event()")
200+
else:
201+
simulate_click(coords['center_x'], coords['center_y'], press_duration_ms=100)
202+
print(f"Clicked '{text}' button using simulate_click()")
203+
return True
204+
else:
205+
print(f"Button with text '{text}' not found")
206+
return False
207+
208+
def _find_dropdown(self, screen):
209+
"""Find a dropdown widget on the screen.
210+
211+
Returns the dropdown widget or None if not found.
212+
"""
213+
def find_dropdown_recursive(obj):
214+
# Check if this object is a dropdown
215+
try:
216+
if obj.__class__.__name__ == 'dropdown' or hasattr(obj, 'get_selected'):
217+
# Verify it's actually a dropdown by checking for dropdown-specific method
218+
if hasattr(obj, 'get_options'):
219+
return obj
220+
except:
221+
pass
222+
223+
# Check children
224+
child_count = obj.get_child_count()
225+
for i in range(child_count):
226+
child = obj.get_child(i)
227+
result = find_dropdown_recursive(child)
228+
if result:
229+
return result
230+
return None
231+
232+
return find_dropdown_recursive(screen)
233+
154234
def test_resolution_change_no_crash(self):
155235
"""
156236
Test that changing resolution doesn't cause a crash.
157237
158238
This tests the full resolution change workflow:
159239
1. Start camera app
160-
2. Open settings
161-
3. Change resolution
162-
4. Save settings
240+
2. Open settings (found dynamically by lv.SYMBOL.SETTINGS)
241+
3. Change resolution via dropdown
242+
4. Save settings (found dynamically by "Save" text)
163243
5. Verify camera continues working
164244
165245
This verifies fixes for:
@@ -176,61 +256,63 @@ def test_resolution_change_no_crash(self):
176256
# Wait for camera to initialize
177257
wait_for_render(iterations=30)
178258

179-
# Click settings button
259+
# Click settings button dynamically
260+
screen = lv.screen_active()
180261
print("\nOpening settings...")
181-
simulate_click(290, 90, press_duration_ms=100)
262+
found = self._find_and_click_settings_button(screen)
263+
self.assertTrue(found, "Settings button with lv.SYMBOL.SETTINGS not found on screen")
182264
wait_for_render(iterations=20)
183265

184266
screen = lv.screen_active()
185267

186-
# Try to find the dropdown/resolution selector
187-
# The CameraSettingsActivity creates a dropdown widget
188-
# Let's look for any dropdown on screen
268+
# Try to find the dropdown/resolution selector dynamically
189269
print("\nLooking for resolution dropdown...")
270+
dropdown = self._find_dropdown(screen)
271+
272+
if dropdown:
273+
# Click the dropdown to open it
274+
coords = get_widget_coords(dropdown)
275+
print(f"Found dropdown at ({coords['center_x']}, {coords['center_y']})")
276+
simulate_click(coords['center_x'], coords['center_y'], press_duration_ms=100)
277+
wait_for_render(iterations=15)
278+
279+
# Get current selection and try to change it
280+
try:
281+
current = dropdown.get_selected()
282+
option_count = dropdown.get_option_count()
283+
print(f"Dropdown has {option_count} options, current selection: {current}")
284+
285+
# Select a different option (next one, or first if at end)
286+
new_selection = (current + 1) % option_count
287+
dropdown.set_selected(new_selection)
288+
print(f"Changed selection to: {new_selection}")
289+
except Exception as e:
290+
print(f"Could not change dropdown selection: {e}")
291+
# Fallback: click below current position to select different option
292+
simulate_click(coords['center_x'], coords['center_y'] + 30, press_duration_ms=100)
293+
else:
294+
print("Dropdown not found, test may not fully exercise resolution change")
190295

191-
# Find all clickable objects (dropdowns are clickable)
192-
# We'll try clicking in the middle area where the dropdown should be
193-
# Dropdown is typically centered, so around x=160, y=120
194-
dropdown_x = 160
195-
dropdown_y = 120
196-
197-
print(f"Clicking dropdown area at ({dropdown_x}, {dropdown_y})")
198-
simulate_click(dropdown_x, dropdown_y, press_duration_ms=100)
199296
wait_for_render(iterations=15)
200297

201-
# The dropdown should now be open showing resolution options
202-
# Let's capture what we see
298+
# Capture screenshot
203299
screenshot_path = f"{self.screenshot_dir}/camera_dropdown_open.raw"
204300
print(f"Capturing dropdown screenshot: {screenshot_path}")
205301
capture_screenshot(screenshot_path, width=320, height=240)
206302

207303
screen = lv.screen_active()
208-
print("\nScreen after opening dropdown:")
304+
print("\nScreen after dropdown interaction:")
209305
print_screen_labels(screen)
210306

211-
# Try to select a different resolution
212-
# Options are typically stacked vertically
213-
# Let's click a bit lower to select a different option
214-
option_x = 160
215-
option_y = 150 # Below the current selection
216-
217-
print(f"\nSelecting different resolution at ({option_x}, {option_y})")
218-
simulate_click(option_x, option_y, press_duration_ms=100)
219-
wait_for_render(iterations=15)
220-
221-
# Now find and click the Save button
307+
# Find and click the Save button dynamically
222308
print("\nLooking for Save button...")
223-
save_button = find_button_with_text(lv.screen_active(), "Save")
224-
225-
if save_button:
226-
coords = get_widget_coords(save_button)
227-
print(f"Found Save button at {coords}")
228-
simulate_click(coords['center_x'], coords['center_y'], press_duration_ms=100)
229-
else:
230-
# Fallback: Save button is typically at bottom-left
231-
# Based on CameraSettingsActivity code: ALIGN.BOTTOM_LEFT
232-
print("Save button not found via text, trying bottom-left corner")
233-
simulate_click(80, 220, press_duration_ms=100)
309+
save_found = self._find_and_click_button(lv.screen_active(), "Save")
310+
311+
if not save_found:
312+
# Try "OK" as alternative
313+
save_found = self._find_and_click_button(lv.screen_active(), "OK")
314+
315+
self.assertTrue(save_found, "Save/OK button not found on settings screen")
234316

235317
# Wait for reconfiguration to complete
236318
print("\nWaiting for reconfiguration...")
@@ -244,12 +326,18 @@ def test_resolution_change_no_crash(self):
244326
# If we got here without segfault, the test passes!
245327
print("\n✓ Resolution changed successfully without crash!")
246328

247-
# Verify camera is still showing something
329+
# Verify camera is still showing something by checking for camera UI elements
248330
screen = lv.screen_active()
249331
# The camera app should still be active (not crashed back to launcher)
250-
# We can check this by looking for camera-specific UI elements
251-
# or just the fact that we haven't crashed
252-
332+
# Check for camera-specific buttons (close, settings, snap, qr)
333+
has_camera_ui = (
334+
find_button_with_text(screen, lv.SYMBOL.CLOSE) or
335+
find_button_with_text(screen, lv.SYMBOL.SETTINGS) or
336+
find_button_with_text(screen, lv.SYMBOL.OK) or
337+
find_button_with_text(screen, lv.SYMBOL.EYE_OPEN)
338+
)
339+
340+
self.assertTrue(has_camera_ui, "Camera app UI not found after resolution change - app may have crashed")
253341
print("\n✓ Camera app still running after resolution change!")
254342

255343

tests/test_graphical_imu_calibration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def test_calibrate_activity_flow(self):
125125
# Click "Calibrate Now" button to start calibration
126126
calibrate_btn = find_button_with_text(screen, "Calibrate Now")
127127
self.assertIsNotNone(calibrate_btn, "Could not find 'Calibrate Now' button")
128-
coords = get_widget_coords(calibrate_btn)
129-
simulate_click(coords['center_x'], coords['center_y'])
128+
# Use send_event instead of simulate_click (more reliable)
129+
calibrate_btn.send_event(lv.EVENT.CLICKED, None)
130130
wait_for_render(10)
131131

132132
# Wait for calibration to complete (mock takes ~3 seconds)

0 commit comments

Comments
 (0)