Files
horizon-airdef/tools/test_component_durability_parity.py
T
Ludovic Marie 9edcbc2559 Give parity harness javac runs the oracle source path
Oracles reference shared helper oracles (heightmap, level shape...) that
javac could not find when only the entry files were listed. Also let
chunkgen_bench list unsupported features (HORIZON_LIST_UNSUPPORTED=1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:33:12 +02:00

88 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""Compare durability algorithms bound to actual prototype/patch storage."""
import argparse
import json
import os
from pathlib import Path
import platform
import subprocess
import random
import struct
import math
try:
from . import lab, test_rust_parity as base
except ImportError:
import lab
import test_rust_parity as base
def base_corpus():
modes = ['hurt', 'without', 'set', 'query', 'grow', 'shrink']
edges = [-2147483648, -2147483647, -1, 0, 1, 2, 19, 20, 21, 16777215, 16777216, 16777217, 2147483646, 2147483647]
def state(count=1, maximum=20, damage=0, unbreakable=False):
return dict(count=count, max=maximum, damage=damage, unbreakable=unbreakable)
def row(mode='hurt', amount=1, **kw):
f = dict(mode=mode, amount=amount, air=False, player=True, infinite=False, after_infinite=False, initial=state(), after=state(count=2, maximum=5, damage=4), fail=-1, mutate_at=-1)
f.update(kw)
return f
for mode in modes:
for maximum in [None, *edges]:
for damage in [None, *edges]:
for amount in edges:
yield row(mode, amount, initial=state(maximum=maximum, damage=damage))
for mode in ['hurt', 'without']:
for mutate in range(-1, 5):
for fail in range(-1, 5):
for count in [-1, 0, 1, 2]:
for maximum in [None, 0, 5, 20]:
yield row(mode, 20, mutate_at=mutate, fail=fail, after=state(count=count, maximum=maximum, damage=4))
rng = random.Random(0x4455524142494c)
def integer(): return rng.choice(edges) if rng.randrange(2) else rng.randrange(-2147483648, 2147483648)
def random_state(): return state(rng.choice([-2, 0, 1, 2, 64, 2147483647]), rng.choice([None, integer()]), rng.choice([None, integer()]), bool(rng.randrange(2)))
for _ in range(10000):
yield row(rng.choice(modes), integer(), air=bool(rng.randrange(5)==0), player=bool(rng.randrange(2)), infinite=bool(rng.randrange(2)), after_infinite=bool(rng.randrange(2)), initial=random_state(), after=random_state(), fail=rng.choice([-1, -1, *range(5)]), mutate_at=rng.randrange(-1, 5))
def corpus():
prototypes=[dict(max=None,damage=None,unbreakable=False),dict(max=20,damage=0,unbreakable=False),dict(max=20,damage=0,unbreakable=True),dict(max=None,damage=5,unbreakable=False),dict(max=-2147483648,damage=-1,unbreakable=False)]
for i,row in enumerate(base_corpus()):
yield row | dict(prototype=prototypes[i%len(prototypes)])
for prototype in prototypes:
for amount in [-2147483648,-1,0,1,20,2147483647]:
for mode in ['hurt','without','set']:
yield dict(mode=mode,amount=amount,air=False,player=True,infinite=False,after_infinite=False,prototype=prototype,initial=dict(count=1,max=20,damage=0,unbreakable=False),after=dict(count=2,max=5,damage=4,unbreakable=False),fail=-1,mutate_at=-1,restore=[[1,None],[0,20]])
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-component-durability-parity';build.mkdir(parents=True,exist_ok=True)
report_path=build/'report.json';report_path.unlink(missing_ok=True)
if args.prepare:lab.prepare()
lock,jars,libraries=base.verify_artifacts()
oracle=lab.ROOT/'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaComponentDurabilityOracle.java'
sources=[oracle,Path(__file__),lab.ROOT/'tools/test_rust_parity.py',lab.ROOT/'tools/lab.py',lab.ROOT/'Cargo.toml',lab.ROOT/'Cargo.lock',lab.ROOT/'crates/horizon-core/Cargo.toml',lab.ROOT/'crates/horizon-world/Cargo.toml',*sorted((lab.ROOT/'crates/horizon-core/src').rglob('*.rs')),*sorted((lab.ROOT/'crates/horizon-world/src').rglob('*.rs')),lab.ROOT/'crates/horizon-world/examples/component_durability_fixture.rs']
tested_sources=base.source_hashes(sources)
classes=build/'classes';classes.mkdir(exist_ok=True)
def run(command,name):
with (build/(name+'.log')).open('w',encoding='utf-8') as log:base.run(list(map(str,command)),stdout=log,stderr=subprocess.STDOUT)
dependency_path=os.pathsep.join(map(str,libraries))
run(['javac','--release',lock['minecraft']['java_major'],'-proc:none','-sourcepath',str(lab.ROOT/'mods/horizon/src/test/java'),'-cp',str(jars['server'])+os.pathsep+dependency_path,'-d',classes,oracle],'compile-java')
def java(side,mode,path):
run(['java','-Xmx512M','-cp',os.pathsep.join([str(classes),str(jars[side]),dependency_path]),'dev.horizonlab.parity.VanillaComponentDurabilityOracle',mode,path],side+'-'+mode)
fixtures=list(corpus());inputs=build/'inputs.txt';inputs.write_text(''.join(json.dumps(value,separators=(',',':'))+'\n' for value in fixtures),encoding='utf-8')
fixture_paths=[inputs];tested_fixtures=base.source_hashes(fixture_paths)
run(['cargo','build','--release','-p','horizon-world','--example','component_durability_fixture'],'compile-rust')
metadata=json.loads(subprocess.check_output(['cargo','metadata','--no-deps','--format-version','1'],cwd=lab.ROOT,text=True))
binary=Path(metadata['target_directory'])/'release/examples'/('component_durability_fixture.exe' if os.name=='nt' else 'component_durability_fixture')
run([binary,build],'rust');references=[]
for side,jar in jars.items():
java(side,side,build);reference=build/f'{side}.txt';matched=base.compare(reference,build/'rust.txt')
references.append({'side':side,'jar_sha256':lab.digest(jar,'sha256'),'output_sha256':lab.digest(reference,'sha256'),'matched_output_lines':matched})
print(f'{side}: {matched:,} component durability output lines match Rust',flush=True)
base.ensure_sources_unchanged(sources,tested_sources);base.ensure_sources_unchanged(fixture_paths,tested_fixtures)
report={'schema':1,'minecraft':lock['minecraft']['version'],'manifest_sha1':lock['minecraft']['manifest_sha1'],'platform':platform.platform(),'java':subprocess.check_output(['java','--version'],text=True).splitlines()[0],'rustc':subprocess.check_output(['rustc','--version'],text=True).strip(),'fixtures':len(fixtures),'sources_sha256':tested_sources,'fixtures_sha256':tested_fixtures,'rust_output_sha256':lab.digest(build/'rust.txt','sha256'),'references':references,'scope':'Actual unenchanted ItemStack durability operations with nonempty prototypes, visible component patches at callbacks, stack copies before/after original mutation and unsanitized restored removals. Rust durability algorithms operate directly on copy-on-write component storage through DurabilityStorage. Includes creative/stat callback ordering, clamped writes, shrink/break state, empty stacks and storage mutation-before-error. Full item-component storage/codecs, enchantments, equipment lifecycle and complete gameplay integration remain pending.'}
lab.write_json(report_path,report);print(f'Scoped component durability parity passed: {report_path}')
if __name__=='__main__':main()