Files
somew1nd 67248c887e feat: improve task execution, verification and packaging
Add execution incidents, action bursts and shared Flash history.

Expose verification and Explorer settings with streamed check results.

Support graceful cancellation, recording recovery and bundled UI resources.

Add CI quality checks and clean up comments and documentation.
2026-09-02 21:45:51 -07:00

176 lines
5.9 KiB
Python

# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Unit tests for RemoteAdbDevice, RemoteAdbClient, and platform specific commands parity.
"""
from unittest.mock import MagicMock
import pytest
pytestmark = [pytest.mark.integration, pytest.mark.cloud]
pytest.importorskip(
"cloud_service",
reason="optional cloud_service package is not installed",
)
from cloud_service.virtualization.remote_adb import (
RemoteAdbDevice,
RemoteAdbClient,
)
from artemis.context import ArtemisContext, DeviceContext, DevicePlatform
from artemis.controllers.platform_specific_commands_controller import (
get_adb_device,
list_packages,
get_current_foreground_package,
get_device_date,
)
def test_remote_adb_device_shell():
mock_bridge = MagicMock()
mock_bridge.execute_sync.return_value = {
"status": "SUCCESS",
"output": "package:/data/app/com.android.chrome.apk=com.android.chrome\npackage:/data/app/com.android.settings.apk=com.android.settings",
}
device = RemoteAdbDevice(serial="emulator-5554", bridge=mock_bridge)
output = device.shell("pm list packages -f")
assert "com.android.chrome" in output
mock_bridge.execute_sync.assert_called_once_with("shell", {"command": "pm list packages -f"})
def test_remote_adb_device_current_app():
mock_bridge = MagicMock()
mock_bridge.execute_sync.return_value = {
"status": "SUCCESS",
"package": "com.android.chrome",
"activity": "com.google.android.apps.chrome.Main",
}
device = RemoteAdbDevice(serial="emulator-5554", bridge=mock_bridge)
app = device.current_app()
assert app is not None
assert app.package == "com.android.chrome"
assert app.activity == "com.google.android.apps.chrome.Main"
def test_remote_adb_device_window_size():
mock_bridge = MagicMock()
mock_bridge.execute_sync.return_value = {"status": "SUCCESS", "width": 1440, "height": 3120}
device = RemoteAdbDevice(serial="emulator-5554", bridge=mock_bridge)
size = device.window_size()
assert size == (1440, 3120)
def test_platform_commands_parity_with_remote_device(monkeypatch):
monkeypatch.setenv("ARTEMIS_CLOUD_MODE", "1")
monkeypatch.setenv("ARTEMIS_CLOUD_SESSION_ID", "session_parity_001")
monkeypatch.setenv("ADB_DEVICE_SERIAL", "emulator-5554")
mock_bridge = MagicMock()
mock_bridge.execute_sync.side_effect = [
# 1. date
{"status": "SUCCESS", "output": "Thu Aug 6 12:00:00 UTC 2026\n"},
# 2. pm list packages
{
"status": "SUCCESS",
"output": "package:/data/app/com.example.app1.apk=com.example.app1\npackage:/data/app/com.example.app2.apk=com.example.app2",
},
# 3. current_app
{"status": "SUCCESS", "package": "com.example.app1", "activity": ".MainActivity"},
]
client = RemoteAdbClient(bridge=mock_bridge)
device = client.device("emulator-5554")
ctx = ArtemisContext(
device=DeviceContext(
host_platform="LINUX",
mobile_platform=DevicePlatform.ANDROID,
device_id="emulator-5554",
device_width=1080,
device_height=2400,
),
adb_client=client,
)
dev = get_adb_device(ctx)
assert dev.serial == "emulator-5554"
# Verify real dynamic date
date_str = get_device_date(ctx)
assert "2026" in date_str
# Verify real dynamic package listing (Zero hardcoding)
pkgs = list_packages(ctx)
assert pkgs == "com.example.app1\ncom.example.app2"
# Verify real dynamic foreground package detection (Zero retry loop)
fg_pkg = get_current_foreground_package(ctx)
assert fg_pkg == "com.example.app1"
def test_remote_uiautomator_client_parity():
"""Verify RemoteUIAutomatorClient correctly returns structured UIAutomatorScreenData."""
from cloud_service.virtualization.remote_adb import RemoteUIAutomatorClient
mock_bridge = MagicMock()
mock_bridge.execute_sync.return_value = {
"status": "SUCCESS",
"base64": "fake_b64",
"hierarchy_xml": "<hierarchy><node text='Search' bounds='[100,200][300,400]'/></hierarchy>",
"elements": [{"text": "Search", "bounds": "[100,200][300,400]"}],
"width": 1080,
"height": 2400,
}
client = RemoteUIAutomatorClient(bridge=mock_bridge)
b64, xml, elements = client.get_screen_data()
assert b64 == "fake_b64"
assert "<node text='Search'" in xml
assert len(elements) == 1
assert elements[0]["text"] == "Search"
def test_controller_factory_cloud_mode_uses_unified_controller(monkeypatch):
"""Verify controller_factory creates UnifiedMobileController in Cloud Mode."""
monkeypatch.setenv("ARTEMIS_CLOUD_MODE", "1")
monkeypatch.setenv("ARTEMIS_CLOUD_SESSION_ID", "test_cloud_session")
monkeypatch.setenv("ADB_DEVICE_SERIAL", "emulator-5554")
from artemis.controllers.controller_factory import create_device_controller
from artemis.controllers.unified_controller import UnifiedMobileController
ctx = ArtemisContext(
device=DeviceContext(
host_platform="LINUX",
mobile_platform=DevicePlatform.ANDROID,
device_id="emulator-5554",
device_width=1080,
device_height=2400,
)
)
controller = create_device_controller(ctx)
assert isinstance(controller, UnifiedMobileController)
assert controller.driver.device_id == "emulator-5554"