feat: support orientation-aware recording and device wakefulness
Roll scrcpy recordings by device rotation and duration, and publish timeline manifests for playback and analysis. Add seamless segmented playback in the showcase UI and configurable stay-awake strategies for Android automation.
This commit is contained in:
1 parent
160f43a74a
commit
474e56ef8e
14 files changed
+1150
-310
No files matched your search
@@ -0,0 +1,165 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from artemis.clients.ui_automator_client import (
|
||||
AWAKE_STRATEGY_HEARTBEAT,
|
||||
AWAKE_STRATEGY_STAY_ON,
|
||||
AWAKE_STRATEGY_WAKE_LOCK,
|
||||
UIAutomatorClient,
|
||||
_keep_device_awake,
|
||||
)
|
||||
from artemis.drivers.android.adb_driver import AndroidAdbDriver
|
||||
|
||||
|
||||
def completed(stdout="", returncode=0, stderr=""):
|
||||
return MagicMock(returncode=returncode, stdout=stdout, stderr=stderr)
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.subprocess.run")
|
||||
def test_keep_device_awake_uses_verified_primary_strategy(mock_run):
|
||||
mock_run.side_effect = [
|
||||
completed(),
|
||||
completed(),
|
||||
completed(),
|
||||
completed(" mStayOn=true\n"),
|
||||
]
|
||||
|
||||
strategy = _keep_device_awake("device-123")
|
||||
|
||||
assert strategy == AWAKE_STRATEGY_STAY_ON
|
||||
assert mock_run.call_count == 4
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.subprocess.run")
|
||||
def test_keep_device_awake_falls_back_to_verified_screen_wake_lock(mock_run):
|
||||
mock_run.side_effect = [
|
||||
completed(),
|
||||
completed(),
|
||||
completed(),
|
||||
completed(" mStayOn=false\n"),
|
||||
completed("WakeLock{abc held=true, refCount=1}\n"),
|
||||
completed(
|
||||
"Display 0, wakelock type: SCREEN_BRIGHT_WAKE_LOCK: "
|
||||
"WakeLock{abc held=true, refCount=1}\n"
|
||||
),
|
||||
]
|
||||
|
||||
strategy = _keep_device_awake("device-123")
|
||||
|
||||
assert strategy == AWAKE_STRATEGY_WAKE_LOCK
|
||||
assert mock_run.call_args_list[-2].args[0] == [
|
||||
"adb",
|
||||
"-s",
|
||||
"device-123",
|
||||
"shell",
|
||||
"cmd",
|
||||
"power",
|
||||
"set-wakelock",
|
||||
"acquire",
|
||||
"-d",
|
||||
"0",
|
||||
"SCREEN_BRIGHT_WAKE_LOCK",
|
||||
]
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.subprocess.run")
|
||||
def test_keep_device_awake_uses_heartbeat_when_wake_lock_is_unavailable(mock_run):
|
||||
mock_run.side_effect = [
|
||||
completed(),
|
||||
completed(),
|
||||
completed(),
|
||||
completed(" mStayOn=false\n"),
|
||||
completed(returncode=1, stderr="unknown command"),
|
||||
completed("Wakelocks:\n"),
|
||||
]
|
||||
|
||||
strategy = _keep_device_awake("device-123")
|
||||
|
||||
assert strategy == AWAKE_STRATEGY_HEARTBEAT
|
||||
|
||||
|
||||
@patch.dict("os.environ", {"ARTEMIS_KEEP_DEVICE_AWAKE": "false"})
|
||||
@patch("artemis.clients.ui_automator_client.subprocess.run")
|
||||
def test_keep_device_awake_can_be_disabled(mock_run):
|
||||
assert _keep_device_awake("device-123") is None
|
||||
mock_run.assert_not_called()
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.u2.connect")
|
||||
@patch(
|
||||
"artemis.clients.ui_automator_client._keep_device_awake",
|
||||
return_value=AWAKE_STRATEGY_HEARTBEAT,
|
||||
)
|
||||
@patch("artemis.clients.ui_automator_client._ensure_maestro_not_installed")
|
||||
def test_new_ui_connection_starts_heartbeat_fallback(
|
||||
mock_remove_maestro, mock_keep_awake, mock_connect
|
||||
):
|
||||
client = UIAutomatorClient("device-123")
|
||||
client._start_awake_heartbeat = MagicMock()
|
||||
|
||||
client._ensure_connected()
|
||||
|
||||
mock_remove_maestro.assert_called_once_with("device-123")
|
||||
mock_keep_awake.assert_called_once_with("device-123")
|
||||
mock_connect.assert_called_once_with("device-123")
|
||||
client._start_awake_heartbeat.assert_called_once_with()
|
||||
assert client._awake_strategy == AWAKE_STRATEGY_HEARTBEAT
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client._release_screen_wake_lock")
|
||||
def test_disconnect_releases_screen_wake_lock(mock_release):
|
||||
client = UIAutomatorClient("device-123")
|
||||
client._awake_strategy = AWAKE_STRATEGY_WAKE_LOCK
|
||||
client._stop_awake_heartbeat = MagicMock()
|
||||
|
||||
client.disconnect()
|
||||
|
||||
client._stop_awake_heartbeat.assert_called_once_with()
|
||||
mock_release.assert_called_once_with("device-123")
|
||||
assert client._awake_strategy is None
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.threading.Thread")
|
||||
@patch("artemis.clients.ui_automator_client.threading.Event")
|
||||
def test_heartbeat_thread_is_daemonized_and_stoppable(mock_event, mock_thread):
|
||||
stop_event = MagicMock()
|
||||
thread = MagicMock()
|
||||
mock_event.return_value = stop_event
|
||||
mock_thread.return_value = thread
|
||||
client = UIAutomatorClient("device-123")
|
||||
|
||||
client._start_awake_heartbeat()
|
||||
client._stop_awake_heartbeat()
|
||||
|
||||
assert mock_thread.call_args.kwargs["daemon"] is True
|
||||
thread.start.assert_called_once_with()
|
||||
stop_event.set.assert_called_once_with()
|
||||
thread.join.assert_called_once_with(timeout=2.0)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_android_driver_disconnect_cleans_up_ui_client():
|
||||
ui_client = MagicMock()
|
||||
driver = AndroidAdbDriver(
|
||||
device_id="device-123",
|
||||
adb_client=MagicMock(),
|
||||
ui_adb_client=ui_client,
|
||||
)
|
||||
|
||||
await driver.disconnect()
|
||||
|
||||
ui_client.disconnect.assert_called_once_with()
|
||||
|
||||
|
||||
@patch("artemis.clients.ui_automator_client.subprocess.run")
|
||||
def test_keep_device_awake_continues_after_an_adb_failure(mock_run):
|
||||
mock_run.side_effect = [
|
||||
completed(returncode=1, stderr="not allowed"),
|
||||
OSError("adb temporarily unavailable"),
|
||||
completed(),
|
||||
completed("mStayOn=true\n"),
|
||||
]
|
||||
|
||||
assert _keep_device_awake("device-123") == AWAKE_STRATEGY_STAY_ON
|
||||
assert mock_run.call_count == 4
|
||||
Reference in new issue
Block a user