1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
import os
import sys
from subprocess import CalledProcessError
from unittest.mock import MagicMock, PropertyMock
import httpx
import pytest
from briefcase.console import LogLevel
from briefcase.exceptions import BriefcaseCommandError
from briefcase.integrations.android_sdk import AndroidSDK
from briefcase.integrations.subprocess import Subprocess
from briefcase.platforms.android.gradle import GradleBuildCommand
@pytest.fixture
def build_command(dummy_console, tmp_path, first_app_generated, monkeypatch):
command = GradleBuildCommand(
console=dummy_console,
base_path=tmp_path / "base_path",
data_path=tmp_path / "briefcase",
)
command.tools.android_sdk = MagicMock(spec_set=AndroidSDK)
command.tools.os = MagicMock(spec_set=os)
command.tools.os.environ = {}
command.tools.sys = MagicMock(spec_set=sys)
command.tools.httpx = MagicMock(spec_set=httpx)
command.tools.subprocess = MagicMock(spec_set=Subprocess)
monkeypatch.setattr(
type(command.tools), "system_encoding", PropertyMock(return_value="ISO-42")
)
return command
def test_unsupported_template_version(build_command, first_app_generated):
"""Error raised if template's target version is not supported."""
build_command.verify_app = MagicMock(wraps=build_command.verify_app)
build_command._briefcase_toml.update(
{first_app_generated: {"briefcase": {"target_epoch": "0.3.16"}}}
)
with pytest.raises(
BriefcaseCommandError,
match="The app template used to generate this app is not compatible",
):
build_command(first_app_generated)
build_command.verify_app.assert_called_once_with(first_app_generated)
@pytest.mark.parametrize(
"host_os, gradlew_name, tool_debug_mode",
[
("Windows", "gradlew.bat", True),
("Windows", "gradlew.bat", False),
("NonWindows", "gradlew", True),
("NonWindows", "gradlew", False),
],
)
def test_build_app(
build_command,
first_app_generated,
host_os,
gradlew_name,
tool_debug_mode,
tmp_path,
):
"""The app can be built, invoking gradle."""
# Mock out `host_os` so we can validate which name is used for gradlew.
build_command.tools.host_os = host_os
# Enable verbose tool logging
if tool_debug_mode:
build_command.tools.console.verbosity = LogLevel.DEEP_DEBUG
# Create mock environment with `key`, which we expect to be preserved, and
# `ANDROID_SDK_ROOT`, which we expect to be overwritten.
build_command.tools.os.environ = {"ANDROID_SDK_ROOT": "somewhere", "key": "value"}
build_command.build_app(first_app_generated)
build_command.tools.android_sdk.verify_emulator.assert_called_once_with()
build_command.tools.subprocess.run.assert_called_once_with(
[
build_command.bundle_path(first_app_generated) / gradlew_name,
"--console",
"plain",
]
+ (["--debug"] if tool_debug_mode else [])
+ ["assembleDebug"],
cwd=build_command.bundle_path(first_app_generated),
env=build_command.tools.android_sdk.env,
check=True,
encoding="ISO-42",
)
# The app metadata contains the app module
# The app metadata has been rewritten to reference the test module
with (
tmp_path
/ "base_path"
/ "build"
/ "first-app"
/ "android"
/ "gradle"
/ "res"
/ "briefcase.xml"
).open(encoding="utf-8") as f:
assert (
f.read()
== "\n".join(
[
"<resources>",
' <string name="main_module">first_app</string>',
"</resources>",
]
)
+ "\n"
)
@pytest.mark.parametrize(
"host_os, gradlew_name, debug_mode",
[
("Windows", "gradlew.bat", True),
("Windows", "gradlew.bat", False),
("NonWindows", "gradlew", True),
("NonWindows", "gradlew", False),
],
)
def test_build_app_test_mode(
build_command,
first_app_generated,
host_os,
gradlew_name,
debug_mode,
tmp_path,
):
"""The app can be built in test mode, invoking gradle and rewriting app metadata."""
first_app_generated.test_mode = True
# Mock out `host_os` so we can validate which name is used for gradlew.
build_command.tools.host_os = host_os
# Enable verbose tool logging
if debug_mode:
build_command.tools.console.verbosity = LogLevel.DEEP_DEBUG
# Create mock environment with `key`, which we expect to be preserved, and
# `ANDROID_SDK_ROOT`, which we expect to be overwritten.
build_command.tools.os.environ = {"ANDROID_SDK_ROOT": "somewhere", "key": "value"}
build_command.build_app(first_app_generated)
build_command.tools.android_sdk.verify_emulator.assert_called_once_with()
build_command.tools.subprocess.run.assert_called_once_with(
[
build_command.bundle_path(first_app_generated) / gradlew_name,
"--console",
"plain",
]
+ (["--debug"] if debug_mode else [])
+ ["assembleDebug"],
cwd=build_command.bundle_path(first_app_generated),
env=build_command.tools.android_sdk.env,
check=True,
encoding="ISO-42",
)
# The app metadata contains the app module
# The app metadata has been rewritten to reference the test module
with (
tmp_path
/ "base_path"
/ "build"
/ "first-app"
/ "android"
/ "gradle"
/ "res"
/ "briefcase.xml"
).open(encoding="utf-8") as f:
assert (
f.read()
== "\n".join(
[
"<resources>",
' <string name="main_module">tests.first_app</string>',
"</resources>",
]
)
+ "\n"
)
def test_print_gradle_errors(build_command, first_app_generated):
"""Validate that build_app() will convert stderr/stdout from the process into
exception text."""
# Create a mock subprocess that crashes, printing text partly in non-ASCII.
build_command.tools.subprocess.run.side_effect = CalledProcessError(
returncode=1,
cmd=["ignored"],
)
with pytest.raises(BriefcaseCommandError):
build_command.build_app(first_app_generated)
|