Skip to content

Commit b6869d5

Browse files
Add more tests
1 parent 5827d40 commit b6869d5

File tree

4 files changed

+268
-1
lines changed

4 files changed

+268
-1
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "ErrorTest",
3+
"publisher": "MicroPythonOS",
4+
"short_description": "Test app with intentional error",
5+
"long_description": "This app has an intentional import error for testing.",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.errortest/icons/com.micropythonos.errortest_0.0.1_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.errortest/mpks/com.micropythonos.errortest_0.0.1.mpk",
8+
"fullname": "com.micropythonos.errortest",
9+
"version": "0.0.1",
10+
"category": "development",
11+
"activities": [
12+
{
13+
"entrypoint": "assets/error.py",
14+
"classname": "Error",
15+
"intent_filters": [
16+
{
17+
"action": "main",
18+
"category": "launcher"
19+
}
20+
]
21+
}
22+
]
23+
}
24+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from mpos.apps import ActivityDoesntExist # should fail here
2+
3+
class Error(Activity):
4+
5+
def onCreate(self):
6+
screen = lv.obj()
7+
label = lv.label(screen)
8+
label.set_text('Hello World!')
9+
label.center()
10+
self.setContentView(screen)

scripts/bundle_apps.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ rm "$outputjson"
2020
# com.micropythonos.confetti crashes when closing
2121
# com.micropythonos.showfonts is slow to open
2222
# com.micropythonos.draw isnt very useful
23-
blacklist="com.micropythonos.filemanager com.quasikili.quasidoodle com.micropythonos.confetti com.micropythonos.showfonts com.micropythonos.draw"
23+
# com.micropythonos.errortest is an intentional bad app for testing (caught by tests/test_graphical_launch_all_apps.py)
24+
blacklist="com.micropythonos.filemanager com.quasikili.quasidoodle com.micropythonos.confetti com.micropythonos.showfonts com.micropythonos.draw com.micropythonos.errortest"
2425

2526
echo "[" | tee -a "$outputjson"
2627

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
"""
2+
Test that launches all installed apps to check for startup errors.
3+
4+
This test discovers all apps in apps/ and builtin/apps/ directories,
5+
launches each one, and checks for exceptions during startup.
6+
"""
7+
8+
import unittest
9+
import os
10+
import sys
11+
import time
12+
13+
# This is a graphical test - needs boot and main to run first
14+
# Add tests directory to path for helpers
15+
if '../tests' not in sys.path:
16+
sys.path.insert(0, '../tests')
17+
18+
from graphical_test_helper import wait_for_render
19+
import mpos.apps
20+
import mpos.ui
21+
from mpos.content.package_manager import PackageManager
22+
23+
24+
class TestLaunchAllApps(unittest.TestCase):
25+
"""Test launching all installed apps."""
26+
27+
def setUp(self):
28+
"""Set up test fixtures."""
29+
self.apps_to_test = []
30+
self.app_errors = {}
31+
32+
# Discover all apps
33+
self._discover_apps()
34+
35+
def _discover_apps(self):
36+
"""Discover all installed apps."""
37+
# Use PackageManager to get all apps
38+
all_packages = PackageManager.get_app_list()
39+
40+
for package in all_packages:
41+
# Get the main activity for each app
42+
if package.activities:
43+
# Use first activity as the main one (activities are dicts)
44+
main_activity = package.activities[0]
45+
self.apps_to_test.append({
46+
'package_name': package.fullname,
47+
'activity_name': main_activity.get('classname', 'MainActivity'),
48+
'label': package.name
49+
})
50+
51+
def test_launch_all_apps(self):
52+
"""Launch each app and check for errors."""
53+
print(f"\n{'='*60}")
54+
print(f"Testing {len(self.apps_to_test)} apps for startup errors")
55+
print(f"{'='*60}\n")
56+
57+
failed_apps = []
58+
passed_apps = []
59+
60+
for i, app_info in enumerate(self.apps_to_test, 1):
61+
package_name = app_info['package_name']
62+
activity_name = app_info['activity_name']
63+
label = app_info['label']
64+
65+
print(f"\n[{i}/{len(self.apps_to_test)}] Testing: {label} ({package_name})")
66+
67+
error_found = False
68+
error_message = ""
69+
70+
try:
71+
# Launch the app by package name
72+
result = mpos.apps.start_app(package_name)
73+
74+
# Wait for UI to render
75+
wait_for_render(iterations=5)
76+
77+
# Check if start_app returned False (indicates error during execution)
78+
if result is False:
79+
error_found = True
80+
error_message = "App failed to start (execute_script returned False)"
81+
print(f" ❌ FAILED - App failed to start")
82+
print(f" {error_message}")
83+
failed_apps.append({
84+
'info': app_info,
85+
'error': error_message
86+
})
87+
else:
88+
# If we got here without error, the app loaded successfully
89+
print(f" ✓ PASSED - App loaded successfully")
90+
passed_apps.append(app_info)
91+
92+
# Navigate back to exit the app
93+
mpos.ui.back_screen()
94+
wait_for_render(iterations=3)
95+
96+
except Exception as e:
97+
error_found = True
98+
error_message = f"{type(e).__name__}: {str(e)}"
99+
print(f" ❌ FAILED - Exception during launch")
100+
print(f" {error_message}")
101+
failed_apps.append({
102+
'info': app_info,
103+
'error': error_message
104+
})
105+
106+
# Print summary
107+
print(f"\n{'='*60}")
108+
print(f"Test Summary")
109+
print(f"{'='*60}")
110+
print(f"Total apps tested: {len(self.apps_to_test)}")
111+
print(f"Passed: {len(passed_apps)}")
112+
print(f"Failed: {len(failed_apps)}")
113+
print(f"{'='*60}\n")
114+
115+
if failed_apps:
116+
print("Failed apps:")
117+
for fail in failed_apps:
118+
print(f" - {fail['info']['label']} ({fail['info']['package_name']})")
119+
print(f" Error: {fail['error']}")
120+
print()
121+
122+
# Test should detect at least one error (the intentional errortest app)
123+
if len(failed_apps) > 0:
124+
print(f"✓ Test successfully detected {len(failed_apps)} app(s) with errors")
125+
126+
# Check if we found the errortest app
127+
errortest_found = any(
128+
'errortest' in fail['info']['package_name'].lower()
129+
for fail in failed_apps
130+
)
131+
132+
if errortest_found:
133+
print("✓ Successfully detected the intentional error in errortest app")
134+
135+
# Verify errortest was found
136+
all_app_names = [app['package_name'] for app in self.apps_to_test]
137+
has_errortest = any('errortest' in name.lower() for name in all_app_names)
138+
139+
if has_errortest:
140+
self.assertTrue(errortest_found,
141+
"Failed to detect error in com.micropythonos.errortest app")
142+
else:
143+
print("⚠ Warning: No errors detected. All apps launched successfully.")
144+
145+
146+
class TestLaunchSpecificApps(unittest.TestCase):
147+
"""Test specific apps individually for more detailed error reporting."""
148+
149+
def _launch_and_check_app(self, package_name, expected_error=False):
150+
"""
151+
Launch an app and check for errors.
152+
153+
Args:
154+
package_name: Full package name (e.g., 'com.micropythonos.camera')
155+
expected_error: Whether this app is expected to have errors
156+
157+
Returns:
158+
tuple: (success, error_message)
159+
"""
160+
error_found = False
161+
error_message = ""
162+
163+
try:
164+
# Launch the app by package name
165+
result = mpos.apps.start_app(package_name)
166+
wait_for_render(iterations=5)
167+
168+
# Check if start_app returned False (indicates error)
169+
if result is False:
170+
error_found = True
171+
error_message = "App failed to start (execute_script returned False)"
172+
173+
# Navigate back
174+
mpos.ui.back_screen()
175+
wait_for_render(iterations=3)
176+
177+
except Exception as e:
178+
error_found = True
179+
error_message = f"{type(e).__name__}: {str(e)}"
180+
181+
if expected_error:
182+
# For apps expected to have errors
183+
return (error_found, error_message)
184+
else:
185+
# For apps that should work
186+
return (not error_found, error_message)
187+
188+
def test_errortest_app_has_error(self):
189+
"""Test that the errortest app properly reports an error."""
190+
success, error_msg = self._launch_and_check_app(
191+
'com.micropythonos.errortest',
192+
expected_error=True
193+
)
194+
195+
if success:
196+
print(f"\n✓ Successfully detected error in errortest app:")
197+
print(f" {error_msg}")
198+
else:
199+
print(f"\n❌ Failed to detect error in errortest app")
200+
201+
self.assertTrue(success,
202+
"The errortest app should have an error but none was detected")
203+
204+
def test_launcher_app_loads(self):
205+
"""Test that the launcher app loads without errors."""
206+
success, error_msg = self._launch_and_check_app(
207+
'com.micropythonos.launcher',
208+
expected_error=False
209+
)
210+
211+
if not success:
212+
print(f"\n❌ Launcher app has errors: {error_msg}")
213+
214+
self.assertTrue(success,
215+
f"Launcher app should load without errors: {error_msg}")
216+
217+
def test_about_app_loads(self):
218+
"""Test that the About app loads without errors."""
219+
success, error_msg = self._launch_and_check_app(
220+
'com.micropythonos.about',
221+
expected_error=False
222+
)
223+
224+
if not success:
225+
print(f"\n❌ About app has errors: {error_msg}")
226+
227+
self.assertTrue(success,
228+
f"About app should load without errors: {error_msg}")
229+
230+
231+
if __name__ == '__main__':
232+
unittest.main()

0 commit comments

Comments
 (0)