From 371409a96215aabd2df1178271396206ac8caef3 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Thu, 23 Oct 2025 21:13:58 +0200 Subject: [PATCH 01/14] Added android debugging support --- changes/2351.feature.md | 1 + src/briefcase/integrations/android_sdk.py | 14 +- src/briefcase/platforms/android/gradle.py | 48 ++++- .../android_sdk/ADB/test_start_app.py | 53 ++++- tests/platforms/android/gradle/test_create.py | 8 + tests/platforms/android/gradle/test_run.py | 200 +++++++++++++++++- 6 files changed, 311 insertions(+), 13 deletions(-) create mode 100644 changes/2351.feature.md diff --git a/changes/2351.feature.md b/changes/2351.feature.md new file mode 100644 index 000000000..ab10bf3b9 --- /dev/null +++ b/changes/2351.feature.md @@ -0,0 +1 @@ +Added a `--debug` option to the `build` and `run` commands for Android. diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index 0375f0f41..6966a5b4d 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -1523,7 +1523,9 @@ def force_stop_app(self, package: str): f"Unable to force stop app {package} on {self.device}" ) from e - def start_app(self, package: str, activity: str, passthrough: list[str]): + def start_app( + self, package: str, activity: str, passthrough: list[str], env: dict[str, str] + ): """Start an app, specified as a package name & activity name. If you have an APK file, and you are not sure of the package or activity @@ -1533,6 +1535,7 @@ def start_app(self, package: str, activity: str, passthrough: list[str]): :param package: The name of the Android package, e.g., com.username.myapp. :param activity: The activity of the APK to start. :param passthrough: Arguments to pass to the app. + :param env: Environment variables to pass to the app. :returns: `None` on success; raises an exception on failure. """ try: @@ -1552,6 +1555,15 @@ def start_app(self, package: str, activity: str, passthrough: list[str]): "--es", "org.beeware.ARGV", shlex.quote(json.dumps(passthrough)), # Protect from Android's shell + *( + [ + "--es", + "org.beeware.ENVIRON", + shlex.quote(json.dumps(env)), # Protect from Android's shell + ] + if env + else [] + ), ) # `adb shell am start` always exits with status zero. We look for error diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index b32382ea7..e973a29dc 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -19,6 +19,10 @@ ) from briefcase.config import AppConfig, parsed_version from briefcase.console import ANSI_ESC_SEQ_RE_DEF +from briefcase.debuggers.base import ( + AppPackagesPathMappings, + DebuggerConnectionMode, +) from briefcase.exceptions import BriefcaseCommandError from briefcase.integrations.android_sdk import ADB, AndroidSDK from briefcase.integrations.subprocess import SubprocessArgT @@ -216,15 +220,19 @@ def output_format_template_context(self, app: AppConfig): "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0", ] + # Extract test packages, to enable features like test discovery and assertion rewriting. + extract_sources = app.test_sources or [] + + # In debug mode extract all source packages so that the debugger can get the source code + # at runtime (eg. via 'll' in pdb). + if app.debugger: + extract_sources.extend(app.sources) + return { "version_code": version_code, "safe_formal_name": safe_formal_name(app.formal_name), - # Extract test packages to enable features like test discovery and assertion - # rewriting. "extract_packages": ", ".join( - f'"{name}"' - for path in (app.test_sources or []) - if (name := Path(path).name) + [f'"{name}"' for path in extract_sources if (name := Path(path).name)] ), "build_gradle_dependencies": {"implementation": dependencies}, } @@ -288,6 +296,7 @@ def permissions_context(self, app: AppConfig, x_permissions: dict[str, str]): class GradleUpdateCommand(GradleCreateCommand, UpdateCommand): description = "Update an existing Android Gradle project." + supports_debugger = True class GradleOpenCommand(GradleMixin, OpenCommand): @@ -296,6 +305,7 @@ class GradleOpenCommand(GradleMixin, OpenCommand): class GradleBuildCommand(GradleMixin, BuildCommand): description = "Build an Android debug APK." + supports_debugger = True def metadata_resource_path(self, app: AppConfig): return self.bundle_path(app) / self.path_index(app, "metadata_resource_path") @@ -333,6 +343,7 @@ def build_app(self, app: AppConfig, **kwargs): class GradleRunCommand(GradleMixin, RunCommand): description = "Run an Android debug APK on a device (physical or virtual)." + supports_debugger = True def verify_tools(self): super().verify_tools() @@ -378,6 +389,20 @@ def add_options(self, parser): help="Reverse the specified port from device to host.", ) + def debugger_app_packages_path_mapping( + self, app: AppConfig + ) -> AppPackagesPathMappings: + """Get the path mappings for the app packages. + + :param app: The config object for the app + :returns: The path mappings for the app packages + """ + app_packages_path = self.bundle_path(app) / "app/build/python/pip/debug/common" + return AppPackagesPathMappings( + sys_path_regex="requirements$", + host_folder=f"{app_packages_path}", + ) + def run_app( self, app: AppConfig, @@ -450,6 +475,17 @@ def run_app( forward_ports = forward_ports or [] reverse_ports = reverse_ports or [] + env = {} + if self.console.is_debug: + env["BRIEFCASE_DEBUG"] = "1" + + if app.debugger: + env["BRIEFCASE_DEBUGGER"] = app.debugger.get_env_config(self, app) + if app.debugger.connection_mode == DebuggerConnectionMode.SERVER: + forward_ports.append(app.debugger_port) + else: + reverse_ports.append(app.debugger_port) + # Forward/Reverse requested ports with self.forward_ports(adb, forward_ports, reverse_ports): # To start the app, we launch `org.beeware.android.MainActivity`. @@ -458,7 +494,7 @@ def run_app( device_start_time = adb.datetime() adb.start_app( - package, "org.beeware.android.MainActivity", passthrough + package, "org.beeware.android.MainActivity", passthrough, env ) # Try to get the PID for 5 seconds. diff --git a/tests/integrations/android_sdk/ADB/test_start_app.py b/tests/integrations/android_sdk/ADB/test_start_app.py index 17788a0dc..1c6a0d525 100644 --- a/tests/integrations/android_sdk/ADB/test_start_app.py +++ b/tests/integrations/android_sdk/ADB/test_start_app.py @@ -30,7 +30,7 @@ def test_start_app_launches_app(adb, capsys, passthrough): # Invoke start_app adb.start_app( - "com.example.sample.package", "com.example.sample.activity", passthrough + "com.example.sample.package", "com.example.sample.activity", passthrough, {} ) # Validate call parameters. @@ -54,6 +54,45 @@ def test_start_app_launches_app(adb, capsys, passthrough): assert "normal adb output" not in capsys.readouterr() +@pytest.mark.parametrize( + "env", + [ + {"PARAM1": "VALUE1"}, + {"BRIEFCASE_DEBUGGER": '{"host": "localhost", "port": 1234}'}, + ], +) +def test_start_app_launches_app_with_env(adb, capsys, env): + """Invoking `start_app()` calls `run()` with the appropriate parameters.""" + # Mock out the run command on an adb instance + adb.run = MagicMock(return_value="example normal adb output") + + # Invoke start_app + adb.start_app("com.example.sample.package", "com.example.sample.activity", [], env) + + # Validate call parameters. + adb.run.assert_called_once_with( + "shell", + "am", + "start", + "-n", + "com.example.sample.package/com.example.sample.activity", + "-a", + "android.intent.action.MAIN", + "-c", + "android.intent.category.LAUNCHER", + "--es", + "org.beeware.ARGV", + "'[]'", + "--es", + "org.beeware.ENVIRON", + shlex.quote(json.dumps(env)), + ) + + # Validate that the normal output of the command was not printed (since there + # was no error). + assert "normal adb output" not in capsys.readouterr() + + def test_missing_activity(adb): """If the activity doesn't exist, the error is caught.""" # Use real `adb` output from launching an activity that does not exist. @@ -69,7 +108,9 @@ def test_missing_activity(adb): ) with pytest.raises(BriefcaseCommandError) as exc_info: - adb.start_app("com.example.sample.package", "com.example.sample.activity", []) + adb.start_app( + "com.example.sample.package", "com.example.sample.activity", [], {} + ) assert "Activity class not found" in str(exc_info.value) @@ -81,7 +122,9 @@ def test_invalid_device(adb): adb.run = MagicMock(side_effect=InvalidDeviceError("device", "exampleDevice")) with pytest.raises(InvalidDeviceError): - adb.start_app("com.example.sample.package", "com.example.sample.activity", []) + adb.start_app( + "com.example.sample.package", "com.example.sample.activity", [], {} + ) def test_unable_to_start(adb): @@ -92,4 +135,6 @@ def test_unable_to_start(adb): BriefcaseCommandError, match=r"Unable to start com.example.sample.package/com.example.sample.activity on exampleDevice", ): - adb.start_app("com.example.sample.package", "com.example.sample.activity", []) + adb.start_app( + "com.example.sample.package", "com.example.sample.activity", [], {} + ) diff --git a/tests/platforms/android/gradle/test_create.py b/tests/platforms/android/gradle/test_create.py index a5cbcd8d1..1b82825a0 100644 --- a/tests/platforms/android/gradle/test_create.py +++ b/tests/platforms/android/gradle/test_create.py @@ -200,6 +200,14 @@ def test_extract_packages(create_command, first_app_config, test_sources, expect assert context["extract_packages"] == expected +def test_extract_packages_debugger(create_command, first_app_config, dummy_debugger): + first_app_config.test_sources = ["one", "two", "three"] + first_app_config.sources = ["four", "five", "six"] + first_app_config.debugger = dummy_debugger + context = create_command.output_format_template_context(first_app_config) + assert context["extract_packages"] == '"one", "two", "three", "four", "five", "six"' + + @pytest.mark.parametrize( ("permissions", "features", "context"), [ diff --git a/tests/platforms/android/gradle/test_run.py b/tests/platforms/android/gradle/test_run.py index 7a2a3ba5d..f954c3100 100644 --- a/tests/platforms/android/gradle/test_run.py +++ b/tests/platforms/android/gradle/test_run.py @@ -1,4 +1,5 @@ import datetime +import json import os import platform import sys @@ -9,6 +10,8 @@ import httpx import pytest +from briefcase.console import LogLevel +from briefcase.debuggers.base import BaseDebugger, DebuggerConnectionMode from briefcase.exceptions import BriefcaseCommandError from briefcase.integrations.android_sdk import ADB, AndroidSDK from briefcase.integrations.java import JDK @@ -89,6 +92,9 @@ def test_device_option(run_command): "update_stub": False, "no_update": False, "test_mode": False, + "debugger": None, + "debugger_host": "localhost", + "debugger_port": 5678, "passthrough": [], "extra_emulator_args": None, "shutdown_on_exit": False, @@ -114,6 +120,9 @@ def test_extra_emulator_args_option(run_command): "update_stub": False, "no_update": False, "test_mode": False, + "debugger": None, + "debugger_host": "localhost", + "debugger_port": 5678, "passthrough": [], "extra_emulator_args": ["-no-window", "-no-audio"], "shutdown_on_exit": False, @@ -137,6 +146,9 @@ def test_shutdown_on_exit_option(run_command): "update_stub": False, "no_update": False, "test_mode": False, + "debugger": None, + "debugger_host": "localhost", + "debugger_port": 5678, "passthrough": [], "extra_emulator_args": None, "shutdown_on_exit": True, @@ -163,6 +175,9 @@ def test_forward_ports_option(run_command): "no_update": False, "test_mode": False, "passthrough": [], + "debugger": None, + "debugger_host": "localhost", + "debugger_port": 5678, "extra_emulator_args": None, "shutdown_on_exit": False, "forward_ports": [80, 81], @@ -188,6 +203,9 @@ def test_reverse_ports_option(run_command): "no_update": False, "test_mode": False, "passthrough": [], + "debugger": None, + "debugger_host": "localhost", + "debugger_port": 5678, "extra_emulator_args": None, "shutdown_on_exit": False, "forward_ports": None, @@ -247,6 +265,8 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=[], ) @@ -273,6 +293,7 @@ def mock_stream_output(app, stop_func, **kwargs): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) run_command.tools.mock_adb.forward_remove.assert_not_called() @@ -323,6 +344,8 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=["foo", "--bar"], ) @@ -346,6 +369,7 @@ def mock_stream_output(app, stop_func, **kwargs): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", ["foo", "--bar"], + {}, ) run_command.tools.mock_adb.pidof.assert_called_once_with( @@ -395,6 +419,7 @@ def test_run_forward_reverse_ports(run_command, first_app_config): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) assert run_command.tools.mock_adb.forward_remove.mock_calls == [ @@ -424,6 +449,8 @@ def test_run_slow_start(run_command, first_app_config, monkeypatch): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=[], ) @@ -476,6 +503,8 @@ def test_run_crash_at_start(run_command, first_app_config, monkeypatch): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=[], ) @@ -513,7 +542,9 @@ def test_run_created_emulator(run_command, first_app_config): run_command.tools.mock_adb.logcat.return_value = log_popen # Invoke run_app - run_command.run_app(first_app_config, passthrough=[]) + run_command.run_app( + first_app_config, debugger_host=None, debugger_port=None, passthrough=[] + ) # A new emulator was created run_command.tools.android_sdk.create_emulator.assert_called_once_with() @@ -542,6 +573,7 @@ def test_run_created_emulator(run_command, first_app_config): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) run_command.tools.mock_adb.logcat.assert_called_once_with(pid="777") @@ -575,7 +607,9 @@ def test_run_idle_device(run_command, first_app_config): run_command.tools.mock_adb.logcat.return_value = log_popen # Invoke run_app - run_command.run_app(first_app_config, passthrough=[]) + run_command.run_app( + first_app_config, debugger_host=None, debugger_port=None, passthrough=[] + ) # No attempt was made to create a new emulator run_command.tools.android_sdk.create_emulator.assert_not_called() @@ -603,6 +637,7 @@ def test_run_idle_device(run_command, first_app_config): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) run_command.tools.mock_adb.logcat.assert_called_once_with(pid="777") @@ -680,6 +715,8 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=[], shutdown_on_exit=True, ) @@ -704,6 +741,7 @@ def mock_stream_output(app, stop_func, **kwargs): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) run_command.tools.mock_adb.pidof.assert_called_once_with( @@ -753,6 +791,8 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", + debugger_host=None, + debugger_port=None, passthrough=["foo", "--bar"], shutdown_on_exit=True, ) @@ -777,6 +817,7 @@ def mock_stream_output(app, stop_func, **kwargs): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", ["foo", "--bar"], + {}, ) run_command.tools.mock_adb.pidof.assert_called_once_with( @@ -821,6 +862,8 @@ def test_run_test_mode_created_emulator(run_command, first_app_config): # Invoke run_app run_command.run_app( first_app_config, + debugger_host=None, + debugger_port=None, passthrough=[], extra_emulator_args=["-no-window", "-no-audio"], shutdown_on_exit=True, @@ -854,6 +897,7 @@ def test_run_test_mode_created_emulator(run_command, first_app_config): f"{first_app_config.package_name}.{first_app_config.module_name}", "org.beeware.android.MainActivity", [], + {}, ) run_command.tools.mock_adb.logcat.assert_called_once_with(pid="777") @@ -869,3 +913,155 @@ def test_run_test_mode_created_emulator(run_command, first_app_config): # The emulator was killed at the end of the test run_command.tools.mock_adb.kill.assert_called_once_with() + + +class ServerDebugger(BaseDebugger): + @property + def name(self) -> str: + return "dummy" + + @property + def connection_mode(self) -> DebuggerConnectionMode: + return DebuggerConnectionMode.SERVER + + @property + def debugger_support_pkg(self) -> str: + raise NotImplementedError + + +class ClientDebugger(BaseDebugger): + @property + def name(self) -> str: + return "dummy" + + @property + def connection_mode(self) -> DebuggerConnectionMode: + return DebuggerConnectionMode.CLIENT + + @property + def debugger_support_pkg(self) -> str: + raise NotImplementedError + + +@pytest.mark.parametrize( + "debugger", + [ + ServerDebugger(), + ClientDebugger(), + ], +) +def test_run_debugger(run_command, first_app_config, tmp_path, debugger): + """An app can be run in debug mode.""" + run_command.console.verbosity = LogLevel.DEBUG + + # Set up device selection to return a running physical device. + run_command.tools.android_sdk.select_target_device = mock.MagicMock( + return_value=("exampleDevice", "ExampleDevice", None) + ) + + # Set up the log streamer to return a known stream + log_popen = mock.MagicMock() + run_command.tools.mock_adb.logcat.return_value = log_popen + + # To satisfy coverage, the stop function must be invoked at least once + # when invoking stream_output. + def mock_stream_output(app, stop_func, **kwargs): + stop_func() + + run_command._stream_app_logs.side_effect = mock_stream_output + + # Set up app config to have a `-` in the `bundle`, to ensure it gets + # normalized into a `_` via `package_name`. + first_app_config.bundle = "com.ex-ample" + + # Set up the debugger + first_app_config.debugger = debugger + first_app_config.debugger_host = "somehost" + first_app_config.debugger_port = 9999 + + # Invoke run_app with args. + run_command.run_app( + first_app_config, + device_or_avd="exampleDevice", + passthrough=[], + ) + + # select_target_device was invoked with a specific device + run_command.tools.android_sdk.select_target_device.assert_called_once_with( + "exampleDevice" + ) + + # The ADB wrapper is created + run_command.tools.android_sdk.adb.assert_called_once_with(device="exampleDevice") + + # The adb wrapper is invoked with the expected arguments + run_command.tools.mock_adb.install_apk.assert_called_once_with( + run_command.binary_path(first_app_config) + ) + run_command.tools.mock_adb.force_stop_app.assert_called_once_with( + f"{first_app_config.package_name}.{first_app_config.module_name}", + ) + + run_command.tools.mock_adb.start_app.assert_called_once_with( + f"{first_app_config.package_name}.{first_app_config.module_name}", + "org.beeware.android.MainActivity", + [], + { + "BRIEFCASE_DEBUG": "1", + "BRIEFCASE_DEBUGGER": json.dumps( + { + "debugger": "dummy", + "host": "somehost", + "port": 9999, + "host_os": platform.system(), + "app_path_mappings": { + "device_sys_path_regex": "app$", + "device_subfolders": ["first_app"], + "host_folders": [str(tmp_path / "base_path/src/first_app")], + }, + "app_packages_path_mappings": { + "sys_path_regex": "requirements$", + "host_folder": str( + tmp_path + / "base_path/build/first-app/android/gradle/app/build/python/pip/debug/common" + ), + }, + } + ), + }, + ) + + run_command.tools.mock_adb.pidof.assert_called_once_with( + f"{first_app_config.package_name}.{first_app_config.module_name}", + quiet=2, + ) + run_command.tools.mock_adb.logcat.assert_called_once_with(pid="777") + + if isinstance(debugger, ServerDebugger): + run_command.tools.mock_adb.forward.assert_called_once_with( + 9999, + 9999, + ) + run_command.tools.mock_adb.forward_remove.assert_called_once_with( + 9999, + ) + elif isinstance(debugger, ClientDebugger): + run_command.tools.mock_adb.reverse.assert_called_once_with( + 9999, + 9999, + ) + run_command.tools.mock_adb.reverse_remove.assert_called_once_with( + 9999, + ) + + run_command._stream_app_logs.assert_called_once_with( + first_app_config, + popen=log_popen, + clean_filter=android_log_clean_filter, + clean_output=False, + stop_func=mock.ANY, + log_stream=True, + ) + + # The emulator was not killed at the end of the test + run_command.tools.mock_adb.kill.assert_not_called() From 1f3a2d41798dbbc16b0ef4e5cf42895ed8936d08 Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:00:02 +0100 Subject: [PATCH 02/14] add comment, that it is also necessary for setting breakpoints in vscode --- src/briefcase/platforms/android/gradle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index e973a29dc..097275e24 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -224,7 +224,7 @@ def output_format_template_context(self, app: AppConfig): extract_sources = app.test_sources or [] # In debug mode extract all source packages so that the debugger can get the source code - # at runtime (eg. via 'll' in pdb). + # at runtime. This is necessary for setting breakpoints in VSCode or when using 'll' in pdb. if app.debugger: extract_sources.extend(app.sources) From 7ef2c1f29e966b26ea0a464a49b4b6380dcef4cd Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Sun, 16 Nov 2025 21:37:31 +0100 Subject: [PATCH 03/14] set packages to extract in build command and extract all packages --- src/briefcase/platforms/android/gradle.py | 33 ++++-- tests/platforms/android/gradle/conftest.py | 13 +++ tests/platforms/android/gradle/test_build.py | 104 ++++++++++++++++++ tests/platforms/android/gradle/test_create.py | 54 --------- 4 files changed, 139 insertions(+), 65 deletions(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index 097275e24..fa43175a7 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -220,20 +220,9 @@ def output_format_template_context(self, app: AppConfig): "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0", ] - # Extract test packages, to enable features like test discovery and assertion rewriting. - extract_sources = app.test_sources or [] - - # In debug mode extract all source packages so that the debugger can get the source code - # at runtime. This is necessary for setting breakpoints in VSCode or when using 'll' in pdb. - if app.debugger: - extract_sources.extend(app.sources) - return { "version_code": version_code, "safe_formal_name": safe_formal_name(app.formal_name), - "extract_packages": ", ".join( - [f'"{name}"' for path in extract_sources if (name := Path(path).name)] - ), "build_gradle_dependencies": {"implementation": dependencies}, } @@ -310,6 +299,11 @@ class GradleBuildCommand(GradleMixin, BuildCommand): def metadata_resource_path(self, app: AppConfig): return self.bundle_path(app) / self.path_index(app, "metadata_resource_path") + def metadata_extract_packages_path(self, app: AppConfig): + return self.bundle_path(app) / self.path_index( + app, "metadata_extract_packages_path" + ) + def update_app_metadata(self, app: AppConfig): with ( self.console.wait_bar("Setting main module..."), @@ -325,6 +319,23 @@ def update_app_metadata(self, app: AppConfig): """ ) + with ( + self.console.wait_bar("Setting packages to extract..."), + self.metadata_extract_packages_path(app).open("w", encoding="utf-8") as f, + ): + if app.debugger: + # In debug mode extract all source packages so that the debugger can get the source code + # at runtime. This is necessary for setting breakpoints in VSCode. + extract_packages = ["*"] + else: + # Extract test packages, to enable features like test discovery and assertion rewriting. + extract_sources = app.test_sources or [] + extract_packages = [ + name for path in extract_sources if (name := Path(path).name) + ] + + f.write("\n".join(extract_packages)) + def build_app(self, app: AppConfig, **kwargs): """Build an application. diff --git a/tests/platforms/android/gradle/conftest.py b/tests/platforms/android/gradle/conftest.py index cef53de31..8bbf666c6 100644 --- a/tests/platforms/android/gradle/conftest.py +++ b/tests/platforms/android/gradle/conftest.py @@ -50,6 +50,7 @@ def first_app_generated(first_app_config, tmp_path): app_packages_path="app_packages" support_path="support" metadata_resource_path="res/briefcase.xml" +metadata_extract_packages_path = "app/extract-packages.txt" """, ) @@ -64,4 +65,16 @@ def first_app_generated(first_app_config, tmp_path): / "briefcase.xml", """""", ) + + create_file( + tmp_path + / "base_path" + / "build" + / "first-app" + / "android" + / "gradle" + / "app" + / "extract-packages.txt", + "something-to-be-overwritten", + ) return first_app_config diff --git a/tests/platforms/android/gradle/test_build.py b/tests/platforms/android/gradle/test_build.py index c118701ae..fd32d1cd2 100644 --- a/tests/platforms/android/gradle/test_build.py +++ b/tests/platforms/android/gradle/test_build.py @@ -115,6 +115,18 @@ def test_build_app( + "\n" ) + with ( + tmp_path + / "base_path" + / "build" + / "first-app" + / "android" + / "gradle" + / "app" + / "extract-packages.txt" + ).open(encoding="utf-8") as f: + assert f.read() == "" + @pytest.mark.parametrize( ("host_os", "gradlew_name", "debug_mode"), @@ -135,6 +147,7 @@ def test_build_app_test_mode( ): """The app can be built in test mode, invoking gradle and rewriting app metadata.""" first_app_generated.test_mode = True + first_app_generated.test_sources = ["my_test_package"] # Mock out `host_os` so we can validate which name is used for gradlew. build_command.tools.host_os = host_os @@ -184,6 +197,97 @@ def test_build_app_test_mode( + "\n" ) + with ( + tmp_path + / "base_path" + / "build" + / "first-app" + / "android" + / "gradle" + / "app" + / "extract-packages.txt" + ).open(encoding="utf-8") as f: + assert f.read() == "my_test_package" + + +extract_packages_params = [ + ([], ""), + ([""], ""), + (["one"], "one"), + (["one/two"], "two"), + (["one//two"], "two"), + (["one/two/three"], "three"), + (["one", "two"], "one\ntwo"), + (["one", "two", "three"], "one\ntwo\nthree"), + (["one/two", "three/four"], "two\nfour"), + (["/leading"], "leading"), + (["/leading/two"], "two"), + (["/leading/two/three"], "three"), + (["trailing/"], "trailing"), + (["trailing//"], "trailing"), + (["trailing/two/"], "two"), +] + +# Handle differences in UNC path parsing (https://github.com/python/cpython/pull/100351). +extract_packages_params += [ + ( + ["//leading"], + "" if sys.platform == "win32" and sys.version_info >= (3, 12) else "leading", + ), + ( + ["//leading/two"], + "" if sys.platform == "win32" else "two", + ), + (["//leading/two/three"], "three"), + (["//leading/two/three/four"], "four"), +] + +if sys.platform == "win32": + extract_packages_params += [ + ([path.replace("/", "\\") for path in test_sources], expected) + for test_sources, expected in extract_packages_params + ] + + +@pytest.mark.parametrize(("test_sources", "expected"), extract_packages_params) +def test_extract_packages( + build_command, first_app_generated, test_sources, expected, tmp_path +): + first_app_generated.test_sources = test_sources + build_command.update_app_metadata(first_app_generated) + + with ( + tmp_path + / "base_path" + / "build" + / "first-app" + / "android" + / "gradle" + / "app" + / "extract-packages.txt" + ).open(encoding="utf-8") as f: + assert f.read() == expected + + +def test_extract_packages_debugger( + build_command, first_app_generated, dummy_debugger, tmp_path +): + first_app_generated.test_sources = ["one", "two", "three"] + first_app_generated.debugger = dummy_debugger + build_command.update_app_metadata(first_app_generated) + + with ( + tmp_path + / "base_path" + / "build" + / "first-app" + / "android" + / "gradle" + / "app" + / "extract-packages.txt" + ).open(encoding="utf-8") as f: + assert f.read() == "*" + def test_print_gradle_errors(build_command, first_app_generated): """Validate that build_app() will convert stderr/stdout from the process into diff --git a/tests/platforms/android/gradle/test_create.py b/tests/platforms/android/gradle/test_create.py index 1b82825a0..8055e7237 100644 --- a/tests/platforms/android/gradle/test_create.py +++ b/tests/platforms/android/gradle/test_create.py @@ -154,60 +154,6 @@ def test_build_gradle_dependencies( ) == has_warning -extract_packages_params = [ - ([], ""), - ([""], ""), - (["one"], '"one"'), - (["one/two"], '"two"'), - (["one//two"], '"two"'), - (["one/two/three"], '"three"'), - (["one", "two"], '"one", "two"'), - (["one", "two", "three"], '"one", "two", "three"'), - (["one/two", "three/four"], '"two", "four"'), - (["/leading"], '"leading"'), - (["/leading/two"], '"two"'), - (["/leading/two/three"], '"three"'), - (["trailing/"], '"trailing"'), - (["trailing//"], '"trailing"'), - (["trailing/two/"], '"two"'), -] - -# Handle differences in UNC path parsing (https://github.com/python/cpython/pull/100351). -extract_packages_params += [ - ( - ["//leading"], - "" if sys.platform == "win32" and sys.version_info >= (3, 12) else '"leading"', - ), - ( - ["//leading/two"], - "" if sys.platform == "win32" else '"two"', - ), - (["//leading/two/three"], '"three"'), - (["//leading/two/three/four"], '"four"'), -] - -if sys.platform == "win32": - extract_packages_params += [ - ([path.replace("/", "\\") for path in test_sources], expected) - for test_sources, expected in extract_packages_params - ] - - -@pytest.mark.parametrize(("test_sources", "expected"), extract_packages_params) -def test_extract_packages(create_command, first_app_config, test_sources, expected): - first_app_config.test_sources = test_sources - context = create_command.output_format_template_context(first_app_config) - assert context["extract_packages"] == expected - - -def test_extract_packages_debugger(create_command, first_app_config, dummy_debugger): - first_app_config.test_sources = ["one", "two", "three"] - first_app_config.sources = ["four", "five", "six"] - first_app_config.debugger = dummy_debugger - context = create_command.output_format_template_context(first_app_config) - assert context["extract_packages"] == '"one", "two", "three", "four", "five", "six"' - - @pytest.mark.parametrize( ("permissions", "features", "context"), [ From a3b998524c4123be0fd94545085948ae2bc93c4e Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:55:13 +0100 Subject: [PATCH 04/14] correct comment --- src/briefcase/platforms/android/gradle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index fa43175a7..86bff09c1 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -324,8 +324,8 @@ def update_app_metadata(self, app: AppConfig): self.metadata_extract_packages_path(app).open("w", encoding="utf-8") as f, ): if app.debugger: - # In debug mode extract all source packages so that the debugger can get the source code - # at runtime. This is necessary for setting breakpoints in VSCode. + # In debug mode include the .py files and extract all of them so that the debugger can get + # the source code at runtime. This is e.g. necessary for setting breakpoints in VS Code. extract_packages = ["*"] else: # Extract test packages, to enable features like test discovery and assertion rewriting. From c6ee46dfb8fc2bb3cda9a2990892f3d9b8182495 Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:00:02 +0100 Subject: [PATCH 05/14] change name to reflect the current changes in the template repo --- src/briefcase/platforms/android/gradle.py | 8 +++----- tests/platforms/android/gradle/conftest.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index 22449b91b..b2f16b35b 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -325,10 +325,8 @@ class GradleBuildCommand(GradleMixin, BuildCommand): def metadata_resource_path(self, app: AppConfig): return self.bundle_path(app) / self.path_index(app, "metadata_resource_path") - def metadata_extract_packages_path(self, app: AppConfig): - return self.bundle_path(app) / self.path_index( - app, "metadata_extract_packages_path" - ) + def extract_packages_path(self, app: AppConfig): + return self.bundle_path(app) / self.path_index(app, "extract_packages_path") def update_app_metadata(self, app: AppConfig): with ( @@ -347,7 +345,7 @@ def update_app_metadata(self, app: AppConfig): with ( self.console.wait_bar("Setting packages to extract..."), - self.metadata_extract_packages_path(app).open("w", encoding="utf-8") as f, + self.extract_packages_path(app).open("w", encoding="utf-8") as f, ): if app.debugger: # In debug mode include the .py files and extract all of them so that the debugger can get diff --git a/tests/platforms/android/gradle/conftest.py b/tests/platforms/android/gradle/conftest.py index 8bbf666c6..6b72b1db7 100644 --- a/tests/platforms/android/gradle/conftest.py +++ b/tests/platforms/android/gradle/conftest.py @@ -50,7 +50,7 @@ def first_app_generated(first_app_config, tmp_path): app_packages_path="app_packages" support_path="support" metadata_resource_path="res/briefcase.xml" -metadata_extract_packages_path = "app/extract-packages.txt" +extract_packages_path = "app/extract-packages.txt" """, ) From 8cd21712cf7424fa5a2603cb0bebafc4be995296 Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:14:49 +0100 Subject: [PATCH 06/14] fix platform target --- src/briefcase/platforms/android/gradle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index b2f16b35b..cd9c48a15 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -73,7 +73,7 @@ def android_log_clean_filter(line): class GradleMixin: output_format = "gradle" platform = "android" - platform_target_version = "0.3.15" + platform_target_version = "0.3.26" @property def packaging_formats(self): From 93a1ce3be422b9c2dd857accad41f7ecae90fbc1 Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:19:46 +0100 Subject: [PATCH 07/14] fix line length --- src/briefcase/platforms/android/gradle.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index cd9c48a15..5372e7fdd 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -348,11 +348,13 @@ def update_app_metadata(self, app: AppConfig): self.extract_packages_path(app).open("w", encoding="utf-8") as f, ): if app.debugger: - # In debug mode include the .py files and extract all of them so that the debugger can get - # the source code at runtime. This is e.g. necessary for setting breakpoints in VS Code. + # In debug mode include the .py files and extract all of them so + # that the debugger can get the source code at runtime. This is e.g. + # necessary for setting breakpoints in VS Code. extract_packages = ["*"] else: - # Extract test packages, to enable features like test discovery and assertion rewriting. + # Extract test packages, to enable features like test discovery and + # assertion rewriting. extract_sources = app.test_sources or [] extract_packages = [ name for path in extract_sources if (name := Path(path).name) From a544698816ba0c1cd72037f5cc1000ce585a4575 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:10:04 +0100 Subject: [PATCH 08/14] fixed typo --- docs/en/how-to/debugging/pdb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/how-to/debugging/pdb.md b/docs/en/how-to/debugging/pdb.md index 7e2fe642f..4c28dbc6c 100644 --- a/docs/en/how-to/debugging/pdb.md +++ b/docs/en/how-to/debugging/pdb.md @@ -16,7 +16,7 @@ This is currently an **experimental feature** that is only supported on Windows, To debug a bundled app, add `breakpoint()` somewhere in your code where the debugger should halt. -Your app must then be modified to include a bootstrap that will connect to the VS Code debugger. This is done by passing the `--debug debugpy` option to `briefcase build`: +Your app must then be modified to include a bootstrap that will connect to the VS Code debugger. This is done by passing the `--debug pdb` option to `briefcase build`: ```console $ briefcase build --debug pdb From 95daf27b868c21682175ff8e3e12826ed3cd5791 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:14:11 +0100 Subject: [PATCH 09/14] This PR has not made it in briefcase v0.3.26. So prepare it for v0.3.27. --- src/briefcase/platforms/android/gradle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index 5372e7fdd..cc64ce970 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -73,7 +73,7 @@ def android_log_clean_filter(line): class GradleMixin: output_format = "gradle" platform = "android" - platform_target_version = "0.3.26" + platform_target_version = "0.3.27" @property def packaging_formats(self): From ad1d88a240f3d548e332f938149baec22268c706 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:18:11 +0100 Subject: [PATCH 10/14] Add Android as supported in the docs --- docs/en/how-to/debugging/pdb.md | 2 +- docs/en/how-to/debugging/vscode.md | 2 +- docs/en/reference/commands/build.md | 2 +- docs/en/reference/commands/run.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/how-to/debugging/pdb.md b/docs/en/how-to/debugging/pdb.md index 4c28dbc6c..4668aec1b 100644 --- a/docs/en/how-to/debugging/pdb.md +++ b/docs/en/how-to/debugging/pdb.md @@ -10,7 +10,7 @@ To debug an app in development mode, add `breakpoint()` to your code somewhere t /// warning | Note -This is currently an **experimental feature** that is only supported on Windows, macOS and iOS. +This is currently an **experimental feature** that is only supported on Windows, macOS, iOS and Android. /// diff --git a/docs/en/how-to/debugging/vscode.md b/docs/en/how-to/debugging/vscode.md index 49fecb915..5d34fb7ee 100644 --- a/docs/en/how-to/debugging/vscode.md +++ b/docs/en/how-to/debugging/vscode.md @@ -40,7 +40,7 @@ To start a debug session, open the debug view in VS Code using the sidebar, sele /// warning | Experimental feature -This is currently an **experimental feature** that is only supported on Windows, macOS and iOS. +This is currently an **experimental feature** that is only supported on Windows, macOS, iOS and Android. /// diff --git a/docs/en/reference/commands/build.md b/docs/en/reference/commands/build.md index 25863ff4a..b2d1ceea2 100644 --- a/docs/en/reference/commands/build.md +++ b/docs/en/reference/commands/build.md @@ -105,7 +105,7 @@ Currently the following debuggers are supported: If calling only `--debug` without selecting a debugger explicitly, `pdb` is used as default. -This is an **experimental** new feature, that is currently only supported on Windows, macOS and iOS. +This is an **experimental** new feature, that is currently only supported on Windows, macOS, iOS and Android. This option may slow down the app a little bit. diff --git a/docs/en/reference/commands/run.md b/docs/en/reference/commands/run.md index 669ba9a11..aab0f5a77 100644 --- a/docs/en/reference/commands/run.md +++ b/docs/en/reference/commands/run.md @@ -109,7 +109,7 @@ Currently the following debuggers are supported: If calling only `--debug` without selecting a debugger explicitly, `pdb` is used as default. -This is an **experimental** new feature, that is currently only supported on Windows, macOS and iOS. +This is an **experimental** new feature, that is currently only supported on Windows, macOS, iOS and Android. The selected debugger in `run --debug ` has to match the selected debugger in `build --debug `. From 77b3b815e29dfa46950bd143a3251cbb7d2a4222 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Fri, 5 Dec 2025 15:49:02 +0100 Subject: [PATCH 11/14] "target_epoch" is not defined. The correct string is "target_version". --- tests/platforms/android/gradle/test_build.py | 2 +- tests/platforms/android/gradle/test_create.py | 2 +- tests/platforms/android/gradle/test_open.py | 2 +- tests/platforms/android/gradle/test_package__aab.py | 2 +- tests/platforms/android/gradle/test_package__apk.py | 2 +- tests/platforms/android/gradle/test_package__debug_apk.py | 2 +- tests/platforms/android/gradle/test_run.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/platforms/android/gradle/test_build.py b/tests/platforms/android/gradle/test_build.py index fd32d1cd2..1e8b2bc68 100644 --- a/tests/platforms/android/gradle/test_build.py +++ b/tests/platforms/android/gradle/test_build.py @@ -37,7 +37,7 @@ def test_unsupported_template_version(build_command, first_app_generated): build_command.verify_app = MagicMock(wraps=build_command.verify_app) build_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_create.py b/tests/platforms/android/gradle/test_create.py index 6db4aed9f..44832e213 100644 --- a/tests/platforms/android/gradle/test_create.py +++ b/tests/platforms/android/gradle/test_create.py @@ -38,7 +38,7 @@ def test_unsupported_template_version(create_command, first_app_config): create_command.verify_app = MagicMock(wraps=create_command.verify_app) create_command._briefcase_toml.update( - {first_app_config: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_config: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_open.py b/tests/platforms/android/gradle/test_open.py index eb1a14a29..011109a64 100644 --- a/tests/platforms/android/gradle/test_open.py +++ b/tests/platforms/android/gradle/test_open.py @@ -70,7 +70,7 @@ def test_unsupported_template_version(open_command, first_app_generated, tmp_pat open_command.verify_app = MagicMock(wraps=open_command.verify_app) open_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_package__aab.py b/tests/platforms/android/gradle/test_package__aab.py index 07a737b40..85ed717ca 100644 --- a/tests/platforms/android/gradle/test_package__aab.py +++ b/tests/platforms/android/gradle/test_package__aab.py @@ -23,7 +23,7 @@ def test_unsupported_template_version(package_command, first_app_generated): package_command.verify_app = MagicMock(wraps=package_command.verify_app) package_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_package__apk.py b/tests/platforms/android/gradle/test_package__apk.py index b3944ea99..48d0919a3 100644 --- a/tests/platforms/android/gradle/test_package__apk.py +++ b/tests/platforms/android/gradle/test_package__apk.py @@ -23,7 +23,7 @@ def test_unsupported_template_version(package_command, first_app_generated): package_command.verify_app = MagicMock(wraps=package_command.verify_app) package_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_package__debug_apk.py b/tests/platforms/android/gradle/test_package__debug_apk.py index 23d7b75a1..a611a2f22 100644 --- a/tests/platforms/android/gradle/test_package__debug_apk.py +++ b/tests/platforms/android/gradle/test_package__debug_apk.py @@ -23,7 +23,7 @@ def test_unsupported_template_version(package_command, first_app_generated): package_command.verify_app = MagicMock(wraps=package_command.verify_app) package_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( diff --git a/tests/platforms/android/gradle/test_run.py b/tests/platforms/android/gradle/test_run.py index f954c3100..91247e7d2 100644 --- a/tests/platforms/android/gradle/test_run.py +++ b/tests/platforms/android/gradle/test_run.py @@ -227,7 +227,7 @@ def test_unsupported_template_version(run_command, first_app_generated): run_command.verify_app = mock.MagicMock(wraps=run_command.verify_app) run_command._briefcase_toml.update( - {first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}} + {first_app_generated: {"briefcase": {"target_version": "0.3.16"}}} ) with pytest.raises( From 4637f793db330fba62972bc3a6f00540a5590bbb Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:04:47 +0100 Subject: [PATCH 12/14] fix target_version in unit test --- tests/platforms/android/gradle/test_open.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/platforms/android/gradle/test_open.py b/tests/platforms/android/gradle/test_open.py index 011109a64..94f4e5abb 100644 --- a/tests/platforms/android/gradle/test_open.py +++ b/tests/platforms/android/gradle/test_open.py @@ -39,9 +39,9 @@ def open_command(dummy_console, tmp_path, first_app_config): command.tools.subprocess = MagicMock(spec_set=Subprocess) command.tools.file.download = MagicMock(spec_set=File.download) - # Mock all apps as targeting version 0.3.15 + # Mock all apps as targeting version 0.3.27 command._briefcase_toml = defaultdict( - lambda: {"briefcase": {"target_version": "0.3.15"}} + lambda: {"briefcase": {"target_version": "0.3.27"}} ) # Mock some OS calls needed to make the tools appear to exist From cd8132c9b1dcbc01fe0b313eb72ecafa077d3c47 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:23:47 +0100 Subject: [PATCH 13/14] some change to trigger pipeline. --- src/briefcase/platforms/android/gradle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index cc64ce970..f8fab4356 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -349,8 +349,8 @@ def update_app_metadata(self, app: AppConfig): ): if app.debugger: # In debug mode include the .py files and extract all of them so - # that the debugger can get the source code at runtime. This is e.g. - # necessary for setting breakpoints in VS Code. + # that the debugger can get the source code at runtime. This is + # e.g. necessary for setting breakpoints in VS Code. extract_packages = ["*"] else: # Extract test packages, to enable features like test discovery and From 36cd78ca268136e5f3d6e6ef6858ccaa43efa961 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:50:46 +0100 Subject: [PATCH 14/14] remove unnecessary parameters --- tests/platforms/android/gradle/test_run.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/tests/platforms/android/gradle/test_run.py b/tests/platforms/android/gradle/test_run.py index 91247e7d2..8490c3e9a 100644 --- a/tests/platforms/android/gradle/test_run.py +++ b/tests/platforms/android/gradle/test_run.py @@ -265,8 +265,6 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=[], ) @@ -344,8 +342,6 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=["foo", "--bar"], ) @@ -449,8 +445,6 @@ def test_run_slow_start(run_command, first_app_config, monkeypatch): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=[], ) @@ -503,8 +497,6 @@ def test_run_crash_at_start(run_command, first_app_config, monkeypatch): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=[], ) @@ -542,9 +534,7 @@ def test_run_created_emulator(run_command, first_app_config): run_command.tools.mock_adb.logcat.return_value = log_popen # Invoke run_app - run_command.run_app( - first_app_config, debugger_host=None, debugger_port=None, passthrough=[] - ) + run_command.run_app(first_app_config, passthrough=[]) # A new emulator was created run_command.tools.android_sdk.create_emulator.assert_called_once_with() @@ -607,9 +597,7 @@ def test_run_idle_device(run_command, first_app_config): run_command.tools.mock_adb.logcat.return_value = log_popen # Invoke run_app - run_command.run_app( - first_app_config, debugger_host=None, debugger_port=None, passthrough=[] - ) + run_command.run_app(first_app_config, passthrough=[]) # No attempt was made to create a new emulator run_command.tools.android_sdk.create_emulator.assert_not_called() @@ -715,8 +703,6 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=[], shutdown_on_exit=True, ) @@ -791,8 +777,6 @@ def mock_stream_output(app, stop_func, **kwargs): run_command.run_app( first_app_config, device_or_avd="exampleDevice", - debugger_host=None, - debugger_port=None, passthrough=["foo", "--bar"], shutdown_on_exit=True, ) @@ -862,8 +846,6 @@ def test_run_test_mode_created_emulator(run_command, first_app_config): # Invoke run_app run_command.run_app( first_app_config, - debugger_host=None, - debugger_port=None, passthrough=[], extra_emulator_args=["-no-window", "-no-audio"], shutdown_on_exit=True,