Files
MicroPythonOS/tests/test_graphical_launch_all_apps.py
T
Thomas Farstrike 09f9775903 Don't launch com.micropythonos.breakout for now
The compiled .mpy are not pushed.
2026-03-22 23:34:55 +01:00

269 lines
9.6 KiB
Python

"""
Test that launches all installed apps to check for startup errors.
This test discovers all apps in apps/ and builtin/apps/ directories,
launches each one, and checks for exceptions during startup.
"""
import unittest
import os
import sys
import time
# This is a graphical test - needs boot and main to run first
# Add tests directory to path for helpers
from mpos import wait_for_render, ui, AppManager
class TestLaunchAllApps(unittest.TestCase):
"""Test launching all installed apps."""
def setUp(self):
"""Set up test fixtures."""
self.apps_to_test = []
self.app_errors = {}
# Discover all apps
self._discover_apps()
def _discover_apps(self):
"""Discover all installed apps."""
# Use PackageManager to get all apps
all_packages = AppManager.get_app_list()
skipped_packages = {
'com.micropythonos.breakout',
'com.micropythonos.doom',
'cz.ucw.pavel.cellular',
'cz.ucw.pavel.compass',
'cz.ucw.pavel.navstar',
}
for package in all_packages:
# Skip apps that should not be launched by this test
if package.fullname in skipped_packages:
continue
# Get the main activity for each app
if package.activities:
# Use first activity as the main one (activities are dicts)
main_activity = package.activities[0]
self.apps_to_test.append({
'package_name': package.fullname,
'activity_name': main_activity.get('classname', 'MainActivity'),
'label': package.name
})
def test_launch_all_apps(self):
"""Launch each app and check for errors."""
print(f"\n{'='*60}")
print(f"Testing {len(self.apps_to_test)} apps for startup errors")
print(f"{'='*60}\n")
failed_apps = []
passed_apps = []
for i, app_info in enumerate(self.apps_to_test, 1):
package_name = app_info['package_name']
activity_name = app_info['activity_name']
label = app_info['label']
print(f"\n[{i}/{len(self.apps_to_test)}] Testing: {label} ({package_name})")
error_found = False
error_message = ""
try:
# Launch the app by package name
result = AppManager.start_app(package_name)
# Wait for UI to render
wait_for_render(iterations=5)
# Check if start_app returned False (indicates error during execution)
if result is False:
error_found = True
error_message = "App failed to start (execute_script returned False)"
print(f" ❌ FAILED - App failed to start")
print(f" {error_message}")
failed_apps.append({
'info': app_info,
'error': error_message
})
else:
# If we got here without error, the app loaded successfully
print(f" ✓ PASSED - App loaded successfully")
passed_apps.append(app_info)
# Navigate back to exit the app
mpos.ui.back_screen()
wait_for_render(iterations=3)
except Exception as e:
error_found = True
error_message = f"{type(e).__name__}: {str(e)}"
print(f" ❌ FAILED - Exception during launch")
print(f" {error_message}")
failed_apps.append({
'info': app_info,
'error': error_message
})
# Print summary
print(f"\n{'='*60}")
print(f"Test Summary")
print(f"{'='*60}")
print(f"Total apps tested: {len(self.apps_to_test)}")
print(f"Passed: {len(passed_apps)}")
print(f"Failed: {len(failed_apps)}")
print(f"{'='*60}\n")
if failed_apps:
print("Failed apps:")
for fail in failed_apps:
print(f" - {fail['info']['label']} ({fail['info']['package_name']})")
print(f" Error: {fail['error']}")
print()
# Separate errortest failures from other failures
errortest_failures = [
fail for fail in failed_apps
if 'errortest' in fail['info']['package_name'].lower()
]
# On macOS, musicplayer is known to fail due to @micropython.viper issue
# and camera app fails due to no camera hardware
is_macos = sys.platform == 'darwin'
musicplayer_failures = [
fail for fail in failed_apps
if fail['info']['package_name'] == 'com.micropythonos.musicplayer' and is_macos
]
camera_failures = [
fail for fail in failed_apps
if fail['info']['package_name'] == 'com.micropythonos.camera' and is_macos
]
other_failures = [
fail for fail in failed_apps
if 'errortest' not in fail['info']['package_name'].lower() and
not (fail['info']['package_name'] == 'com.micropythonos.musicplayer' and is_macos) and
not (fail['info']['package_name'] == 'com.micropythonos.breakout' and is_macos) and
not (fail['info']['package_name'] == 'com.micropythonos.camera' and is_macos)
]
# Check if errortest app exists
all_app_names = [app['package_name'] for app in self.apps_to_test]
has_errortest = any('errortest' in name.lower() for name in all_app_names)
# Verify errortest app fails if it exists
if has_errortest:
self.assertTrue(len(errortest_failures) > 0,
"Failed to detect error in com.micropythonos.errortest app")
print("✓ Successfully detected the intentional error in errortest app")
# Report on musicplayer failures on macOS (known issue)
if musicplayer_failures:
print("⚠ Skipped musicplayer failure on macOS (known @micropython.viper issue)")
# Report on camera failures on macOS (no camera hardware)
if camera_failures:
print("⚠ Skipped camera app failure on macOS (no camera hardware available)")
# Fail the test if any non-errortest apps have errors
if other_failures:
print(f"\n❌ FAIL: {len(other_failures)} non-errortest app(s) have errors:")
for fail in other_failures:
print(f" - {fail['info']['label']} ({fail['info']['package_name']})")
print(f" Error: {fail['error']}")
self.fail(f"{len(other_failures)} app(s) failed to launch (excluding errortest)")
else:
print("✓ All non-errortest apps launched successfully")
class TestLaunchSpecificApps(unittest.TestCase):
"""Test specific apps individually for more detailed error reporting."""
def _launch_and_check_app(self, package_name, expected_error=False):
"""
Launch an app and check for errors.
Args:
package_name: Full package name (e.g., 'com.micropythonos.camera')
expected_error: Whether this app is expected to have errors
Returns:
tuple: (success, error_message)
"""
error_found = False
error_message = ""
try:
# Launch the app by package name
result = AppManager.start_app(package_name)
wait_for_render(iterations=5)
# Check if start_app returned False (indicates error)
if result is False:
error_found = True
error_message = "App failed to start (execute_script returned False)"
# Navigate back
mpos.ui.back_screen()
wait_for_render(iterations=3)
except Exception as e:
error_found = True
error_message = f"{type(e).__name__}: {str(e)}"
if expected_error:
# For apps expected to have errors
return (error_found, error_message)
else:
# For apps that should work
return (not error_found, error_message)
def test_errortest_app_has_error(self):
"""Test that the errortest app properly reports an error."""
success, error_msg = self._launch_and_check_app(
'com.micropythonos.errortest',
expected_error=True
)
if success:
print(f"\n✓ Successfully detected error in errortest app:")
print(f" {error_msg}")
else:
print(f"\n❌ Failed to detect error in errortest app")
self.assertTrue(success,
"The errortest app should have an error but none was detected")
def test_launcher_app_loads(self):
"""Test that the launcher app loads without errors."""
success, error_msg = self._launch_and_check_app(
'com.micropythonos.launcher',
expected_error=False
)
if not success:
print(f"\n❌ Launcher app has errors: {error_msg}")
self.assertTrue(success,
f"Launcher app should load without errors: {error_msg}")
def test_about_app_loads(self):
"""Test that the About app loads without errors."""
success, error_msg = self._launch_and_check_app(
'com.micropythonos.about',
expected_error=False
)
if not success:
print(f"\n❌ About app has errors: {error_msg}")
self.assertTrue(success,
f"About app should load without errors: {error_msg}")