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.
183 lines
6.6 KiB
Python
183 lines
6.6 KiB
Python
"""Tests for devices mixin (list, use, current, info, reboot, logcat)."""
|
|
|
|
import pytest
|
|
|
|
from tests.conftest import fail, ok
|
|
|
|
|
|
class TestDevicesList:
|
|
async def test_parse_devices(self, server):
|
|
server.run_adb.return_value = ok(
|
|
stdout=(
|
|
"List of devices attached\n"
|
|
"ABC123\tdevice\tmodel:Pixel_6 product:oriole\n"
|
|
"10.20.0.25:5555\tdevice\tmodel:K2401 product:K2401\n"
|
|
)
|
|
)
|
|
devices = await server.devices_list()
|
|
assert len(devices) == 2
|
|
assert devices[0].device_id == "ABC123"
|
|
assert devices[0].model == "Pixel_6"
|
|
assert devices[1].device_id == "10.20.0.25:5555"
|
|
|
|
async def test_empty(self, server):
|
|
server.run_adb.return_value = ok(stdout="List of devices attached\n")
|
|
devices = await server.devices_list()
|
|
assert len(devices) == 0
|
|
|
|
async def test_failure(self, server):
|
|
server.run_adb.return_value = fail("adb not found")
|
|
devices = await server.devices_list()
|
|
assert len(devices) == 0
|
|
|
|
|
|
class TestDevicesUse:
|
|
async def test_select_device(self, server):
|
|
server.run_adb.return_value = ok(
|
|
stdout="List of devices attached\nABC123\tdevice\n"
|
|
)
|
|
result = await server.devices_use("ABC123")
|
|
assert result.success is True
|
|
assert server.get_current_device() == "ABC123"
|
|
|
|
async def test_not_found(self, server):
|
|
server.run_adb.return_value = ok(
|
|
stdout="List of devices attached\nOTHER\tdevice\n"
|
|
)
|
|
result = await server.devices_use("MISSING")
|
|
assert result.success is False
|
|
assert "not found" in result.error
|
|
|
|
async def test_offline_device(self, server):
|
|
server.run_adb.return_value = ok(
|
|
stdout="List of devices attached\nABC123\toffline\n"
|
|
)
|
|
result = await server.devices_use("ABC123")
|
|
assert result.success is False
|
|
assert "offline" in result.error
|
|
|
|
|
|
class TestDevicesCurrent:
|
|
async def test_no_device_set(self, server):
|
|
server.run_adb.return_value = ok(stdout="List of devices attached\n")
|
|
result = await server.devices_current()
|
|
assert result.device is None
|
|
|
|
async def test_auto_detect_single(self, server):
|
|
server.run_adb.return_value = ok(
|
|
stdout="List of devices attached\nABC123\tdevice\n"
|
|
)
|
|
result = await server.devices_current()
|
|
assert result.available is not None
|
|
|
|
async def test_device_set(self, server):
|
|
# Pre-populate cache and set device
|
|
server.run_adb.return_value = ok(
|
|
stdout="List of devices attached\nABC123\tdevice\tmodel:Pixel\n"
|
|
)
|
|
await server.devices_list()
|
|
server.set_current_device("ABC123")
|
|
result = await server.devices_current()
|
|
# device is a dict from model_dump()
|
|
assert result.device["device_id"] == "ABC123"
|
|
|
|
|
|
class TestDeviceInfo:
|
|
async def test_full_info(self, server):
|
|
battery = (
|
|
"Current Battery Service state:\n level: 85\n status: 2\n plugged: 2"
|
|
)
|
|
df_out = (
|
|
"Filesystem 1K-blocks Used Available\n"
|
|
"/data 64000000 32000000 32000000"
|
|
)
|
|
server.run_shell_args.side_effect = [
|
|
ok(stdout=battery),
|
|
ok(stdout="10: wlan0 inet 192.168.1.100/24"),
|
|
ok(stdout="mWifiInfo SSID: MyNetwork, BSSID: ..."),
|
|
ok(stdout=df_out),
|
|
]
|
|
server.get_device_property.side_effect = lambda p, d=None: {
|
|
"ro.build.version.release": "14",
|
|
"ro.build.version.sdk": "34",
|
|
"ro.product.model": "Pixel 6",
|
|
"ro.product.manufacturer": "Google",
|
|
"ro.product.device": "oriole",
|
|
}.get(p)
|
|
|
|
result = await server.device_info()
|
|
assert result.success is True
|
|
assert result.battery["level"] == 85
|
|
assert result.ip_address == "192.168.1.100"
|
|
assert result.wifi_ssid == "MyNetwork"
|
|
assert result.model == "Pixel 6"
|
|
|
|
async def test_device_offline(self, server):
|
|
server.run_shell_args.return_value = fail("device offline")
|
|
result = await server.device_info()
|
|
assert result.success is False
|
|
|
|
|
|
class TestDeviceReboot:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_reboot(self, server, ctx):
|
|
ctx.set_elicit("accept", "Yes, reboot now")
|
|
server.run_adb.return_value = ok()
|
|
result = await server.device_reboot(ctx)
|
|
assert result.success is True
|
|
assert result.mode == "normal"
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_reboot_recovery(self, server, ctx):
|
|
ctx.set_elicit("accept", "Yes, reboot now")
|
|
server.run_adb.return_value = ok()
|
|
result = await server.device_reboot(ctx, mode="recovery")
|
|
assert result.mode == "recovery"
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_cancelled(self, server, ctx):
|
|
ctx.set_elicit("accept", "Cancel")
|
|
result = await server.device_reboot(ctx)
|
|
assert result.success is False
|
|
assert result.cancelled is True
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server, ctx):
|
|
result = await server.device_reboot(ctx)
|
|
assert result.success is False
|
|
|
|
|
|
class TestLogcat:
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_capture(self, server):
|
|
logline = "01-01 00:00:00.000 I/TAG: message"
|
|
server.run_shell_args.return_value = ok(stdout=logline)
|
|
result = await server.logcat_capture()
|
|
assert result.success is True
|
|
assert result.output.startswith("01-01")
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_with_filter(self, server):
|
|
server.run_shell_args.return_value = ok(stdout="filtered output")
|
|
result = await server.logcat_capture(filter_spec="MyApp:D *:S")
|
|
assert result.filter == "MyApp:D *:S"
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_clear_first(self, server):
|
|
server.run_shell_args.side_effect = [ok(), ok(stdout="fresh logs")]
|
|
result = await server.logcat_capture(clear_first=True)
|
|
assert result.success is True
|
|
assert server.run_shell_args.call_count == 2
|
|
|
|
@pytest.mark.usefixtures("_no_dev_mode")
|
|
async def test_requires_dev_mode(self, server):
|
|
result = await server.logcat_capture()
|
|
assert result.success is False
|
|
|
|
@pytest.mark.usefixtures("_dev_mode")
|
|
async def test_logcat_clear(self, server):
|
|
server.run_shell_args.return_value = ok()
|
|
result = await server.logcat_clear()
|
|
assert result.success is True
|
|
assert result.action == "logcat_clear"
|