You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
7e4585e91e
It's a bit unstable, as it crashes if the settings button is clicked after startup, but not when closing and then re-opening. Seems to work for 640x480, including QR decoding.
259 lines
9.3 KiB
Python
259 lines
9.3 KiB
Python
"""
|
|
Graphical test for Camera app settings functionality.
|
|
|
|
This test verifies that:
|
|
1. The camera app settings button can be clicked without crashing
|
|
2. The settings dialog opens correctly
|
|
3. Resolution can be changed without causing segfault
|
|
4. The camera continues to work after resolution change
|
|
|
|
This specifically tests the fixes for:
|
|
- Segfault when clicking settings button
|
|
- Pale colors after resolution change
|
|
- Buffer size mismatches
|
|
|
|
Usage:
|
|
Desktop: ./tests/unittest.sh tests/test_graphical_camera_settings.py
|
|
Device: ./tests/unittest.sh tests/test_graphical_camera_settings.py --ondevice
|
|
"""
|
|
|
|
import unittest
|
|
import lvgl as lv
|
|
import mpos.apps
|
|
import mpos.ui
|
|
import os
|
|
from mpos.ui.testing import (
|
|
wait_for_render,
|
|
capture_screenshot,
|
|
find_label_with_text,
|
|
find_button_with_text,
|
|
verify_text_present,
|
|
print_screen_labels,
|
|
simulate_click,
|
|
get_widget_coords
|
|
)
|
|
|
|
|
|
class TestGraphicalCameraSettings(unittest.TestCase):
|
|
"""Test suite for Camera app settings verification."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures before each test method."""
|
|
# Check if webcam module is available
|
|
try:
|
|
import webcam
|
|
self.has_webcam = True
|
|
except:
|
|
try:
|
|
import camera
|
|
self.has_webcam = False # Has internal camera instead
|
|
except:
|
|
self.skipTest("No camera module available (webcam or internal)")
|
|
|
|
# Get absolute path to screenshots directory
|
|
import sys
|
|
if sys.platform == "esp32":
|
|
self.screenshot_dir = "tests/screenshots"
|
|
else:
|
|
self.screenshot_dir = "../tests/screenshots"
|
|
|
|
# Ensure screenshots directory exists
|
|
try:
|
|
os.mkdir(self.screenshot_dir)
|
|
except OSError:
|
|
pass # Directory already exists
|
|
|
|
def tearDown(self):
|
|
"""Clean up after each test method."""
|
|
# Navigate back to launcher (closes the camera app)
|
|
try:
|
|
mpos.ui.back_screen()
|
|
wait_for_render(10) # Allow navigation and cleanup to complete
|
|
except:
|
|
pass # Already on launcher or error
|
|
|
|
def test_settings_button_click_no_crash(self):
|
|
"""
|
|
Test that clicking the settings button doesn't cause a segfault.
|
|
|
|
This is the critical test that verifies the fix for the segfault
|
|
that occurred when clicking settings due to stale image_dsc.data pointer.
|
|
|
|
Steps:
|
|
1. Start camera app
|
|
2. Wait for camera to initialize
|
|
3. Capture initial screenshot
|
|
4. Click settings button (top-right corner)
|
|
5. Verify settings dialog opened
|
|
6. If we get here without crash, test passes
|
|
"""
|
|
print("\n=== Testing settings button click (no crash) ===")
|
|
|
|
# Start the Camera app
|
|
result = mpos.apps.start_app("com.micropythonos.camera")
|
|
self.assertTrue(result, "Failed to start Camera app")
|
|
|
|
# Wait for camera to initialize and first frame to render
|
|
wait_for_render(iterations=30)
|
|
|
|
# Get current screen
|
|
screen = lv.screen_active()
|
|
|
|
# Debug: Print all text on screen
|
|
print("\nInitial screen labels:")
|
|
print_screen_labels(screen)
|
|
|
|
# Capture screenshot before clicking settings
|
|
screenshot_path = f"{self.screenshot_dir}/camera_before_settings.raw"
|
|
print(f"\nCapturing initial screenshot: {screenshot_path}")
|
|
capture_screenshot(screenshot_path, width=320, height=240)
|
|
|
|
# Find and click settings button
|
|
# The settings button is positioned at TOP_RIGHT with offset (0, 60)
|
|
# On a 320x240 screen, this is approximately x=260, y=90
|
|
# We'll click slightly inside the button to ensure we hit it
|
|
settings_x = 290 # Right side of screen, inside the 60px button
|
|
settings_y = 90 # 60px down from top, center of 60px button
|
|
|
|
print(f"\nClicking settings button at ({settings_x}, {settings_y})")
|
|
simulate_click(settings_x, settings_y, press_duration_ms=100)
|
|
|
|
# Wait for settings dialog to appear
|
|
wait_for_render(iterations=20)
|
|
|
|
# Get screen again (might have changed after navigation)
|
|
screen = lv.screen_active()
|
|
|
|
# Debug: Print labels after clicking
|
|
print("\nScreen labels after clicking settings:")
|
|
print_screen_labels(screen)
|
|
|
|
# Verify settings screen opened
|
|
# Look for "Camera Settings" or "resolution" text
|
|
has_settings_ui = (
|
|
verify_text_present(screen, "Camera Settings") or
|
|
verify_text_present(screen, "Resolution") or
|
|
verify_text_present(screen, "resolution") or
|
|
verify_text_present(screen, "Save") or
|
|
verify_text_present(screen, "Cancel")
|
|
)
|
|
|
|
self.assertTrue(
|
|
has_settings_ui,
|
|
"Settings screen did not open (no expected UI elements found)"
|
|
)
|
|
|
|
# Capture screenshot of settings dialog
|
|
screenshot_path = f"{self.screenshot_dir}/camera_settings_dialog.raw"
|
|
print(f"\nCapturing settings dialog screenshot: {screenshot_path}")
|
|
capture_screenshot(screenshot_path, width=320, height=240)
|
|
|
|
# If we got here without segfault, the test passes!
|
|
print("\n✓ Settings button clicked successfully without crash!")
|
|
|
|
def test_resolution_change_no_crash(self):
|
|
"""
|
|
Test that changing resolution doesn't cause a crash.
|
|
|
|
This tests the full resolution change workflow:
|
|
1. Start camera app
|
|
2. Open settings
|
|
3. Change resolution
|
|
4. Save settings
|
|
5. Verify camera continues working
|
|
|
|
This verifies fixes for:
|
|
- Segfault during reconfiguration
|
|
- Buffer size mismatches
|
|
- Stale data pointers
|
|
"""
|
|
print("\n=== Testing resolution change (no crash) ===")
|
|
|
|
# Start the Camera app
|
|
result = mpos.apps.start_app("com.micropythonos.camera")
|
|
self.assertTrue(result, "Failed to start Camera app")
|
|
|
|
# Wait for camera to initialize
|
|
wait_for_render(iterations=30)
|
|
|
|
# Click settings button
|
|
print("\nOpening settings...")
|
|
simulate_click(290, 90, press_duration_ms=100)
|
|
wait_for_render(iterations=20)
|
|
|
|
screen = lv.screen_active()
|
|
|
|
# Try to find the dropdown/resolution selector
|
|
# The CameraSettingsActivity creates a dropdown widget
|
|
# Let's look for any dropdown on screen
|
|
print("\nLooking for resolution dropdown...")
|
|
|
|
# Find all clickable objects (dropdowns are clickable)
|
|
# We'll try clicking in the middle area where the dropdown should be
|
|
# Dropdown is typically centered, so around x=160, y=120
|
|
dropdown_x = 160
|
|
dropdown_y = 120
|
|
|
|
print(f"Clicking dropdown area at ({dropdown_x}, {dropdown_y})")
|
|
simulate_click(dropdown_x, dropdown_y, press_duration_ms=100)
|
|
wait_for_render(iterations=15)
|
|
|
|
# The dropdown should now be open showing resolution options
|
|
# Let's capture what we see
|
|
screenshot_path = f"{self.screenshot_dir}/camera_dropdown_open.raw"
|
|
print(f"Capturing dropdown screenshot: {screenshot_path}")
|
|
capture_screenshot(screenshot_path, width=320, height=240)
|
|
|
|
screen = lv.screen_active()
|
|
print("\nScreen after opening dropdown:")
|
|
print_screen_labels(screen)
|
|
|
|
# Try to select a different resolution
|
|
# Options are typically stacked vertically
|
|
# Let's click a bit lower to select a different option
|
|
option_x = 160
|
|
option_y = 150 # Below the current selection
|
|
|
|
print(f"\nSelecting different resolution at ({option_x}, {option_y})")
|
|
simulate_click(option_x, option_y, press_duration_ms=100)
|
|
wait_for_render(iterations=15)
|
|
|
|
# Now find and click the Save button
|
|
print("\nLooking for Save button...")
|
|
save_button = find_button_with_text(lv.screen_active(), "Save")
|
|
|
|
if save_button:
|
|
coords = get_widget_coords(save_button)
|
|
print(f"Found Save button at {coords}")
|
|
simulate_click(coords['center_x'], coords['center_y'], press_duration_ms=100)
|
|
else:
|
|
# Fallback: Save button is typically at bottom-left
|
|
# Based on CameraSettingsActivity code: ALIGN.BOTTOM_LEFT
|
|
print("Save button not found via text, trying bottom-left corner")
|
|
simulate_click(80, 220, press_duration_ms=100)
|
|
|
|
# Wait for reconfiguration to complete
|
|
print("\nWaiting for reconfiguration...")
|
|
wait_for_render(iterations=30)
|
|
|
|
# Capture screenshot after reconfiguration
|
|
screenshot_path = f"{self.screenshot_dir}/camera_after_resolution_change.raw"
|
|
print(f"Capturing post-change screenshot: {screenshot_path}")
|
|
capture_screenshot(screenshot_path, width=320, height=240)
|
|
|
|
# If we got here without segfault, the test passes!
|
|
print("\n✓ Resolution changed successfully without crash!")
|
|
|
|
# Verify camera is still showing something
|
|
screen = lv.screen_active()
|
|
# The camera app should still be active (not crashed back to launcher)
|
|
# We can check this by looking for camera-specific UI elements
|
|
# or just the fact that we haven't crashed
|
|
|
|
print("\n✓ Camera app still running after resolution change!")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Note: Don't include unittest.main() - handled by unittest.sh
|
|
pass
|