From 31dcfba683b50ac078eb1e592aa8e5aaa20fbcf2 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Sun, 25 Jan 2026 00:08:01 +0100 Subject: [PATCH] Move mpos.apps functionality to PackageManager --- .../assets/launcher.py | 3 +- internal_filesystem/lib/mpos/__init__.py | 3 +- internal_filesystem/lib/mpos/apps.py | 115 ------------------ .../lib/mpos/content/package_manager.py | 113 +++++++++++++++++ internal_filesystem/lib/mpos/main.py | 5 +- internal_filesystem/lib/mpos/task_manager.py | 1 - internal_filesystem/lib/mpos/testing/mocks.py | 44 ++++++- internal_filesystem/lib/mpos/ui/testing.py | 6 +- internal_filesystem/lib/mpos/ui/topmenu.py | 9 +- internal_filesystem/lib/mpos/ui/util.py | 1 - internal_filesystem/lib/mpos/ui/view.py | 1 - internal_filesystem/lib/threading.py | 1 - tests/manual_test_nostr_asyncio.py | 1 - tests/test_audioflinger.py | 2 - tests/test_graphical_about_app.py | 8 +- tests/test_graphical_camera_settings.py | 8 +- tests/test_graphical_imu_calibration.py | 10 +- .../test_graphical_imu_calibration_ui_bug.py | 6 +- tests/test_graphical_launch_all_apps.py | 6 +- tests/test_graphical_osupdate.py | 25 ++-- tests/test_graphical_start_app.py | 9 +- tests/test_syspath_restore.py | 16 +-- tests/test_websocket.py | 1 - tests/unittest.sh | 2 +- 24 files changed, 211 insertions(+), 185 deletions(-) delete mode 100644 internal_filesystem/lib/mpos/apps.py diff --git a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py index 952dfb9c..7e85b91e 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py @@ -9,7 +9,6 @@ # All icons took: 1250ms # Most of this time is actually spent reading and parsing manifests. import lvgl as lv -import mpos.apps from mpos import AppearanceManager, PackageManager, Activity, DisplayMetrics import time import uhashlib @@ -129,7 +128,7 @@ class Launcher(Activity): # ----- events -------------------------------------------------- app_cont.add_event_cb( - lambda e, fullname=app.fullname: mpos.apps.start_app(fullname), + lambda e, fullname=app.fullname: PackageManager.start_app(fullname), lv.EVENT.CLICKED, None) app_cont.add_event_cb( lambda e, cont=app_cont: self.focus_app_cont(cont), diff --git a/internal_filesystem/lib/mpos/__init__.py b/internal_filesystem/lib/mpos/__init__.py index 858f6c35..8552fe8a 100644 --- a/internal_filesystem/lib/mpos/__init__.py +++ b/internal_filesystem/lib/mpos/__init__.py @@ -46,7 +46,6 @@ from .ui.widget_animator import WidgetAnimator from .ui import focus_direction # Utility modules -from . import apps from . import bootloader from . import ui from . import config @@ -91,7 +90,7 @@ __all__ = [ "click_button", "click_label", "click_keyboard_button", "find_button_with_text", "get_all_widgets_with_text", # Submodules - "apps", "ui", "config", "net", "content", "time", "sensor_manager", + "ui", "config", "net", "content", "time", "sensor_manager", "camera_manager", "sdcard", "battery_voltage", "audio", "hardware", "bootloader", # Timezone utilities "TimeZone" diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py deleted file mode 100644 index 10ae498a..00000000 --- a/internal_filesystem/lib/mpos/apps.py +++ /dev/null @@ -1,115 +0,0 @@ -import lvgl as lv - -import _thread -import traceback - -import mpos.ui - -# Run the script in the current thread: -# Returns True if successful -def execute_script(script_source, is_file, classname, cwd=None): - import utime # for timing read and compile - thread_id = _thread.get_ident() - compile_name = 'script' if not is_file else script_source - print(f"Thread {thread_id}: executing script with cwd: {cwd}") - try: - if is_file: - print(f"Thread {thread_id}: reading script from file {script_source}") - with open(script_source, 'r') as f: # No need to check if it exists as exceptions are caught - start_time = utime.ticks_ms() - script_source = f.read() - read_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"execute_script: reading script_source took {read_time}ms") - script_globals = { - 'lv': lv, - '__name__': "__main__" - } - print(f"Thread {thread_id}: starting script") - import sys - path_before = sys.path[:] # Make a copy, not a reference - if cwd: - sys.path.append(cwd) - try: - start_time = utime.ticks_ms() - compiled_script = compile(script_source, compile_name, 'exec') - compile_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"execute_script: compiling script_source took {compile_time}ms") - start_time = utime.ticks_ms() - exec(compiled_script, script_globals) - end_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"apps.py execute_script: exec took {end_time}ms") - # Introspect globals - classes = {k: v for k, v in script_globals.items() if isinstance(v, type)} - functions = {k: v for k, v in script_globals.items() if callable(v) and not isinstance(v, type)} - variables = {k: v for k, v in script_globals.items() if not callable(v)} - print("Classes:", classes.keys()) # This lists a whole bunch of classes, including lib/mpos/ stuff - print("Functions:", functions.keys()) - print("Variables:", variables.keys()) - main_activity = script_globals.get(classname) - if main_activity: - from .app.activity import Activity - from .content.intent import Intent - start_time = utime.ticks_ms() - Activity.startActivity(None, Intent(activity_class=main_activity)) - end_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"execute_script: Activity.startActivity took {end_time}ms") - else: - print(f"Warning: could not find app's main_activity {classname}") - return False - except Exception as e: - print(f"Thread {thread_id}: exception during execution:") - # Print stack trace with exception type, value, and traceback - tb = getattr(e, '__traceback__', None) - traceback.print_exception(type(e), e, tb) - return False - finally: - # Always restore sys.path, even if we return early or raise an exception - print(f"Thread {thread_id}: script {compile_name} finished, restoring sys.path from {sys.path} to {path_before}") - sys.path = path_before - return True - except Exception as e: - print(f"Thread {thread_id}: error:") - tb = getattr(e, '__traceback__', None) - traceback.print_exception(type(e), e, tb) - return False - -# Returns True if successful -def start_app(fullname): - from .content.package_manager import PackageManager - mpos.ui.set_foreground_app(fullname) - import utime - start_time = utime.ticks_ms() - app = PackageManager.get(fullname) - if not app: - print(f"Warning: start_app can't find app {fullname}") - return - if not app.installed_path: - print(f"Warning: start_app can't start {fullname} because no it doesn't have an installed_path") - return - entrypoint = "assets/main.py" - classname = "Main" - if not app.main_launcher_activity: - print(f"WARNING: app {fullname} doesn't have a main_launcher_activity, defaulting to class {classname} in {entrypoint}") - else: - entrypoint = app.main_launcher_activity.get('entrypoint') - classname = app.main_launcher_activity.get("classname") - result = execute_script(app.installed_path + "/" + entrypoint, True, classname, app.installed_path + "/assets/") - # Launchers have the bar, other apps don't have it - if app.is_valid_launcher(): - mpos.ui.topmenu.open_bar() - else: - mpos.ui.topmenu.close_bar() - end_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"start_app() took {end_time}ms") - return result - - -# Starts the first launcher that's found -def restart_launcher(): - from .content.package_manager import PackageManager - print("restart_launcher") - # Stop all apps - mpos.ui.remove_and_stop_all_activities() - # No need to stop the other launcher first, because it exits after building the screen - return start_app(PackageManager.get_launcher().fullname) - diff --git a/internal_filesystem/lib/mpos/content/package_manager.py b/internal_filesystem/lib/mpos/content/package_manager.py index 7efdc2b7..ff45e076 100644 --- a/internal_filesystem/lib/mpos/content/package_manager.py +++ b/internal_filesystem/lib/mpos/content/package_manager.py @@ -1,4 +1,5 @@ import os +import traceback try: import zipfile @@ -232,3 +233,115 @@ class PackageManager: print(f"Checking if app {app_fullname} is installed...") return PackageManager.is_installed_by_path(f"apps/{app_fullname}") or PackageManager.is_installed_by_path(f"builtin/apps/{app_fullname}") + @staticmethod + def execute_script(script_source, is_file, classname, cwd=None): + """Run the script in the current thread. Returns True if successful.""" + import utime # for timing read and compile + import lvgl as lv + import mpos.ui + import _thread + thread_id = _thread.get_ident() + compile_name = 'script' if not is_file else script_source + print(f"Thread {thread_id}: executing script with cwd: {cwd}") + try: + if is_file: + print(f"Thread {thread_id}: reading script from file {script_source}") + with open(script_source, 'r') as f: # No need to check if it exists as exceptions are caught + start_time = utime.ticks_ms() + script_source = f.read() + read_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"execute_script: reading script_source took {read_time}ms") + script_globals = { + 'lv': lv, + '__name__': "__main__" + } + print(f"Thread {thread_id}: starting script") + import sys + path_before = sys.path[:] # Make a copy, not a reference + if cwd: + sys.path.append(cwd) + try: + start_time = utime.ticks_ms() + compiled_script = compile(script_source, compile_name, 'exec') + compile_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"execute_script: compiling script_source took {compile_time}ms") + start_time = utime.ticks_ms() + exec(compiled_script, script_globals) + end_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"apps.py execute_script: exec took {end_time}ms") + # Introspect globals + classes = {k: v for k, v in script_globals.items() if isinstance(v, type)} + functions = {k: v for k, v in script_globals.items() if callable(v) and not isinstance(v, type)} + variables = {k: v for k, v in script_globals.items() if not callable(v)} + print("Classes:", classes.keys()) # This lists a whole bunch of classes, including lib/mpos/ stuff + print("Functions:", functions.keys()) + print("Variables:", variables.keys()) + main_activity = script_globals.get(classname) + if main_activity: + from ..app.activity import Activity + from .intent import Intent + start_time = utime.ticks_ms() + Activity.startActivity(None, Intent(activity_class=main_activity)) + end_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"execute_script: Activity.startActivity took {end_time}ms") + else: + print(f"Warning: could not find app's main_activity {classname}") + return False + except Exception as e: + print(f"Thread {thread_id}: exception during execution:") + # Print stack trace with exception type, value, and traceback + tb = getattr(e, '__traceback__', None) + traceback.print_exception(type(e), e, tb) + return False + finally: + # Always restore sys.path, even if we return early or raise an exception + print(f"Thread {thread_id}: script {compile_name} finished, restoring sys.path from {sys.path} to {path_before}") + sys.path = path_before + return True + except Exception as e: + print(f"Thread {thread_id}: error:") + tb = getattr(e, '__traceback__', None) + traceback.print_exception(type(e), e, tb) + return False + + @staticmethod + def start_app(fullname): + """Start an app by fullname. Returns True if successful.""" + import mpos.ui + mpos.ui.set_foreground_app(fullname) + import utime + start_time = utime.ticks_ms() + app = PackageManager.get(fullname) + if not app: + print(f"Warning: start_app can't find app {fullname}") + return + if not app.installed_path: + print(f"Warning: start_app can't start {fullname} because no it doesn't have an installed_path") + return + entrypoint = "assets/main.py" + classname = "Main" + if not app.main_launcher_activity: + print(f"WARNING: app {fullname} doesn't have a main_launcher_activity, defaulting to class {classname} in {entrypoint}") + else: + entrypoint = app.main_launcher_activity.get('entrypoint') + classname = app.main_launcher_activity.get("classname") + result = PackageManager.execute_script(app.installed_path + "/" + entrypoint, True, classname, app.installed_path + "/assets/") + # Launchers have the bar, other apps don't have it + if app.is_valid_launcher(): + mpos.ui.topmenu.open_bar() + else: + mpos.ui.topmenu.close_bar() + end_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"start_app() took {end_time}ms") + return result + + @staticmethod + def restart_launcher(): + """Restart the launcher by stopping all activities and starting the launcher app.""" + import mpos.ui + print("restart_launcher") + # Stop all apps + mpos.ui.remove_and_stop_all_activities() + # No need to stop the other launcher first, because it exits after building the screen + return PackageManager.start_app(PackageManager.get_launcher().fullname) + diff --git a/internal_filesystem/lib/mpos/main.py b/internal_filesystem/lib/mpos/main.py index ef4700ea..81e471f4 100644 --- a/internal_filesystem/lib/mpos/main.py +++ b/internal_filesystem/lib/mpos/main.py @@ -2,7 +2,6 @@ import task_handler import _thread import lvgl as lv -import mpos.apps import mpos.ui import mpos.ui.topmenu @@ -126,11 +125,11 @@ except Exception as e: # Start launcher so it's always at bottom of stack launcher_app = PackageManager.get_launcher() -started_launcher = mpos.apps.start_app(launcher_app.fullname) +started_launcher = PackageManager.start_app(launcher_app.fullname) # Then start auto_start_app if configured auto_start_app = prefs.get_string("auto_start_app", None) if auto_start_app and launcher_app.fullname != auto_start_app: - result = mpos.apps.start_app(auto_start_app) + result = PackageManager.start_app(auto_start_app) if result is not True: print(f"WARNING: could not run {auto_start_app} app") diff --git a/internal_filesystem/lib/mpos/task_manager.py b/internal_filesystem/lib/mpos/task_manager.py index 032276e4..b4eb3d41 100644 --- a/internal_filesystem/lib/mpos/task_manager.py +++ b/internal_filesystem/lib/mpos/task_manager.py @@ -1,6 +1,5 @@ import asyncio # this is the only place where asyncio is allowed to be imported - apps should not use it directly but use this TaskManager import _thread -import mpos.apps class TaskManager: diff --git a/internal_filesystem/lib/mpos/testing/mocks.py b/internal_filesystem/lib/mpos/testing/mocks.py index a3b2ba4c..08462e9c 100644 --- a/internal_filesystem/lib/mpos/testing/mocks.py +++ b/internal_filesystem/lib/mpos/testing/mocks.py @@ -815,13 +815,49 @@ class MockThread: class MockApps: """ - Mock mpos.apps module for testing. + Mock mpos.apps module for testing (deprecated, use MockPackageManager instead). + + This is kept for backward compatibility with existing tests. Usage: sys.modules['mpos.apps'] = MockApps """ @staticmethod - def good_stack_size(): - """Return a reasonable stack size for testing.""" - return 8192 \ No newline at end of file + def start_app(fullname): + """Mock start_app function.""" + return True + + @staticmethod + def restart_launcher(): + """Mock restart_launcher function.""" + return True + + @staticmethod + def execute_script(script_source, is_file, classname, cwd=None): + """Mock execute_script function.""" + return True + + +class MockPackageManager: + """ + Mock mpos.content.package_manager module for testing. + + Usage: + sys.modules['mpos.content.package_manager'] = MockPackageManager + """ + + @staticmethod + def start_app(fullname): + """Mock start_app function.""" + return True + + @staticmethod + def restart_launcher(): + """Mock restart_launcher function.""" + return True + + @staticmethod + def execute_script(script_source, is_file, classname, cwd=None): + """Mock execute_script function.""" + return True \ No newline at end of file diff --git a/internal_filesystem/lib/mpos/ui/testing.py b/internal_filesystem/lib/mpos/ui/testing.py index 44738f91..193afb96 100644 --- a/internal_filesystem/lib/mpos/ui/testing.py +++ b/internal_filesystem/lib/mpos/ui/testing.py @@ -13,9 +13,10 @@ infrastructure are already initialized (boot.py and main.py executed). Usage in tests: from mpos.ui.testing import wait_for_render, capture_screenshot + from mpos import PackageManager # Start your app - mpos.apps.start_app("com.example.myapp") + PackageManager.start_app("com.example.myapp") # Wait for UI to render wait_for_render() @@ -62,7 +63,8 @@ def wait_for_render(iterations=10): iterations: Number of task handler iterations to run (default: 10) Example: - mpos.apps.start_app("com.example.myapp") + from mpos import PackageManager + PackageManager.start_app("com.example.myapp") wait_for_render() # Ensure UI is ready assert verify_text_present(lv.screen_active(), "Welcome") """ diff --git a/internal_filesystem/lib/mpos/ui/topmenu.py b/internal_filesystem/lib/mpos/ui/topmenu.py index 1c81e849..ce66846e 100644 --- a/internal_filesystem/lib/mpos/ui/topmenu.py +++ b/internal_filesystem/lib/mpos/ui/topmenu.py @@ -7,6 +7,7 @@ from .appearance_manager import AppearanceManager from .util import (get_foreground_app) from . import focus_direction from .widget_animator import WidgetAnimator +from mpos.content.package_manager import PackageManager CLOCK_UPDATE_INTERVAL = 1000 # 10 or even 1 ms doesn't seem to change the framerate but 100ms is enough WIFI_ICON_UPDATE_INTERVAL = 1500 @@ -267,7 +268,7 @@ def create_drawer(): wifi_label.center() def wifi_event(e): close_drawer() - mpos.apps.start_app("com.micropythonos.wifi") + PackageManager.start_app("com.micropythonos.wifi") wifi_btn.add_event_cb(wifi_event,lv.EVENT.CLICKED,None) settings_btn=lv.button(drawer) settings_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) @@ -277,7 +278,7 @@ def create_drawer(): settings_label.center() def settings_event(e): close_drawer() - mpos.apps.start_app("com.micropythonos.settings") + PackageManager.start_app("com.micropythonos.settings") settings_btn.add_event_cb(settings_event,lv.EVENT.CLICKED,None) launcher_btn=lv.button(drawer) launcher_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) @@ -288,7 +289,7 @@ def create_drawer(): def launcher_event(e): print("Launch button pressed!") close_drawer(True) - mpos.apps.restart_launcher() + PackageManager.restart_launcher() launcher_btn.add_event_cb(launcher_event,lv.EVENT.CLICKED,None) ''' sleep_btn=lv.button(drawer) @@ -307,7 +308,7 @@ def create_drawer(): else: # assume unix: # maybe do a system suspend here? or at least show a popup toast "not supported" close_drawer(True) - mpos.apps.restart_launcher() + PackageManager.restart_launcher() sleep_btn.add_event_cb(sleep_event,lv.EVENT.CLICKED,None) ''' restart_btn=lv.button(drawer) diff --git a/internal_filesystem/lib/mpos/ui/util.py b/internal_filesystem/lib/mpos/ui/util.py index 5b125a3c..81904e47 100644 --- a/internal_filesystem/lib/mpos/ui/util.py +++ b/internal_filesystem/lib/mpos/ui/util.py @@ -1,7 +1,6 @@ # lib/mpos/ui/util.py import lvgl as lv import sys -from ..apps import restart_launcher _foreground_app_name = None diff --git a/internal_filesystem/lib/mpos/ui/view.py b/internal_filesystem/lib/mpos/ui/view.py index 377fa2bf..5de70666 100644 --- a/internal_filesystem/lib/mpos/ui/view.py +++ b/internal_filesystem/lib/mpos/ui/view.py @@ -1,7 +1,6 @@ import lvgl as lv import sys -from ..apps import restart_launcher from .focus import save_and_clear_current_focusgroup from .topmenu import open_bar diff --git a/internal_filesystem/lib/threading.py b/internal_filesystem/lib/threading.py index 4471ddb3..2f02d254 100644 --- a/internal_filesystem/lib/threading.py +++ b/internal_filesystem/lib/threading.py @@ -3,7 +3,6 @@ import _thread from mpos.task_manager import TaskManager -import mpos.apps class Thread: def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, daemon=None): diff --git a/tests/manual_test_nostr_asyncio.py b/tests/manual_test_nostr_asyncio.py index 7962afa5..9bec4a12 100644 --- a/tests/manual_test_nostr_asyncio.py +++ b/tests/manual_test_nostr_asyncio.py @@ -6,7 +6,6 @@ import time import unittest from mpos import App, PackageManager -import mpos.apps from nostr.relay_manager import RelayManager from nostr.message_type import ClientMessageType diff --git a/tests/test_audioflinger.py b/tests/test_audioflinger.py index 59d6b6b1..ab51ab38 100644 --- a/tests/test_audioflinger.py +++ b/tests/test_audioflinger.py @@ -8,7 +8,6 @@ from mpos.testing import ( MockPWM, MockPin, MockThread, - MockApps, inject_mocks, ) @@ -16,7 +15,6 @@ from mpos.testing import ( inject_mocks({ 'machine': MockMachine(), '_thread': MockThread, - 'mpos.apps': MockApps, }) # Now import the module to test diff --git a/tests/test_graphical_about_app.py b/tests/test_graphical_about_app.py index cfe7a921..27d17bff 100644 --- a/tests/test_graphical_about_app.py +++ b/tests/test_graphical_about_app.py @@ -17,7 +17,6 @@ Usage: import unittest import lvgl as lv -import mpos.apps import mpos.ui import os from mpos import ( @@ -27,7 +26,8 @@ from mpos import ( verify_text_present, print_screen_labels, DeviceInfo, - BuildInfo + BuildInfo, + PackageManager ) @@ -78,7 +78,7 @@ class TestGraphicalAboutApp(unittest.TestCase): print("\n=== Starting About app test ===") # Start the About app - result = mpos.apps.start_app("com.micropythonos.about") + result = PackageManager.start_app("com.micropythonos.about") self.assertTrue(result, "Failed to start About app") # Wait for UI to fully render @@ -146,7 +146,7 @@ class TestGraphicalAboutApp(unittest.TestCase): print("\n=== Starting About app OS version test ===") # Start the About app - result = mpos.apps.start_app("com.micropythonos.about") + result = PackageManager.start_app("com.micropythonos.about") self.assertTrue(result, "Failed to start About app") # Wait for UI to render diff --git a/tests/test_graphical_camera_settings.py b/tests/test_graphical_camera_settings.py index 471a05d1..44237027 100644 --- a/tests/test_graphical_camera_settings.py +++ b/tests/test_graphical_camera_settings.py @@ -19,7 +19,6 @@ Usage: import unittest import lvgl as lv -import mpos.apps import mpos.ui import os import sys @@ -31,7 +30,8 @@ from mpos import ( verify_text_present, print_screen_labels, simulate_click, - get_widget_coords + get_widget_coords, + PackageManager ) @unittest.skipIf(sys.platform == 'darwin', "Camera tests not supported on macOS (no camera available)") @@ -117,7 +117,7 @@ class TestGraphicalCameraSettings(unittest.TestCase): print("\n=== Testing settings button click (no crash) ===") # Start the Camera app - result = mpos.apps.start_app("com.micropythonos.camera") + result = PackageManager.start_app("com.micropythonos.camera") self.assertTrue(result, "Failed to start Camera app") # Wait for camera to initialize and first frame to render @@ -251,7 +251,7 @@ class TestGraphicalCameraSettings(unittest.TestCase): print("\n=== Testing resolution change (no crash) ===") # Start the Camera app - result = mpos.apps.start_app("com.micropythonos.camera") + result = PackageManager.start_app("com.micropythonos.camera") self.assertTrue(result, "Failed to start Camera app") # Wait for camera to initialize diff --git a/tests/test_graphical_imu_calibration.py b/tests/test_graphical_imu_calibration.py index 4686594a..5f106c23 100644 --- a/tests/test_graphical_imu_calibration.py +++ b/tests/test_graphical_imu_calibration.py @@ -11,7 +11,6 @@ Usage: import unittest import lvgl as lv -import mpos.apps import mpos.ui import os import sys @@ -27,7 +26,8 @@ from mpos import ( find_button_with_text, click_label, click_button, - find_text_on_screen + find_text_on_screen, + PackageManager ) @@ -63,7 +63,7 @@ class TestIMUCalibration(unittest.TestCase): print("\n=== Testing CheckIMUCalibrationActivity ===") # Navigate: Launcher -> Settings -> Check IMU Calibration - result = mpos.apps.start_app("com.micropythonos.settings") + result = PackageManager.start_app("com.micropythonos.settings") self.assertTrue(result, "Failed to start Settings app") wait_for_render(15) @@ -98,7 +98,7 @@ class TestIMUCalibration(unittest.TestCase): print("\n=== Testing CalibrateIMUActivity Flow ===") # Navigate: Launcher -> Settings -> Calibrate IMU - result = mpos.apps.start_app("com.micropythonos.settings") + result = PackageManager.start_app("com.micropythonos.settings") self.assertTrue(result, "Failed to start Settings app") wait_for_render(15) @@ -155,7 +155,7 @@ class TestIMUCalibration(unittest.TestCase): print("\n=== Testing Check -> Calibrate Navigation ===") # Navigate to Check activity - result = mpos.apps.start_app("com.micropythonos.settings") + result = PackageManager.start_app("com.micropythonos.settings") self.assertTrue(result) wait_for_render(15) diff --git a/tests/test_graphical_imu_calibration_ui_bug.py b/tests/test_graphical_imu_calibration_ui_bug.py index f7ab6046..62adfc89 100755 --- a/tests/test_graphical_imu_calibration_ui_bug.py +++ b/tests/test_graphical_imu_calibration_ui_bug.py @@ -26,7 +26,8 @@ from mpos import ( capture_screenshot, click_label, click_button, - find_text_on_screen + find_text_on_screen, + PackageManager ) @@ -43,10 +44,9 @@ class TestIMUCalibrationUI(unittest.TestCase): # Step 2: Open Settings app print("Step 2: Opening Settings app...") - import mpos.apps # Start Settings app by name - mpos.apps.start_app("com.micropythonos.settings") + PackageManager.start_app("com.micropythonos.settings") wait_for_render(iterations=30) print("Settings app opened\n") diff --git a/tests/test_graphical_launch_all_apps.py b/tests/test_graphical_launch_all_apps.py index 7010285a..13147b47 100644 --- a/tests/test_graphical_launch_all_apps.py +++ b/tests/test_graphical_launch_all_apps.py @@ -13,7 +13,7 @@ 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, apps, ui, PackageManager +from mpos import wait_for_render, ui, PackageManager class TestLaunchAllApps(unittest.TestCase): @@ -64,7 +64,7 @@ class TestLaunchAllApps(unittest.TestCase): try: # Launch the app by package name - result = mpos.apps.start_app(package_name) + result = PackageManager.start_app(package_name) # Wait for UI to render wait_for_render(iterations=5) @@ -188,7 +188,7 @@ class TestLaunchSpecificApps(unittest.TestCase): try: # Launch the app by package name - result = mpos.apps.start_app(package_name) + result = PackageManager.start_app(package_name) wait_for_render(iterations=5) # Check if start_app returned False (indicates error) diff --git a/tests/test_graphical_osupdate.py b/tests/test_graphical_osupdate.py index 83dbfeb6..71e535f2 100644 --- a/tests/test_graphical_osupdate.py +++ b/tests/test_graphical_osupdate.py @@ -13,7 +13,8 @@ from mpos import ( verify_text_present, print_screen_labels, DeviceInfo, - BuildInfo + BuildInfo, + PackageManager ) @@ -28,7 +29,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_app_launches_successfully(self): """Test that OSUpdate app launches without errors.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result, "Failed to start OSUpdate app") wait_for_render(10) @@ -39,7 +40,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_ui_elements_exist(self): """Test that all required UI elements are created.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -59,7 +60,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_force_checkbox_initially_unchecked(self): """Test that force update checkbox starts unchecked.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -102,7 +103,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_install_button_initially_disabled(self): """Test that install button starts in disabled state.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -138,7 +139,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_current_version_displayed(self): """Test that current OS version is displayed correctly.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -158,7 +159,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): """Test status message when wifi is not connected.""" # This test assumes desktop mode where wifi check returns True # On actual hardware without wifi, it would show error - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -173,7 +174,7 @@ class TestOSUpdateGraphicalUI(unittest.TestCase): def test_screenshot_initial_state(self): """Capture screenshot of initial app state.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(20) @@ -206,7 +207,7 @@ class TestOSUpdateGraphicalStatusMessages(unittest.TestCase): def test_status_label_exists(self): """Test that status label is created and visible.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -225,7 +226,7 @@ class TestOSUpdateGraphicalStatusMessages(unittest.TestCase): def test_all_labels_readable(self): """Test that all labels are readable (no truncation issues).""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(15) @@ -263,14 +264,14 @@ class TestOSUpdateGraphicalScreenshots(unittest.TestCase): def test_capture_main_screen(self): """Capture screenshot of main OSUpdate screen.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(20) def test_capture_with_labels_visible(self): """Capture screenshot ensuring all text is visible.""" - result = mpos.apps.start_app("com.micropythonos.osupdate") + result = PackageManager.start_app("com.micropythonos.osupdate") self.assertTrue(result) wait_for_render(20) diff --git a/tests/test_graphical_start_app.py b/tests/test_graphical_start_app.py index 2aecc4a2..2fac3f72 100644 --- a/tests/test_graphical_start_app.py +++ b/tests/test_graphical_start_app.py @@ -13,8 +13,7 @@ Usage: """ import unittest -import mpos.apps -from mpos import ui, wait_for_render +from mpos import ui, wait_for_render, PackageManager class TestStartApp(unittest.TestCase): @@ -40,7 +39,7 @@ class TestStartApp(unittest.TestCase): """Test that launching an existing app succeeds.""" print("Testing normal app launch...") - result = mpos.apps.start_app("com.micropythonos.launcher") + result = PackageManager.start_app("com.micropythonos.launcher") wait_for_render(10) # Wait for app to load self.assertTrue(result, "com.micropythonos.launcher should start") @@ -50,7 +49,7 @@ class TestStartApp(unittest.TestCase): """Test that launching a non-existent app fails gracefully.""" print("Testing non-existent app launch...") - result = mpos.apps.start_app("com.micropythonos.nonexistent") + result = PackageManager.start_app("com.micropythonos.nonexistent") self.assertFalse(result, "com.micropythonos.nonexistent should not start") print("Non-existent app handled correctly") @@ -59,7 +58,7 @@ class TestStartApp(unittest.TestCase): """Test that restarting the launcher succeeds.""" print("Testing launcher restart...") - result = mpos.apps.restart_launcher() + result = PackageManager.restart_launcher() wait_for_render(10) # Wait for launcher to load self.assertTrue(result, "restart_launcher() should succeed") diff --git a/tests/test_syspath_restore.py b/tests/test_syspath_restore.py index 36d668d8..0941d787 100644 --- a/tests/test_syspath_restore.py +++ b/tests/test_syspath_restore.py @@ -8,7 +8,7 @@ class TestSysPathRestore(unittest.TestCase): def test_syspath_restored_after_execute_script(self): """Test that sys.path is restored to original state after script execution""" # Import here to ensure we're in the right context - import mpos.apps + from mpos import PackageManager # Capture original sys.path original_path = sys.path[:] @@ -31,11 +31,11 @@ x = 42 # Call execute_script with cwd parameter # Note: This will fail because there's no Activity to start, # but that's fine - we're testing the sys.path restoration - result = mpos.apps.execute_script( + result = PackageManager.execute_script( test_script, is_file=False, - cwd=test_cwd, - classname="NonExistentClass" + classname="NonExistentClass", + cwd=test_cwd ) # After execution, sys.path should be restored @@ -56,7 +56,7 @@ x = 42 def test_syspath_not_affected_when_no_cwd(self): """Test that sys.path is unchanged when cwd is None""" - import mpos.apps + from mpos import PackageManager # Capture original sys.path original_path = sys.path[:] @@ -66,11 +66,11 @@ x = 42 ''' # Call without cwd parameter - result = mpos.apps.execute_script( + result = PackageManager.execute_script( test_script, is_file=False, - cwd=None, - classname="NonExistentClass" + classname="NonExistentClass", + cwd=None ) # sys.path should be unchanged diff --git a/tests/test_websocket.py b/tests/test_websocket.py index ed81e8ea..ac91b84b 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -5,7 +5,6 @@ import time from mpos import App, PackageManager from mpos import TaskManager -import mpos.apps from websocket import WebSocketApp diff --git a/tests/unittest.sh b/tests/unittest.sh index b7878f9c..baa1d1e2 100755 --- a/tests/unittest.sh +++ b/tests/unittest.sh @@ -64,7 +64,7 @@ one_test() { # Desktop execution if [ $is_graphical -eq 1 ]; then echo "Graphical test: include main.py" - "$binary" -X heapsize=8M -c "import sys ; sys.path.insert(0, 'lib') ; import mpos ; mpos.TaskManager.disable() ; $(cat main.py) ; import mpos.apps; sys.path.append(\"$tests_abs_path\") + "$binary" -X heapsize=8M -c "import sys ; sys.path.insert(0, 'lib') ; import mpos ; mpos.TaskManager.disable() ; $(cat main.py) ; sys.path.append(\"$tests_abs_path\") $(cat $file) result = unittest.main() ; sys.exit(0 if result.wasSuccessful() else 1) " result=$?