Every tool now returns a structured BaseModel instead of dict[str, Any], giving callers attribute access, IDE autocomplete, and schema validation. Adds ~30 model classes to models.py and updates all test assertions.
234 lines
8.5 KiB
Python
234 lines
8.5 KiB
Python
"""Tests for apps mixin (launch, close, current, install, intents)."""
|
|
|
|
import pytest
|
|
|
|
from tests.conftest import fail, ok
|
|
|
|
|
|
class TestAppLaunch:
|
|
async def test_launch(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.app_launch("com.android.chrome")
|
|
assert result.success is True
|
|
assert result.package == "com.android.chrome"
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "monkey" in args
|
|
assert "com.android.chrome" in args
|
|
|
|
async def test_failure(self, server):
|
|
server.run_shell_args.return_value = fail("not found")
|
|
result = await server.app_launch("com.missing.app")
|
|
assert result.success is False
|
|
|
|
|
|
class TestAppOpenUrl:
|
|
async def test_open(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.app_open_url("https://example.com")
|
|
assert result.success is True
|
|
assert result.url == "https://example.com"
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "am" in args
|
|
assert "android.intent.action.VIEW" in args
|
|
|
|
|
|
class TestAppClose:
|
|
async def test_close(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.app_close("com.example.app")
|
|
assert result.success is True
|
|
assert result.package == "com.example.app"
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "am" in args
|
|
assert "force-stop" in args
|
|
|
|
|
|
class TestAppCurrent:
|
|
async def test_parse_focused(self, server):
|
|
focused = (
|
|
" mCurrentFocus=Window{abc com.android.chrome"
|
|
"/org.chromium.chrome.browser.ChromeTabbedActivity}"
|
|
)
|
|
server.run_shell_args.return_value = ok(stdout=focused)
|
|
result = await server.app_current()
|
|
assert result.success is True
|
|
assert result.package == "com.android.chrome"
|
|
|
|
async def test_focused_app_format(self, server):
|
|
server.run_shell_args.return_value = ok(
|
|
stdout=" mFocusedApp=ActivityRecord{abc com.example/.MainActivity t123}"
|
|
)
|
|
result = await server.app_current()
|
|
assert result.success is True
|
|
assert result.package == "com.example"
|
|
|
|
async def test_no_focus(self, server):
|
|
server.run_shell_args.return_value = ok(stdout="no focus info")
|
|
result = await server.app_current()
|
|
assert result.success is True
|
|
assert result.package is None
|
|
|
|
|
|
class TestAppListPackages:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_list(self, server):
|
|
server.run_shell_args.return_value = ok(
|
|
stdout="package:com.android.chrome\npackage:com.example.app\n"
|
|
)
|
|
result = await server.app_list_packages()
|
|
assert result.success is True
|
|
assert result.count == 2
|
|
assert "com.android.chrome" in result.packages
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_filter(self, server):
|
|
server.run_shell_args.return_value = ok(
|
|
stdout="package:com.android.chrome\npackage:com.example.app\n"
|
|
)
|
|
result = await server.app_list_packages(filter_text="chrome")
|
|
assert result.count == 1
|
|
assert "com.android.chrome" in result.packages
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_third_party(self, server):
|
|
server.run_shell_args.return_value = ok(stdout="package:com.user.app\n")
|
|
await server.app_list_packages(third_party_only=True)
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "-3" in args
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server):
|
|
result = await server.app_list_packages()
|
|
assert result.success is False
|
|
|
|
|
|
class TestAppInstall:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_install(self, server):
|
|
server.run_adb.return_value = ok(stdout="Success")
|
|
result = await server.app_install("/tmp/app.apk")
|
|
assert result.success is True
|
|
args = server.run_adb.call_args[0][0]
|
|
assert "install" in args
|
|
assert "-r" in args
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server):
|
|
result = await server.app_install("/tmp/app.apk")
|
|
assert result.success is False
|
|
|
|
|
|
class TestAppUninstall:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_uninstall(self, server, ctx):
|
|
ctx.set_elicit("accept", "Yes, uninstall")
|
|
server.run_adb.return_value = ok()
|
|
result = await server.app_uninstall(ctx, "com.example.app")
|
|
assert result.success is True
|
|
assert result.package == "com.example.app"
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_keep_data(self, server, ctx):
|
|
ctx.set_elicit("accept", "Yes, uninstall")
|
|
server.run_adb.return_value = ok()
|
|
result = await server.app_uninstall(ctx, "com.example.app", keep_data=True)
|
|
assert result.kept_data is True
|
|
args = server.run_adb.call_args[0][0]
|
|
assert "-k" in args
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_cancelled(self, server, ctx):
|
|
ctx.set_elicit("accept", "Cancel")
|
|
result = await server.app_uninstall(ctx, "com.example.app")
|
|
assert result.success is False
|
|
assert result.cancelled is True
|
|
|
|
|
|
class TestAppClearData:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_clear(self, server, ctx):
|
|
ctx.set_elicit("accept", "Yes, clear all data")
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.app_clear_data(ctx, "com.example.app")
|
|
assert result.success is True
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_cancelled(self, server, ctx):
|
|
ctx.set_elicit("accept", "Cancel")
|
|
result = await server.app_clear_data(ctx, "com.example.app")
|
|
assert result.cancelled is True
|
|
|
|
|
|
class TestActivityStart:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_basic(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.activity_start("com.example/.MainActivity")
|
|
assert result.success is True
|
|
assert result.component == "com.example/.MainActivity"
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "am" in args
|
|
assert "start" in args
|
|
assert "-n" in args
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_with_action_and_data(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
await server.activity_start(
|
|
"com.example/.DeepLink",
|
|
action="android.intent.action.VIEW",
|
|
data_uri="myapp://product/123",
|
|
)
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "-a" in args
|
|
assert "-d" in args
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_with_extras(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
await server.activity_start(
|
|
"com.example/.Act",
|
|
extras={"key": "value", "flag": "true", "count": "42"},
|
|
)
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "--es" in args # string extra
|
|
assert "--ez" in args # boolean extra
|
|
assert "--ei" in args # integer extra
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_with_flags(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
await server.activity_start(
|
|
"com.example/.Act",
|
|
flags=["FLAG_ACTIVITY_NEW_TASK"],
|
|
)
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "-f" in args
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server):
|
|
result = await server.activity_start("com.example/.Act")
|
|
assert result.success is False
|
|
|
|
|
|
class TestBroadcastSend:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_basic(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.broadcast_send("com.example.ACTION")
|
|
assert result.success is True
|
|
assert result.broadcast_action == "com.example.ACTION"
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_with_package(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
await server.broadcast_send("ACTION", package="com.target")
|
|
args = server.run_shell_args.call_args[0][0]
|
|
assert "-p" in args
|
|
assert "com.target" in args
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server):
|
|
result = await server.broadcast_send("ACTION")
|
|
assert result.success is False
|