同步移动端工程、设备控制改进与发布资料
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
"""Run registered BLE tests; WiFi reapplication requires its explicit option."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent / '测试设备登记'))
|
||||
from prepare_test_device import load_registration, check_connected
|
||||
from remote_support import connect, execute
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--adb', required=True)
|
||||
parser.add_argument('--build', type=Path, required=True)
|
||||
parser.add_argument('--poll-cycles', type=int, default=60)
|
||||
parser.add_argument('--test', choices=('ble', 'activity', 'layout'), default='ble')
|
||||
parser.add_argument('--root-input', action='store_true', help='Compatibility flag; activity tests always use authorized root input')
|
||||
parser.add_argument('--reapply-current-wifi', action='store_true', help='Explicitly reapply the already active saved WiFi profile over BLE; may briefly reconnect WiFi')
|
||||
parser.add_argument('--verify-current-controls', action='store_true', help='Reapply current brightness; briefly pause active animation and restore without selecting new content')
|
||||
parser.add_argument('--toggle-bluetooth', action='store_true', help='Requires explicit root Bluetooth-switch authorization plus activity root input')
|
||||
parser.add_argument('--grant-root', action='store_true', help='Compatibility flag; runner always verifies and grants authorized App permissions')
|
||||
args = parser.parse_args()
|
||||
args.grant_root = True
|
||||
if args.test in ("activity", "layout"): args.root_input = True
|
||||
if not 0 <= args.poll_cycles <= 60:
|
||||
parser.error('poll-cycles must be between 0 and 60')
|
||||
if args.root_input and args.test == 'ble':
|
||||
parser.error('root-input only applies to activity tests')
|
||||
if args.reapply_current_wifi and args.test != 'ble':
|
||||
parser.error('reapply-current-wifi only applies to BLE tests')
|
||||
if args.verify_current_controls and args.test != 'ble':
|
||||
parser.error('verify-current-controls only applies to BLE tests')
|
||||
if args.toggle_bluetooth and not (args.test == 'activity' and args.root_input):
|
||||
parser.error('toggle-bluetooth requires activity and root-input')
|
||||
device = next(d for d in load_registration() if d['alias'] == 'android-test-a')
|
||||
check_connected(args.adb, device)
|
||||
root_check = subprocess.run([args.adb, '-s', device['serial'], 'shell', 'su', '-c', 'id -u'], capture_output=True, text=True, timeout=15)
|
||||
if root_check.returncode or root_check.stdout.strip() != '0':
|
||||
raise RuntimeError('测试手机 root 权限不可用;停止测试,等待用户处理,不使用备用注入方式')
|
||||
client, fields = connect()
|
||||
try:
|
||||
code, output, error = execute(client, "python3 -c \"import json; p=json.load(open('/var/lib/matrix-screen-controller/mobile/identity.json')); print(json.dumps({'device_id':p['device_id']}))\"", password=fields['密码'])
|
||||
if code:
|
||||
raise RuntimeError('Cannot read authorized board identity')
|
||||
board_id = json.loads(output)['device_id']
|
||||
from uuid import UUID
|
||||
UUID(board_id)
|
||||
board_name = 'QMS-' + board_id.replace('-', '')[:8]
|
||||
code, output, _ = execute(client, "python3 -c \"import urllib.request,json,hashlib; d=json.load(urllib.request.urlopen('http://127.0.0.1:8080/api/library/order',timeout=5)); print(hashlib.sha256('\\n'.join(i['type']+'|'+i['id'] for i in d['items']).encode()).hexdigest())\"", password=fields['密码'])
|
||||
library_hash = output.strip()
|
||||
if code or len(library_hash) != 64 or any(c not in '0123456789abcdef' for c in library_hash):
|
||||
raise RuntimeError('Cannot read device library order digest')
|
||||
if not board_name.startswith('QMS-') or len(board_name) != 12:
|
||||
raise RuntimeError('Invalid board identity')
|
||||
finally:
|
||||
client.close()
|
||||
def adb(*command, timeout=120, quiet=False):
|
||||
try:
|
||||
result = subprocess.run([args.adb, '-s', device['serial'], *command], capture_output=True,
|
||||
text=True, encoding='utf-8', errors='replace', timeout=timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
# ADB's host timeout does not stop Android instrumentation itself.
|
||||
if command[:3] == ('shell', 'am', 'instrument'):
|
||||
subprocess.run([args.adb, '-s', device['serial'], 'shell', 'am', 'force-stop',
|
||||
'org.qimiaoscreen.controller'], capture_output=True, timeout=15)
|
||||
raise RuntimeError('ADB operation timed out; device identifiers omitted') from None
|
||||
output = (result.stdout + result.stderr).replace(device['serial'], '[test-phone]').replace(board_name, '[test-board]').replace(board_id, '[test-board-id]')
|
||||
if not quiet:
|
||||
print(output, flush=True)
|
||||
if result.returncode:
|
||||
raise RuntimeError('ADB command failed')
|
||||
return output
|
||||
for relative in ('androidApp/build/outputs/apk/debug/androidApp-debug.apk',
|
||||
'androidApp/build/outputs/apk/androidTest/debug/androidApp-debug-androidTest.apk'):
|
||||
apk = args.build / relative
|
||||
if not apk.is_file():
|
||||
raise RuntimeError('APK missing')
|
||||
adb('install', '-r', str(apk))
|
||||
adb('shell', 'am', 'force-stop', 'org.qimiaoscreen.controller')
|
||||
if args.grant_root:
|
||||
# Some vendor package managers asynchronously reset permissions after
|
||||
# install succeeds. Verify after a settling window, not just at grant.
|
||||
for attempt in range(3):
|
||||
for permission in ('BLUETOOTH_SCAN', 'BLUETOOTH_CONNECT'):
|
||||
adb('shell', 'su', '-c', 'pm grant org.qimiaoscreen.controller android.permission.' + permission)
|
||||
time.sleep(3)
|
||||
verified = adb('shell', 'dumpsys', 'package', 'org.qimiaoscreen.controller', quiet=True)
|
||||
if all('android.permission.' + p + ': granted=true' in verified for p in ('BLUETOOTH_SCAN', 'BLUETOOTH_CONNECT')):
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('Bluetooth permission did not remain granted after installation')
|
||||
if args.root_input:
|
||||
adb('push', str(Path(__file__).with_name('authorized_app_input.sh')), '/data/local/tmp/qms-authorized-input.sh', quiet=True)
|
||||
layout_original = None
|
||||
if args.test == "layout":
|
||||
adb("push", str(Path(__file__).with_name("authorized_root_command.sh")), "/data/local/tmp/qms-authorized-root.sh", quiet=True)
|
||||
def root(command):
|
||||
return adb("shell", "su", "-c", command, quiet=True).strip()
|
||||
import re
|
||||
layout_original = {key: root("settings get system " + key) for key in ("font_scale", "accelerometer_rotation", "user_rotation")}
|
||||
layout_density = re.search(r"Override density: (\d+)", root("wm density"))
|
||||
layout_density = layout_density[1] if layout_density else "reset"
|
||||
dimensions = re.findall(r"(\d+)x(\d+)", root("wm size"))[-1]
|
||||
narrow = min(map(int, dimensions)) * 160 // 340
|
||||
try:
|
||||
for font, rotation in (("1.0", "0"), ("1.3", "0"), ("1.3", "1")) if args.test == "layout" else ((None, None),):
|
||||
if layout_original:
|
||||
root("am force-stop org.qimiaoscreen.controller")
|
||||
root("wm density " + str(narrow))
|
||||
root("settings put system font_scale " + font)
|
||||
root("settings put system accelerometer_rotation 0")
|
||||
root("settings put system user_rotation " + rotation)
|
||||
root("input keyevent KEYCODE_WAKEUP")
|
||||
root("wm dismiss-keyguard")
|
||||
root("am start -W -n org.qimiaoscreen.controller/.MainActivity")
|
||||
output = adb('shell', 'am', 'instrument', '-w', '-r', '-e', 'device_name', board_name,
|
||||
'-e', 'poll_cycles', str(args.poll_cycles),
|
||||
'-e', 'library_order_hash', library_hash,
|
||||
'-e', 'root_input', str(args.root_input).lower(),
|
||||
'-e', 'reapply_current_wifi', str(args.reapply_current_wifi).lower(),
|
||||
'-e', 'verify_current_controls', str(args.verify_current_controls).lower(),
|
||||
'-e', 'toggle_bluetooth', str(args.toggle_bluetooth).lower(),
|
||||
'-e', 'class', 'org.qimiaoscreen.controller.' + {'ble': 'BleIntegrationTest', 'activity': 'AppLifecycleTest', 'layout': 'LayoutTest'}[args.test],
|
||||
'org.qimiaoscreen.controller.test/androidx.test.runner.AndroidJUnitRunner', timeout=360)
|
||||
if "OK (1 test)" not in output:
|
||||
raise RuntimeError("Instrumentation did not pass")
|
||||
if layout_original:
|
||||
print("Layout passed: font=" + font + ", rotation=" + rotation)
|
||||
finally:
|
||||
if layout_original:
|
||||
root("am force-stop org.qimiaoscreen.controller")
|
||||
root("wm density " + layout_density)
|
||||
for key, value in layout_original.items():
|
||||
root("settings delete system " + key if value == "null" else "settings put system " + key + " " + value)
|
||||
if root("settings get system " + key) != value: raise RuntimeError("Layout settings restore failed")
|
||||
if args.test == "layout":
|
||||
adb("shell", "rm", "-f", "/data/local/tmp/qms-authorized-root.sh", quiet=True)
|
||||
if args.toggle_bluetooth:
|
||||
adb('shell', 'su', '-c', 'cmd bluetooth_manager enable', quiet=True)
|
||||
if args.root_input:
|
||||
adb('shell', 'rm', '-f', '/data/local/tmp/qms-authorized-input.sh', quiet=True)
|
||||
if 'OK (1 test)' not in output:
|
||||
raise RuntimeError('BLE instrumentation did not pass')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user