apply_jvm wrote instance.cfg and never restored it, so B5-B12a in run 1 ran on B4's last set (Shenandoah, 6 GB). Within-block verdicts hold (same JVM on both sides); B7's no-shader baseline is re-run in run 2. Also: B14 spawn-count automation (b14/: fresh seeded world per run, offline-located biome sites, datapack entity counts, journaled config variants) and RESULTS-proxy.md with run-1 verdicts.
295 lines
13 KiB
Python
295 lines
13 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()
|
|
|
|
|
|
class ShaderLoaded(unittest.TestCase):
|
|
def test_flags(self):
|
|
ok = ["[Render thread/INFO] [Iris/]: Using shaderpack: MakeUp-UltraFast-9.5f.zip"]
|
|
bad = ok + ["[Render thread/ERROR] [Iris/]: Failed to compile shaders"]
|
|
self.assertEqual(bench.shader_flags("off", []), [])
|
|
self.assertEqual(bench.shader_flags("X.zip", ok), [])
|
|
self.assertEqual(bench.shader_flags("X.zip", []), ["shader_not_loaded"])
|
|
self.assertEqual(bench.shader_flags("X.zip", bad), ["shader_not_loaded"])
|
|
|
|
|
|
class ResetJvm(unittest.TestCase):
|
|
def test_reset(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
d = Path(d)
|
|
(d / "instance.cfg").write_text("JvmArgs=-XX:+UseShenandoahGC\nMaxMemAlloc=6144\n")
|
|
self.assertFalse(bench.reset_jvm(d))
|
|
(d / "instance.cfg.harness-orig").write_text("JvmArgs=\nMaxMemAlloc=8192\n")
|
|
self.assertTrue(bench.reset_jvm(d))
|
|
self.assertEqual((d / "instance.cfg").read_text(), "JvmArgs=\nMaxMemAlloc=8192\n")
|