Files
onion-mc/analysis/client-perf/harness/b14/tests/test_b14.py
T
logan 60e10b35a3 fix(bench): reset instance JVM settings for sessions without --jvm
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.
2026-10-17 22:28:20 -04:00

414 lines
19 KiB
Python

"""Offline tests for b14.py (temp dirs only; no game, no git writes)."""
import gzip
import json
import struct
import sys
import tempfile
import unittest
import zipfile
import zlib
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent))
sys.path.insert(0, str(HERE.parent.parent))
import b14 # noqa: E402
import bench # noqa: E402
import nbt # noqa: E402
AM_PARTIAL = """# partial
[spawning]
\tgazelleSpawnWeight = 20
\tcapuchinMonkeySpawnWeight = 14
\telephantSpawnWeight = 15
\tstraddlerSpawnWeight = 35
\trockyRollerSpawnWeight = 30
\tunderminerSpawnWeight = 25
\tblobfishSpawnWeight = 15
\tmudskipperSpawnWeight = 14
\tcaimanSpawnWeight = 15
"""
FP_PARTIAL = """// partial
{
"duckSpawnWeight": 2, "duckMaxGroupSize": 4, "gooseSpawnWeight": 2,
"gooseMaxGroupSize": 3, "gullSpawnWeight": 3, "gullMaxGroupSize": 5,
"sparrowSpawnWeight": 12, "sparrowMaxGroupSize": 4
}
"""
CAC_PARTIAL = """{
"configVersion": 2,
"spawning": {
"leaf_insect": [
{"biome_spec": "#c:is_jungle", "weight": 8, "min": 1, "max": 1},
{"biome_spec": "#c:is_forest", "weight": 4, "min": 1, "max": 1}
],
"sea_bunny": [
{"biome_spec": "minecraft:ocean", "weight": 10, "min": 1, "max": 2},
{"biome_spec": "minecraft:warm_ocean", "weight": 20, "min": 1, "max": 3}
],
"dragonfly": [
{"biome_spec": "#c:is_river", "weight": 4, "min": 1, "max": 1},
{"biome_spec": "#c:is_lush", "weight": 6, "min": 1, "max": 1}
]
}
}
"""
SHIPPED = {"alexsmobs-common.toml": AM_PARTIAL, "fowlplay.json5": FP_PARTIAL,
"crittersandcompanions-common.json5": CAC_PARTIAL}
# What the mods write back (re-saved, json5 unquoted keys, tabs) with default values.
AM_RESAVED_DEFAULT = "[spawning]\n" + "".join(
f"\t#Spawn Weight\n\t# Default: {v}\n\t{k} = {v}\n"
for k, v in b14.EXPECT_DEFAULTS["alexsmobs-common.toml"].items())
FP_RESAVED_DEFAULT = "{\n" + ",\n".join(
f"\t{k}: {v}" for k, v in b14.EXPECT_DEFAULTS["fowlplay.json5"].items()) + "\n}"
CAC_RESAVED_DEFAULT = """{
\tconfigVersion: 2,
\tspawning: {
\t\tleaf_insect: [
\t\t\t{
\t\t\t\tbiome_spec: "#c:is_jungle",
\t\t\t\tweight: 14,
\t\t\t\tmin: 1,
\t\t\t\tmax: 1
\t\t\t},
\t\t\t{ biome_spec: "#c:is_forest", weight: 6, min: 1, max: 1 }
\t\t],
\t\tred_panda: [ { biome_spec: "#c:is_jungle", weight: 8, min: 1, max: 2 } ],
\t\tsea_bunny: [
\t\t\t{ biome_spec: "minecraft:ocean", weight: 16, min: 1, max: 2 },
\t\t\t{ biome_spec: "minecraft:warm_ocean", weight: 32, min: 1, max: 4 }
\t\t],
\t\tdragonfly: [
\t\t\t{ biome_spec: "#c:is_river", weight: 4, min: 1, max: 1 },
\t\t\t{ biome_spec: "#c:is_lush", weight: 10, min: 1, max: 1 }
\t\t]
\t}
}"""
class ConfigVariants(unittest.TestCase):
def test_render(self):
d = b14.render_variant("defaults", SHIPPED)
self.assertEqual(set(d), set(b14.SPAWN_FILES))
self.assertTrue(all(v is None for v in d.values()))
s = b14.render_variant("shipped", SHIPPED)
self.assertEqual(s["fowlplay.json5"], FP_PARTIAL)
with self.assertRaises(ValueError):
b14.render_variant("nope", SHIPPED)
def test_extract_partial_and_resaved(self):
keys = b14.EXPECT_DEFAULTS
got = b14.extract_values("crittersandcompanions-common.json5", CAC_PARTIAL,
keys["crittersandcompanions-common.json5"])
self.assertEqual(got["sea_bunny@minecraft:warm_ocean"], 20)
self.assertEqual(got["leaf_insect@#c:is_jungle"], 8)
self.assertEqual(got["dragonfly@#c:is_lush"], 6)
got = b14.extract_values("crittersandcompanions-common.json5", CAC_RESAVED_DEFAULT,
keys["crittersandcompanions-common.json5"])
self.assertEqual(got, keys["crittersandcompanions-common.json5"])
self.assertEqual(b14.extract_values("fowlplay.json5", FP_RESAVED_DEFAULT,
keys["fowlplay.json5"]), keys["fowlplay.json5"])
self.assertEqual(b14.extract_values("alexsmobs-common.toml", AM_RESAVED_DEFAULT,
keys["alexsmobs-common.toml"]),
keys["alexsmobs-common.toml"])
# "Default: 40" comment lines must not be mistaken for the value
self.assertEqual(b14.extract_values("alexsmobs-common.toml", AM_PARTIAL,
["gazelleSpawnWeight"]), {"gazelleSpawnWeight": 20})
def test_check_config_and_journaled_apply(self):
with tempfile.TemporaryDirectory() as td:
game = Path(td)
(game / "config").mkdir()
originals = {"alexsmobs-common.toml": AM_RESAVED_DEFAULT + "# orig\n",
"fowlplay.json5": FP_RESAVED_DEFAULT} # CAC absent on purpose
for n, t in originals.items():
(game / "config" / n).write_text(t)
j = bench.Journal.begin(game, "b14-1")
b14.apply_spawn_config(game, j, b14.render_variant("shipped", SHIPPED))
for n in b14.SPAWN_FILES:
self.assertEqual((game / "config" / n).read_text(), SHIPPED[n])
self.assertTrue(b14.check_config(game, "shipped", SHIPPED)["ok"])
chk = b14.check_config(game, "defaults", SHIPPED)
self.assertFalse(chk["ok"])
self.assertIn("fowlplay.json5:duckSpawnWeight want 4 got 2", chk["mismatch"])
self.assertEqual(bench.restore_pending(game), [])
for n, t in originals.items():
self.assertEqual((game / "config" / n).read_text(), t)
self.assertFalse((game / "config" / "crittersandcompanions-common.json5").exists())
self.assertFalse((game / bench.PENDING).exists())
def test_defaults_delete_and_restore(self):
with tempfile.TemporaryDirectory() as td:
game = Path(td)
(game / "config").mkdir()
for n in b14.SPAWN_FILES:
(game / "config" / n).write_text("x" + n)
j = bench.Journal.begin(game, "b14-2")
b14.apply_spawn_config(game, j, b14.render_variant("defaults", SHIPPED))
self.assertFalse(any((game / "config" / n).exists() for n in b14.SPAWN_FILES))
self.assertTrue(b14.check_config(game, "defaults", SHIPPED)["ok"]) # absent = defaults
self.assertFalse(b14.check_config(game, "shipped", SHIPPED)["ok"])
# what the mods write after load
(game / "config" / "alexsmobs-common.toml").write_text(AM_RESAVED_DEFAULT)
(game / "config" / "fowlplay.json5").write_text(FP_RESAVED_DEFAULT)
(game / "config" / "crittersandcompanions-common.json5").write_text(CAC_RESAVED_DEFAULT)
self.assertTrue(b14.check_config(game, "defaults", SHIPPED)["ok"])
bench.restore_pending(game)
for n in b14.SPAWN_FILES:
self.assertEqual((game / "config" / n).read_text(), "x" + n)
def test_config_diff(self):
with tempfile.TemporaryDirectory() as td:
game = Path(td)
(game / "config").mkdir()
(game / "config" / "fowlplay.json5").write_text(FP_RESAVED_DEFAULT)
txt = b14.config_diff(game, b14.render_variant("shipped", SHIPPED), SHIPPED)
self.assertIn("duckSpawnWeight: 4 -> 2 <- changes", txt)
self.assertIn("config/alexsmobs-common.toml: WRITE", txt)
txt = b14.config_diff(game, b14.render_variant("defaults", SHIPPED), SHIPPED)
self.assertIn("config/fowlplay.json5: DELETE", txt)
SITES = [("river", {"x": 904, "y": 63, "z": 88}), ("warm_ocean", {"x": -200, "y": 63, "z": 56})]
class Datapack(unittest.TestCase):
def setUp(self):
self.files = b14.generate_datapack("b14-20261018000000", SITES, 2, 20)
self.base = "data/b14/function/b14-20261018000000"
def test_structure(self):
f = self.files
self.assertEqual(json.loads(f["pack.mcmeta"])["pack"]["pack_format"], 48)
self.assertEqual(json.loads(f["data/minecraft/tags/function/load.json"]),
{"values": ["b14:b14-20261018000000/load"]})
begin = f[f"{self.base}/begin.mcfunction"]
self.assertIn("gamerule doMobSpawning true", begin)
self.assertIn("gamemode creative @a", begin)
self.assertNotIn("spectator", "".join(f.values()))
self.assertIn("step0 400t", begin)
# 2 sites x (tp, arrive, t=0..2) + done
steps = [k for k in f if "/step" in k]
self.assertEqual(len(steps), 2 * (2 + 3) + 1)
self.assertIn("B14_DONE", f[f"{self.base}/step{len(steps) - 1}.mcfunction"])
def test_counts_and_chain(self):
count = self.files[f"{self.base}/count.mcfunction"]
for _, eid in b14.TUNED + b14.VANILLA:
self.assertIn(f"@e[type={eid},distance=..128]", count)
self.assertIn("@e[type=!minecraft:player,distance=..128]", count)
self.assertEqual(count.count("+= #"), len(b14.TUNED))
tl = b14.build_timeline("b14-20261018000000", SITES, 2, 20)
delays = [d for _, d in tl]
self.assertEqual(delays[:5], [100, 200, 1200, 1200, 20])
self.assertEqual(delays[-1], 0)
for i in range(len(tl) - 1):
body = self.files[f"{self.base}/step{i}.mcfunction"]
self.assertIn(f"schedule function b14:b14-20261018000000/step{i + 1} ", body)
s2 = self.files[f"{self.base}/step2.mcfunction"]
self.assertIn("setblock 904 62 88 minecraft:barrier", s2)
self.assertIn("tp @a 904.5 63 88.5", s2)
self.assertIn("if biome ~ ~ ~ minecraft:river", s2)
def test_tellraw_roundtrip(self):
"""The rendered tellraw text, with scores substituted, parses back."""
cmd = b14.tellraw("b14-20261018000000", "river", 3)
parts = json.loads(cmd[len("tellraw @a "):])
text = "".join(p if isinstance(p, str) else p.get("text", "7") for p in parts)
line = ("[18Oct2026 00:10:00.000] [Render thread/INFO] [net.minecraft.client.gui."
f"components.ChatComponent/]: [System] [CHAT] {text}")
r = b14.parse_count_line(line)
self.assertEqual((r["sid"], r["biome"], r["t"], r["total"], r["biome_ok"]),
("b14-20261018000000", "river", 3, 7, 7))
self.assertEqual(set(r["counts"]), {eid for _, eid in b14.TUNED + b14.VANILLA})
def test_world_build(self):
with tempfile.TemporaryDirectory() as td:
td = Path(td)
zp = td / "w.zip"
with zipfile.ZipFile(zp, "w") as z:
z.writestr("onionbench/level.dat", b"lvl")
z.writestr("onionbench/region/r.0.0.mca", b"x")
z.writestr("onionbench/data/chunks.dat", b"x")
z.writestr("onionbench/datapacks/onion-bench/pack.mcmeta", "{}")
z.writestr("onionbench/serverconfig/a.toml", "a")
game = td / "game"
(game / "saves").mkdir(parents=True)
j = bench.Journal.begin(game, "b14-3")
root = b14.build_world(game, j, zp, self.files)
self.assertEqual((root / "level.dat").read_bytes(), b"lvl")
self.assertFalse((root / "region").exists())
self.assertFalse((root / "data").exists())
self.assertTrue((root / "serverconfig/a.toml").exists())
self.assertTrue((root / "datapacks" / b14.PACK / "pack.mcmeta").exists())
with self.assertRaises(ValueError):
b14.build_world(game, bench.Journal(game, "x"), zp, self.files)
bench.restore_pending(game)
self.assertFalse(root.exists())
def _line(sid, biome, t, total, counts, ok=1):
body = " ".join(f"{k}={v}" for k, v in counts.items())
return (f"[x] [Render thread/INFO] [ChatComponent/]: [System] [CHAT] B14 {sid} {biome} "
f"t={t} biome_ok={ok} total={total} nonitem={total} tuned=0 {body}")
class ParseAndSummary(unittest.TestCase):
def test_parse_dedupes_and_filters(self):
lines = [_line("b14-1", "river", 0, 5, {"fowlplay:duck": 1}),
_line("b14-1", "river", 0, 5, {"fowlplay:duck": 1}),
"[Server thread/INFO]: [Server] B14_ARRIVE b14-1 river 1 2 3",
_line("b14-2", "river", 0, 9, {"fowlplay:duck": 2}),
"noise B14 garbage"]
rows = b14.parse_lines(lines)
self.assertEqual(len(rows), 2)
self.assertEqual(len(b14.parse_lines(lines, "b14-2")), 1)
self.assertEqual(rows[0]["counts"], {"fowlplay:duck": 1})
def _rows(self, config, sid, biome, totals, duck):
out = []
for t, (tot, d) in enumerate(zip(totals, duck)):
r = b14.parse_count_line(_line(sid, biome, t, tot, {"fowlplay:duck": d,
"minecraft:cow": 1}))
out.append({**r, "config": config, "kind": "b14_count"})
return out
def _sess(self, sid, ok=True):
return {"kind": "b14_session", "sid": sid, "outcome": "ok",
"config_check": {"ok": ok}}
def test_keep_and_back_off(self):
counts = (self._rows("defaults", "b14-1", "river", [10, 40], [2, 6])
+ self._rows("shipped", "b14-2", "river", [8, 25], [0, 3]))
sess = [self._sess("b14-1"), self._sess("b14-2")]
out, verdicts, overall = b14.summarize(counts, sess)
self.assertEqual(verdicts["river"]["verdict"], "keep")
self.assertEqual(out["river"]["shipped"]["seen"]["fowlplay:duck"], 3)
self.assertEqual(overall, "incomplete") # jungle / warm_ocean missing
counts2 = (self._rows("defaults", "b14-1", "river", [10, 40], [2, 6])
+ self._rows("shipped", "b14-2", "river", [8, 25], [0, 0]))
_, v2, o2 = b14.summarize(counts2, sess)
self.assertEqual(v2["river"]["verdict"], "back off")
self.assertEqual(v2["river"]["disappeared"], ["fowlplay:duck"])
self.assertIn("back off", b14.render_summary(*b14.summarize(counts2, sess)))
def test_bad_sessions_and_biome_check_excluded(self):
counts = (self._rows("defaults", "b14-1", "river", [10, 40], [2, 6])
+ self._rows("shipped", "b14-2", "river", [8, 25], [1, 3]))
_, v, _ = b14.summarize(counts, [self._sess("b14-1"), self._sess("b14-2", ok=False)])
self.assertEqual(v["river"]["verdict"], "incomplete")
for r in counts:
r["biome_ok"] = 0
_, v, _ = b14.summarize(counts, [self._sess("b14-1"), self._sess("b14-2")])
self.assertTrue(v["river"]["verdict"].startswith("invalid"))
def test_all_biomes_keep(self):
counts, sess = [], []
for i, biome in enumerate(b14.BIOMES):
counts += self._rows("defaults", f"b14-{i}0", biome, [5, 30], [1, 2])
counts += self._rows("shipped", f"b14-{i}1", biome, [5, 20], [1, 1])
sess += [self._sess(f"b14-{i}0"), self._sess(f"b14-{i}1")]
self.assertEqual(b14.summarize(counts, sess)[2], "keep")
# ---------------------------------------------------------------- nbt / locate
def _tag(t, name, payload):
n = name.encode()
return bytes([t]) + struct.pack(">H", len(n)) + n + payload
def _str(s):
b = s.encode()
return struct.pack(">H", len(b)) + b
def _compound(*items):
return b"".join(items) + b"\x00"
def _chunk_nbt(biome, height=70, status="minecraft:full"):
hm_longs = []
vals = [height + 64] * 256 # stored relative to min_y -64, 9 bits, 7 per long
for i in range(0, 256, 7):
w = 0
for k, v in enumerate(vals[i:i + 7]):
w |= v << (9 * k)
hm_longs.append(w if w < 2 ** 63 else w - 2 ** 64)
hm = struct.pack(">i", len(hm_longs)) + struct.pack(f">{len(hm_longs)}q", *hm_longs)
section = _compound(
_tag(nbt.BYTE, "Y", struct.pack(">b", (height - 1) >> 4)),
_tag(nbt.COMPOUND, "biomes", _compound(
_tag(nbt.LIST, "palette", bytes([nbt.STRING]) + struct.pack(">i", 1) + _str(biome)))))
root = _compound(
_tag(nbt.STRING, "Status", _str(status)),
_tag(nbt.LIST, "sections", bytes([nbt.COMPOUND]) + struct.pack(">i", 1) + section),
_tag(nbt.COMPOUND, "Heightmaps", _compound(
_tag(nbt.LONG_ARRAY, "MOTION_BLOCKING_NO_LEAVES", hm))))
return bytes([nbt.COMPOUND]) + _str("") + root
def _region(chunks):
"""chunks: {(lx, lz): nbt bytes} -> .mca bytes."""
header, body, sector = bytearray(8192), bytearray(), 2
for (lx, lz), raw in chunks.items():
data = zlib.compress(raw)
blob = struct.pack(">I", len(data) + 1) + b"\x02" + data
blob += b"\x00" * (-len(blob) % 4096)
n = len(blob) // 4096
idx = lz * 32 + lx
header[idx * 4:idx * 4 + 4] = sector.to_bytes(3, "big") + bytes([n])
body += blob
sector += n
return bytes(header) + bytes(body)
class NbtAndLocate(unittest.TestCase):
def test_packed(self):
self.assertEqual(nbt.unpack_packed([0b10_01_00 | (3 << 62)], 2, 3), [0, 1, 2])
self.assertEqual(nbt.unpack_packed([-1], 4, 2), [15, 15])
def test_chunk_roundtrip(self):
_, c = nbt.loads(_chunk_nbt("minecraft:river", 63))
self.assertEqual(nbt.heightmap(c)[0], 63)
self.assertEqual(nbt.biome_at(c, 5, 62, 9), "minecraft:river")
ld = gzip.compress(bytes([nbt.COMPOUND]) + _str("") + _compound(
_tag(nbt.COMPOUND, "Data", _compound(_tag(nbt.INT, "SpawnX", struct.pack(">i", 7))))))
self.assertEqual(nbt.load_gzip(ld)[1]["Data"]["SpawnX"], 7)
def test_scan_and_pick(self):
reg = {}
for lx in range(5):
for lz in range(5):
reg[(lx, lz)] = _chunk_nbt("minecraft:river" if lx < 3 else "minecraft:forest", 63)
reg[(lx + 20, lz)] = _chunk_nbt("minecraft:warm_ocean", 63)
reg[(lx + 10, lz + 20)] = _chunk_nbt("minecraft:jungle", 80,
"minecraft:structure_starts")
with tempfile.TemporaryDirectory() as td:
zp = Path(td) / "w.zip"
with zipfile.ZipFile(zp, "w") as z:
z.writestr("w/level.dat", b"")
z.writestr("w/region/r.0.0.mca", _region(reg))
chunks = b14.scan_world(zp)
self.assertEqual(len(chunks), 50) # structure_starts placeholders skipped
sites = b14.pick_sites(chunks, avoid=[(-1000, -1000)])
self.assertEqual(sites["river"]["biome"], "minecraft:river")
self.assertEqual(sites["river"]["y"], 63)
self.assertIn("warm_ocean", sites)
self.assertNotIn("jungle", sites)
self.assertGreaterEqual(abs(sites["warm_ocean"]["x"] - sites["river"]["x"]), 320)
# avoid radius excludes everything near the avoid point
self.assertEqual(b14.pick_sites(chunks, avoid=[(40, 40), (360, 40)]), {})
class Busy(unittest.TestCase):
def test_ancestors_include_self(self):
import os
self.assertIn(os.getpid(), b14._ancestors(os.getpid()))
self.assertNotIn(os.getpid(), b14.bench_busy())
if __name__ == "__main__":
unittest.main()