Files
horizon-airdef/tools/test_use_flag_reaction_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

77 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""Compare native living item-use tick, release, completion and stop lifecycle."""
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 corpus():
modes=['tick','update','complete','release','stop','notify','flag','assign']
ints=[-2147483648,-1,0,1,2,20,2147483647]
def state(**kw):
s=dict(item=0,remaining=1,virtual_remaining=1,kinetic=True,selected=0,counts=[1,1,0],slots=[[0,0],[8,1],[40,1]],level=0,clients=[False,True],releases=[False,True],finishes=[1,0],durations=[32,72000]);s.update(kw);return s
def row(mode='tick',**kw):
f=dict(mode=mode,argument=0,flags=1,shared_type=False,vibrations=[True,True],initial=state(),after=state(item=1,remaining=0,virtual_remaining=-1,counts=[0,2,0],selected=8,slots=[[0,1],[8,0],[40,0]],level=1,durations=[-1,2147483647]),fail=-1,mutate_at=-1,mask=1,value=True,values=[0,1,3,2]);f.update(kw);return f
for mode in modes:
for flags in [0,1,2,3,127,128,254,255]:
for item in range(3):
for held in range(3):
for client in [False,True]:
yield row(mode,flags=flags,initial=state(item=item,slots=[[0,held],[40,held]],clients=[client,client]))
for mutate in range(-1,15):
for fail in range(-1,15):yield row(mode,mutate_at=mutate,fail=fail)
for flags in range(256):
for mask in [-2147483648,-1,0,1,2,3,128,256,2147483647]:
for value in [False,True]:yield row('flag',flags=flags,mask=mask,value=value,initial=state(item=2,clients=[True,True]))
for count in ints:
for remaining in ints:
for value in [0,1,2,3]:yield row('assign',values=[value,value],initial=state(counts=[count,count,count],remaining=remaining,clients=[True,True]))
rng=random.Random(0x555345464c4147)
def random_state():return state(item=rng.randrange(3),remaining=rng.choice(ints),virtual_remaining=rng.choice(ints),kinetic=bool(rng.randrange(2)),selected=rng.randrange(9),counts=[rng.choice(ints) for _ in range(3)],slots=[[i,rng.randrange(3)] for i in rng.sample(range(43),rng.randrange(12))],level=rng.randrange(2),clients=[bool(rng.randrange(2)),bool(rng.randrange(2))],releases=[bool(rng.randrange(2)),bool(rng.randrange(2))],finishes=[rng.randrange(3),rng.randrange(3)],durations=[rng.choice(ints),rng.choice(ints)])
for _ in range(4500):yield row(rng.choice(modes),argument=rng.randrange(3),flags=rng.randrange(256),shared_type=bool(rng.randrange(2)),vibrations=[bool(rng.randrange(2)),bool(rng.randrange(2))],initial=random_state(),after=random_state(),fail=rng.choice([-1,-1,*range(15)]),mutate_at=rng.randrange(-1,15),mask=rng.randrange(-2147483648,2147483648),value=bool(rng.randrange(2)),values=[rng.randrange(256) for __ in range(rng.randrange(6))])
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--prepare',action='store_true');args=parser.parse_args()
build=lab.LOCAL/'rust-use-flag-reaction-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/VanillaUseFlagReactionOracle.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/use_flag_reaction_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.VanillaUseFlagReactionOracle',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','use_flag_reaction_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'/('use_flag_reaction_fixture.exe' if os.name=='nt' else 'use_flag_reaction_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:,} use flag reaction 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 inherited LivingEntity synchronized-use-flag observer reaction, composed with Inventory/PlayerEquipment, shared stacks and SynchedEntityData writes/assignments. Client reactions initialize a newly active held stack and duration or clear an existing active stack; server reactions leave active use untouched. Includes ordinary use tick/completion/release/stop, same-value received updates, duration callbacks, dirty packing, mutations and failure prefixes. Other synchronized keys, start-use packet flow, full equip/item effects and complete gameplay integration remain pending.'}
lab.write_json(report_path,report);print(f'Scoped use flag reaction parity passed: {report_path}')
if __name__=='__main__':main()