Files
spoopyghosty0--frameport/tests/test_main_process.py
T
spoopyghosty0 104c003c44 frame.main_process: run a game activity that has its own process in the main one (#178)
Halo CE VR's HaloActivity runs in :halo_game; Lepton starts it directly and watches only the main process, which
never exists, so it closed the game about a minute in ('Target app ... did not start'). Analysis field
separate_process (ANALYSIS_VERSION 23), default-on patch, triage app-separate-process, PLAYBOOK row.
2026-10-18 12:35:48 -04:00

75 lines
3.0 KiB
Python

"""frame.main_process (GitHub #178): a game activity in its own process leaves no main process, which is the one
Lepton watches; it closed Halo CE VR about a minute in ("Target app ... did not start")."""
from conftest import build_axml
from frameport.apk import axml
from frameport.patches import base
PKG = "com.halo.decomp.vr"
def _activity(name, process=None, category=axml.LAUNCHER):
attrs = [("name", "str", name)] + ([("process", "str", process)] if process else [])
return [
("start", "activity", attrs),
("start", "intent-filter", []),
("start", "action", [("name", "str", axml.MAIN)]), ("end", "action"),
("start", "category", [("name", "str", category)]), ("end", "category"),
("end", "intent-filter"),
("end", "activity"),
]
def _halo_manifest():
"""Halo CE VR's layout: the VR activity runs in ":halo_game", the 2D launcher starts it."""
return build_axml([
("start", "manifest", [("package", "str", PKG)]),
("start", "application", []),
*_activity("com.halo.decomp.HaloActivity", ":halo_game", axml.VR_CATEGORY),
*_activity("com.halo.decomp.LauncherActivity"),
("start", "service", [("name", "str", "com.halo.decomp.Crash"), ("process", "str", ":crash")]),
("end", "service"),
("end", "application"),
("end", "manifest"),
])
def _processes(manifest):
x = axml.Axml(manifest)
return {x.attr_str(e, "name"): x.attr_str(e, "process") for e in x.elements() if e.name in ("activity", "service")}
def test_detects_and_moves_the_game_activity():
m = _halo_manifest()
assert axml.separate_process_activities(m) == ["com.halo.decomp.HaloActivity"]
fixed = axml.set_main_process(m, axml.separate_process_activities(m))
assert axml.separate_process_activities(fixed) == []
assert _processes(fixed) == {"com.halo.decomp.HaloActivity": PKG, "com.halo.decomp.LauncherActivity": None,
"com.halo.decomp.Crash": ":crash"} # services keep their own process
assert len(fixed) == len(m) # the package name is already in the pool: one value changes, nothing moves
assert axml.set_main_process(fixed, ["com.halo.decomp.HaloActivity"]) is None
def test_main_process_activities_are_left_alone():
m = build_axml([
("start", "manifest", [("package", "str", PKG)]),
("start", "application", []),
*_activity("com.x.Game", PKG), # explicitly the main process
*_activity("com.x.Other"),
("end", "application"),
("end", "manifest"),
])
assert axml.separate_process_activities(m) == []
assert axml.set_main_process(m, ["com.x.Game"]) is None
def test_patch_is_offered_only_when_detected():
patch = base.get("frame.main_process")
assert patch.default_on
class A:
extra = {"separate_process": ["com.halo.decomp.HaloActivity"]}
assert patch.applies(A()) and patch.detect(A()).recommended
A.extra = {}
assert not patch.applies(A()) and patch.detect(A()) is None