mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
136 lines
6.2 KiB
Python
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()
|