Files
onion-mc/analysis/client-perf/harness/tests/test_bench_flags.py
T
logan a15c912ad0 feat(bench): add matrix runner, gamescope and per-session controls
- bench.py: gamescope-headless display (default, 1920x1080; ~2% cost vs
  desktop for isolation from the shared Wayland desktop), --shader,
  --dh, --disable-mods/--add-mods, --world-zip, crash-safe restore
  journal, spectator mode in the plan (elevated cameras fell to the
  sea floor and S4 measured underwater darkness)
- matrix.toml/matrix.py: declarative, resumable B0-B14 runner keyed on
  mods hash (warns when B0 must be re-run)
- build.py: bench-only resource-pack variants (C1-fa, C1-fa-ext,
  emf-lod*-fa, C1-bl); every bench variant pins resourcePacks so packs
  don't leak between sessions; shipped tier output unchanged
- analyze.py: tagged labels use their own block's baseline
2026-10-16 22:14:45 -04:00

274 lines
12 KiB
Python

"""Offline tests for bench.py session instance changes (temp dirs only, no game).
python3 -m unittest discover -s analysis/client-perf/harness/tests
"""
import json
import sys
import tempfile
import unittest
import zipfile
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent))
import bench # noqa: E402
IRIS = """#This file stores configuration options for Iris, such as the currently active shaderpack
#Fri Oct 16 19:37:17 EDT 2026
allowUnknownShaders=false
enableShaders=true
maxShadowRenderDistance=32
shaderPack=
"""
DH = """[server]
\tenableRealTimeUpdates = true
[client]
\tshowDhOptionsButtonInMinecraftUi = true
\t\t[client.advanced.debugging]
\t\t\tlodOnlyMode = false
\t\t\t# DISABLED: Disable rendering
\t\t\trendererMode = "DEFAULT"
\t\t\t[client.advanced.debugging.debugWireframe]
\t\t\t\tenableRendering = false
\t\t\t\trendererMode = "DEFAULT"
"""
class GameDir(unittest.TestCase):
def setUp(self):
self._td = tempfile.TemporaryDirectory()
self.game = Path(self._td.name)
(self.game / "config").mkdir()
(self.game / "mods").mkdir()
(self.game / "shaderpacks").mkdir()
(self.game / "saves" / "onionbench").mkdir(parents=True)
(self.game / "saves" / "onionbench" / "level.dat").write_bytes(b"orig")
(self.game / "config" / "iris.properties").write_text(IRIS)
(self.game / "config" / "DistantHorizons.toml").write_text(DH)
(self.game / "shaderpacks" / "Pack A.zip").write_bytes(b"zip")
for n in ("BadOptimizations-2.4.1.jar", "particle_core-0.3.3.jar", "sodium-0.8.jar"):
(self.game / "mods" / n).write_bytes(n.encode())
self.j = bench.Journal.begin(self.game, "ptest")
def tearDown(self):
self._td.cleanup()
def snapshot(self):
"""Instance contents, excluding the journal itself (setUp opened one)."""
return {p.relative_to(self.game).as_posix(): p.read_bytes()
for p in sorted(self.game.rglob("*")) if p.is_file()
and p.relative_to(self.game).parts[0] not in (bench.PENDING, bench.BACKUP_DIR)}
class IrisAndDh(GameDir):
def test_properties_rewrite(self):
out = bench.set_properties(IRIS, {"enableShaders": "false", "shaderPack": "a:b=c.zip"})
self.assertIn("enableShaders=false\n", out)
self.assertIn("shaderPack=a\\:b\\=c.zip\n", out)
self.assertIn("maxShadowRenderDistance=32", out)
self.assertTrue(out.startswith("#This file"))
self.assertIn("newKey=1", bench.set_properties(IRIS, {"newKey": "1"}))
def test_shader_on_off_restore(self):
before = self.snapshot()
opts = self.game.parent / f"{self.game.name}-opts.txt"
opts.write_text("SHADOW=off\n")
try:
bench.apply_shader(self.game, self.j, "Pack A.zip", opts)
finally:
text = opts.read_text()
opts.unlink()
cfg = (self.game / "config" / "iris.properties").read_text()
self.assertIn("enableShaders=true", cfg)
self.assertIn("shaderPack=Pack A.zip", cfg)
self.assertEqual((self.game / "shaderpacks" / "Pack A.zip.txt").read_text(), text)
self.assertEqual(bench.restore_pending(self.game), [])
self.assertEqual(self.snapshot(), before)
j = bench.Journal.begin(self.game, "p2")
bench.apply_shader(self.game, j, "off")
cfg = (self.game / "config" / "iris.properties").read_text()
self.assertIn("enableShaders=false", cfg)
self.assertIn("shaderPack=\n", cfg)
bench.restore_pending(self.game)
self.assertEqual(self.snapshot(), before)
def test_dh_rewrite_only_debugging_section(self):
off = bench.set_dh_rendering(DH, False)
self.assertIn('\t\t\trendererMode = "DISABLED"\n\n', off)
self.assertIn('\t\t\t\trendererMode = "DEFAULT"', off) # wireframe section untouched
self.assertIn("enableRendering = false", off)
self.assertEqual(bench.set_dh_rendering(off, True), DH)
with self.assertRaises(ValueError):
bench.set_dh_rendering("[client]\n\trendererMode = \"DEFAULT\"\n", False)
def test_apply_dh_restore(self):
bench.apply_dh(self.game, self.j, False)
self.assertIn('"DISABLED"', (self.game / "config" / "DistantHorizons.toml").read_text())
bench.restore_pending(self.game)
self.assertEqual((self.game / "config" / "DistantHorizons.toml").read_text(), DH)
class Mods(GameDir):
def test_disable_add_restore(self):
before = self.snapshot()
names = bench.resolve_disable(self.game, ["badopt*", "PARTICLE_*"])
self.assertEqual(names, ["BadOptimizations-2.4.1.jar", "particle_core-0.3.3.jar"])
with self.assertRaises(ValueError):
bench.resolve_disable(self.game, ["badopt*", "nomatch*"])
extra = self.game.parent / f"{self.game.name}-trial.jar"
extra.write_bytes(b"trial")
try:
jars = bench.check_add_mods(self.game, [str(extra)])
h0 = bench.mods_hash(self.game / "mods")
bench.disable_mods(self.game, self.j, names)
bench.add_mods(self.game, self.j, jars)
finally:
extra.unlink()
mods = sorted(p.name for p in (self.game / "mods").iterdir())
self.assertEqual(mods, sorted(["BadOptimizations-2.4.1.jar.disabled",
f"{self.game.name}-trial.jar",
"particle_core-0.3.3.jar.disabled", "sodium-0.8.jar"]))
self.assertNotEqual(bench.mods_hash(self.game / "mods"), h0)
self.assertEqual(bench.restore_pending(self.game), [])
self.assertEqual(self.snapshot(), before)
self.assertEqual(bench.mods_hash(self.game / "mods"), h0)
def test_add_mods_validation(self):
with self.assertRaises(ValueError):
bench.check_add_mods(self.game, [str(self.game / "nope.jar")])
with self.assertRaises(ValueError): # name collision
bench.check_add_mods(self.game, [str(self.game / "mods" / "sodium-0.8.jar")])
class WorldZip(GameDir):
def make_zip(self, prefix):
z = self.game.parent / f"{self.game.name}-{prefix.strip('/') or 'flat'}.zip"
with zipfile.ZipFile(z, "w") as zf:
zf.writestr(prefix + "level.dat", b"zipped")
zf.writestr(prefix + "region/r.0.0.mca", b"region")
self.addCleanup(z.unlink)
return z
def check_swap(self, prefix):
before = self.snapshot()
z = self.make_zip(prefix)
self.assertEqual(bench.zip_world_root(z), prefix)
sha = bench.swap_world(self.game, self.j, z)
self.assertEqual(sha, bench.sha256_file(z))
w = self.game / "saves" / "onionbench"
self.assertEqual((w / "level.dat").read_bytes(), b"zipped")
self.assertTrue((w / "region" / "r.0.0.mca").exists())
self.assertTrue((self.game / "saves" / "onionbench.harness-bak" / "level.dat").exists())
self.assertEqual(sorted(p.name for p in (self.game / "saves").iterdir()),
["onionbench", "onionbench.harness-bak"])
self.assertEqual(bench.restore_pending(self.game), [])
self.assertEqual(self.snapshot(), before)
self.assertEqual([p.name for p in (self.game / "saves").iterdir()], ["onionbench"])
def test_flat_layout(self):
self.check_swap("")
def test_single_top_dir_layout(self):
self.check_swap("bench-world-v1/")
def test_bad_zips(self):
bad = self.game.parent / f"{self.game.name}-bad.zip"
self.addCleanup(lambda: bad.unlink(missing_ok=True))
with zipfile.ZipFile(bad, "w") as zf:
zf.writestr("a/level.dat", b"x")
zf.writestr("b/level.dat", b"x")
with self.assertRaises(ValueError):
bench.zip_world_root(bad)
with zipfile.ZipFile(bad, "w") as zf:
zf.writestr("../evil/level.dat", b"x")
with self.assertRaises(ValueError):
bench.zip_world_root(bad)
class PendingJournal(GameDir):
def test_crash_then_restore_from_disk(self):
before = self.snapshot()
z = WorldZip.make_zip(self, "w/")
bench.apply_shader(self.game, self.j, "Pack A.zip")
bench.apply_dh(self.game, self.j, False)
bench.disable_mods(self.game, self.j, ["sodium-0.8.jar"])
bench.swap_world(self.game, self.j, z)
plan = self.game / "saves" / "onionbench" / "datapacks" / "autoplan"
self.j.record({"op": "tree", "path": plan.relative_to(self.game).as_posix()})
plan.mkdir(parents=True)
(plan / "pack.mcmeta").write_text("{}")
# simulate a hard kill: only the on-disk journal survives
del self.j
self.assertTrue(bench.pending_exists(self.game))
data = json.loads((self.game / bench.PENDING).read_text())
self.assertEqual([e["op"] for e in data["entries"]],
["file", "file", "file", "rename", "world", "tree"])
with self.assertRaises(RuntimeError):
bench.Journal.begin(self.game, "again")
self.assertEqual(bench.restore_pending(self.game), [])
self.assertFalse(bench.pending_exists(self.game))
self.assertFalse((self.game / bench.BACKUP_DIR).exists())
self.assertEqual(self.snapshot(), before)
def test_entry_written_before_mutation_is_tolerated(self):
# journal says "renamed", but the kill came before os.rename ran
self.j.record({"op": "rename", "src": "mods/sodium-0.8.jar",
"dst": "mods/sodium-0.8.jar.disabled"})
self.j.record({"op": "added", "path": "mods/never-copied.jar"})
before = self.snapshot()
self.assertEqual(bench.restore_pending(self.game), [])
self.assertEqual(self.snapshot(), before)
def test_failed_entry_stays_journaled(self):
self.j.record({"op": "bogus"})
failed = bench.restore_pending(self.game)
self.assertEqual(len(failed), 1)
data = json.loads((self.game / bench.PENDING).read_text())
self.assertEqual(data["entries"], [{"op": "bogus"}])
def test_nothing_pending(self):
bench.restore_pending(self.game)
self.assertEqual(bench.restore_pending(self.game), [])
class OptionsAndFlags(unittest.TestCase):
def test_resource_pack_reset(self):
with tempfile.TemporaryDirectory() as td:
g = Path(td)
(g / "options.txt").write_text('version:3955\nresourcePacks:["vanilla","x.zip"]\n'
"maxFps:260\n")
bench.reset_resource_packs(g)
vals = bench.options_values(g, {"resourcePacks", "incompatibleResourcePacks"})
self.assertEqual(vals, {"resourcePacks": '["vanilla","mod_resources"]',
"incompatibleResourcePacks": "[]"})
text = (g / "options.txt").read_text()
self.assertTrue(text.startswith("version:3955\n"))
self.assertIn("maxFps:260", text)
(g / "options.txt").unlink()
bench.reset_resource_packs(g) # absent file -> just the baseline
self.assertEqual(len((g / "options.txt").read_text().splitlines()), 2)
def test_env_flags(self):
gpus = [{"device": "0x13c0", "dpm_level": "auto"},
{"device": bench.DGPU_DEVICE, "dpm_level": "auto"}]
self.assertEqual(bench.env_flags(gpus, True), ["gpu_dpm_auto", "server_running"])
gpus[1]["dpm_level"] = "high"
self.assertEqual(bench.env_flags(gpus, False), [])
def test_build_parser_session_defaults(self):
a = bench.build_parser().parse_args(["session", "--variant", "C1"])
self.assertEqual((a.shader, a.dh, a.disable_mods, a.add_mods, a.world_zip),
("off", "on", None, None, None))
a = bench.build_parser().parse_args(["restore"])
self.assertIs(a.fn, bench.cmd_restore)
if __name__ == "__main__":
unittest.main()