Files
DroidDeck/tools/tests/test_device_smoke.py
T

136 lines
6.2 KiB
Python

import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / "device-smoke.sh"
FAKE_ADB = r'''#!/usr/bin/env python3
import json, os, shlex, sys
from pathlib import Path
p = Path(os.environ["SMOKE_STATE"])
s = json.loads(p.read_text())
a = sys.argv[1:]
if a == ["get-state"]:
print("device")
elif a[:1] == ["shell"]:
a = shlex.split(a[1])
if a[:1] == ["wm"]:
kind = a[1]
if len(a) == 2:
if s.get("unreadable") == kind:
print("permission denied")
else:
print("Physical " + kind + ": " + ("400" if kind == "density" else "1080x1920"))
if s.get(kind): print("Override " + kind + ": " + s[kind])
else:
value = a[2]
s["writes"].append([kind, value])
restoring = value in ("reset", "420", "1000x1800")
if not (s.get("ignored") == kind or restoring and s.get("restore_ignored") == kind):
s[kind] = "" if value == "reset" else value
p.write_text(json.dumps(s))
elif a[:1] == ["getconf"]: print("4096")
elif a[:1] == ["pidof"]: print("123")
elif a[:2] == ["dumpsys", "activity"]:
print("topResumedActivity=ActivityRecord{abc u0 com.droiddeck.launcher/.MainActivity t1}")
elif a[:3] == ["pm", "list", "packages"]:
if s.get("query_failed"): sys.exit(1)
print("package:" + s.get("package", "com.droiddeck.launcher"))
elif a[:2] == ["am", "start"] and s.get("launch_failed"):
print("Error: activity could not be started")
sys.exit(1 if s["launch_failed"] == "exit" else 0)
elif a[:1] == ["run-as"]:
if s.get("session"):
s.setdefault("log_commands", []).append(a)
p.write_text(json.dumps(s))
if "ls" in a: print(s["session"])
elif a[-1].endswith("/device.txt"): print(s.get("report", ""))
elif a[-1].endswith("/app.log"): print(s.get("app_log", ""))
else: print("package not debuggable")
'''
class DeviceSmokeTest(unittest.TestCase):
def run_smoke(self, **scenario):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
adb = root / "adb"
adb.write_text(FAKE_ADB)
adb.chmod(0o755)
sleep = root / "sleep"
sleep.write_text("#!/bin/sh\nexit 0\n")
sleep.chmod(0o755)
state = root / "state.json"
state.write_text(json.dumps({"density": "", "size": "", "writes": [], **scenario}))
env = dict(os.environ, ADB=str(adb), SMOKE_STATE=str(state), PATH=str(root) + os.pathsep + os.environ["PATH"])
result = subprocess.run(["bash", str(SCRIPT), "--no-fold"], env=env, capture_output=True, text=True, timeout=30)
return result, json.loads(state.read_text())
def test_restores_preexisting_display_overrides(self):
result, state = self.run_smoke(density="420", size="1000x1800")
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
self.assertEqual("420", state["density"])
self.assertEqual("1000x1800", state["size"])
def test_ignored_display_changes_do_not_pass(self):
for kind in ("density", "size"):
with self.subTest(kind=kind):
result, state = self.run_smoke(ignored=kind)
self.assertEqual(1, result.returncode, result.stdout)
self.assertIn("wm " + kind + " failed", result.stdout)
self.assertNotIn("PASS same process " + ("after" if kind == "density" else "at"), result.stdout)
self.assertEqual("", state[kind])
def test_ignored_restore_remains_a_failure_and_is_retried_on_exit(self):
for kind in ("density", "size"):
with self.subTest(kind=kind):
result, state = self.run_smoke(restore_ignored=kind)
self.assertEqual(1, result.returncode, result.stdout)
self.assertIn("could not restore", result.stdout)
self.assertNotEqual("", state[kind])
self.assertEqual(2, state["writes"].count([kind, "reset"]))
def test_unreadable_original_settings_are_not_changed(self):
for kind in ("density", "size"):
with self.subTest(kind=kind):
result, state = self.run_smoke(unreadable=kind)
self.assertEqual(0, result.returncode, result.stdout)
self.assertFalse(any(item[0] == kind for item in state["writes"]))
def test_package_filter_does_not_accept_another_variant_or_a_failed_query(self):
for scenario in ({"package": "com.droiddeck.launcher.debug"}, {"query_failed": True}):
with self.subTest(scenario=scenario):
result, state = self.run_smoke(**scenario)
self.assertEqual(2, result.returncode, result.stdout)
self.assertEqual([], state["writes"])
def test_failed_launch_is_not_masked_by_an_existing_process(self):
for failure in ("exit", "message"):
with self.subTest(failure=failure):
result, _ = self.run_smoke(launch_failed=failure)
self.assertEqual(1, result.returncode, result.stdout)
self.assertIn("could not launch", result.stdout)
def test_fast_path_requires_a_session_activation_log(self):
for active in (False, True):
with self.subTest(active=active):
result, _ = self.run_smoke(
session="session", report="proot fast path (eligible) true",
app_log="proot: fast path on (66 binds)" if active else "proot: fast path off - too many binds",
)
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
self.assertEqual(active, "PASS proot fast path was on" in result.stdout)
def test_session_name_remains_one_remote_argument(self):
session = "x';id;#"
result, state = self.run_smoke(session=session)
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
self.assertIn(["run-as", "com.droiddeck.launcher", "cat", "files/logs/" + session + "/events.jsonl"], state["log_commands"])
if __name__ == "__main__":
unittest.main()