Files
spoopyghosty0--frameport/tests/conftest.py
T
spoopyghosty0 0470a2910a FramePort: modular Quest → Steam Frame porting app
Python core + Flet GUI + CLI that turns the manual Steam Frame porting work
into a pipeline: scan dumps, analyze, suggest patches (overport + Frame fixes,
catalog of 34 verified recipes), build, static-validate, install over SSH via
a Frame-side agent, add Steam shortcuts with artwork, headless launch test and
log triage. Includes the native sources (FrameBridge adapter, VrApi bridge
patches, GL shim), prebuilt artifacts, docs (playbook, runtime notes) and a
parity harness (34/34 games reproduce the known-good builds).
2026-09-28 21:00:29 -04:00

93 lines
3.4 KiB
Python

import os
import struct
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
@pytest.fixture(autouse=True)
def isolated_home(tmp_path, monkeypatch):
"""Never touch the real user data dir (tools, keystores, library) from tests."""
monkeypatch.setenv("FRAMEPORT_HOME", str(tmp_path / "home"))
yield
# ---------------------------------------------------------------------------------------------- AXML builder
def build_axml(elements, strings_extra=()):
"""elements: list of ("start", name, [(attr, kind, value)]) / ("end", name). kind: "str" | "bool".
Produces a UTF-16 string-pool binary XML like aapt does (enough for our editor)."""
ns = "http://schemas.android.com/apk/res/android"
pool = []
def idx(s):
if s not in pool:
pool.append(s)
return pool.index(s)
idx(ns)
for e in elements:
idx(e[1])
if e[0] == "start":
for a, kind, v in e[2]:
idx(a)
if kind == "str":
idx(v)
for s in strings_extra:
idx(s)
data = b""
offsets = []
for s in pool:
offsets.append(len(data))
data += struct.pack("<H", len(s)) + s.encode("utf-16-le") + b"\0\0"
data += b"\0" * (-len(data) % 4)
hsz = 28
sstart = hsz + 4 * len(pool)
pool_chunk = struct.pack("<HHIIIIII", 0x0001, hsz, sstart + len(data), len(pool), 0, 0, sstart, 0)
pool_chunk += struct.pack(f"<{len(pool)}I", *offsets) + data
body = b""
for e in elements:
if e[0] == "start":
attrs = b""
for a, kind, v in e[2]:
if kind == "str":
attrs += struct.pack("<IIIHBBI", idx(ns), idx(a), idx(v), 8, 0, 0x03, idx(v))
else:
attrs += struct.pack("<IIIHBBI", idx(ns), idx(a), 0xFFFFFFFF, 8, 0, 0x12, 0xFFFFFFFF if v else 0)
ext = struct.pack("<IIHHHHHH", 0xFFFFFFFF, idx(e[1]), 20, 20, len(e[2]), 0, 0, 0)
chunk = struct.pack("<HHIII", 0x0102, 16, 16 + len(ext) + len(attrs), 1, 0xFFFFFFFF) + ext + attrs
else:
chunk = struct.pack("<HHIII", 0x0103, 16, 24, 1, 0xFFFFFFFF) + struct.pack("<II", 0xFFFFFFFF, idx(e[1]))
body += chunk
total = 8 + len(pool_chunk) + len(body)
return struct.pack("<HHI", 0x0003, 8, total) + pool_chunk + body
@pytest.fixture
def quest_manifest():
return build_axml([
("start", "manifest", [("package", "str", "com.example.questgame")]),
("start", "uses-permission", [("name", "str", "com.oculus.permission.USE_SCENE")]),
("end", "uses-permission"),
("start", "uses-permission", [("name", "str", "android.permission.INTERNET")]),
("end", "uses-permission"),
("start", "application", [("debuggable", "bool", True)]),
("start", "activity", [("name", "str", "com.example.Main")]),
("start", "intent-filter", []),
("start", "action", [("name", "str", "android.intent.action.MAIN")]),
("end", "action"),
("start", "category", [("name", "str", "android.intent.category.INFO")]),
("end", "category"),
("end", "intent-filter"),
("end", "activity"),
("end", "application"),
("end", "manifest"),
])
def games_dir():
d = os.environ.get("FRAMEPORT_GAMES")
return Path(d) if d else None