111 lines
4.3 KiB
Python
111 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Relances natives dans une session de test déjà démarrée (firmware non distribué)."""
|
|
import argparse
|
|
import http.server
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import runpy
|
|
import subprocess
|
|
import threading
|
|
import time
|
|
import uuid
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument('--emu-home', type=Path, required=True)
|
|
p.add_argument('--agent-port', type=int, required=True)
|
|
p.add_argument('--action', choices=('respring', 'reset'), required=True)
|
|
p.add_argument('--cycles', type=int, default=3)
|
|
p.add_argument('--output', type=Path, required=True)
|
|
args = p.parse_args()
|
|
if args.cycles < 1:
|
|
p.error('--cycles doit être positif')
|
|
home = args.emu_home.resolve()
|
|
run = home / 'run'
|
|
bindir = Path(__file__).resolve().parents[1] / 'bin'
|
|
os.environ.update(IPOD_EMU_HOME=str(home), IPOD_AGENT_PORT=str(args.agent_port))
|
|
Agent = runpy.run_path(str(bindir / 'ipod-agent'))['Agent']
|
|
|
|
def call(*fields, data=None):
|
|
a = Agent(timeout=10)
|
|
try:
|
|
ok, body, detail = a.call(*fields, data=data)
|
|
if not ok:
|
|
raise RuntimeError(body.decode(errors='replace'))
|
|
return body
|
|
finally:
|
|
a.f.close()
|
|
a.s.close()
|
|
|
|
def springboard():
|
|
return next(line.split()[0] for line in call('ps').decode().splitlines()
|
|
if line.endswith(' SpringBoard'))
|
|
|
|
nonce = uuid.uuid4().hex.encode()
|
|
marker = '/private/var/mobile/Library/test-display-' + nonce.decode()
|
|
|
|
class Fixture(http.server.BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
self.send_response(200)
|
|
self.send_header('Content-Length', str(len(nonce)))
|
|
self.end_headers()
|
|
self.wfile.write(nonce)
|
|
|
|
def log_message(self, *unused):
|
|
pass
|
|
|
|
server = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Fixture)
|
|
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
url = f'http://10.0.2.2:{server.server_port}/preuve'
|
|
args.output.mkdir(parents=True, exist_ok=True)
|
|
qpid = (run / 'qemu.pid').read_text()
|
|
npid = (run / 'netd.pid').read_text()
|
|
inode = (run / 'nand').stat().st_ino
|
|
results = []
|
|
try:
|
|
call('put', marker, '600', len(nonce), data=nonce)
|
|
for cycle in range(args.cycles):
|
|
oldpid = springboard()
|
|
offset = (run / 'serial.log').stat().st_size
|
|
started = time.monotonic()
|
|
subprocess.run([str(bindir / 'ipod-emu'), args.action, '-W'],
|
|
check=True, timeout=340)
|
|
newpid = springboard()
|
|
fresh = (run / 'serial.log').read_bytes()[offset:]
|
|
if args.action == 'reset':
|
|
assert b'Darwin Kernel Version' in fresh
|
|
else:
|
|
assert oldpid != newpid
|
|
assert b'Darwin Kernel Version' not in fresh
|
|
assert b'locked=0 passcode=0' in call('screen')
|
|
assert (run / 'qemu.pid').read_text() == qpid
|
|
assert (run / 'netd.pid').read_text() == npid
|
|
assert (run / 'nand').stat().st_ino == inode
|
|
assert call('get', marker) == nonce
|
|
assert nonce in call('fetch', url)
|
|
call('launch', 'com.apple.calculator', '-')
|
|
time.sleep(3)
|
|
assert call('front').strip() == b'com.apple.calculator'
|
|
capture = args.output.resolve() / f'{args.action}-{cycle + 1}.png'
|
|
subprocess.run([str(bindir / 'ipod-qmp'), 'screenshot', str(capture)],
|
|
check=True, timeout=15)
|
|
result = dict(cycle=cycle + 1, seconds=round(time.monotonic() - started, 1),
|
|
qemu_pid=int(qpid), netd_pid=int(npid), springboard_pid=newpid,
|
|
unlocked=True, nand_preserved=True, http=True,
|
|
net=call('net').decode(), front='com.apple.calculator')
|
|
results.append(result)
|
|
print(json.dumps(result, ensure_ascii=False), flush=True)
|
|
(args.output / 'validation.json').write_text(
|
|
json.dumps(results, ensure_ascii=False, indent=2) + '\n')
|
|
call('rm', marker)
|
|
call('sync')
|
|
finally:
|
|
server.shutdown()
|
|
server.server_close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|