Keep specialized parity harnesses compatible with normal CI invocation

This commit is contained in:
voidsip committed 2026-11-06 21:49:02 -06:00
1 parent b0a8cbf6fb
commit ccb85ce454
5 files changed
+199 -10

No files matched your search

+178
View File
@@ -0,0 +1,178 @@
"""CLI admission/routing contracts; every Java, Cargo and native launch is stubbed."""
from contextlib import ExitStack, redirect_stdout
import copy
import importlib
import io
import json
import os
from pathlib import Path
import sys
import tempfile
from types import SimpleNamespace
import unittest
from unittest import mock
TOOLS = Path(__file__).resolve().parents[1] / 'tools'
class RunnerAdmissionTests(unittest.TestCase):
def run_fixture(self, *, reject=False, reference_only=False, binary=False):
with tempfile.TemporaryDirectory() as directory, ExitStack() as stack:
folder = Path(directory)
stack.enter_context(mock.patch.object(sys, 'path', [str(TOOLS), *sys.path]))
runner = importlib.import_module('test_command_exec_parity')
events = []
row = {'cmd': '/say cli', 'messages': [], 'results': [[True, 1]]}
argv = ['parity.py', '--work-dir', str(folder)]
if reference_only:
argv.append('--reference-only')
if binary:
executable = folder / 'archived-game'
executable.touch()
argv += ['--binary', str(executable)]
stack.enter_context(mock.patch.object(sys, 'argv', argv))
stack.enter_context(mock.patch.object(runner.base, 'verify_artifacts', return_value=(
{'minecraft': {'java_major': '25', 'version': '26.3'}},
{'server': folder / 'server.jar'}, [])))
stack.enter_context(mock.patch.object(runner.corpus_module, 'groups', return_value=[
('cli admission', [{'op': 'cmd', 'cmd': '/say cli', 'expect_result': 1}])]))
stack.enter_context(mock.patch.object(runner, 'install_datapack_fixtures'))
stack.enter_context(mock.patch.object(runner.platform, 'platform', return_value='unit'))
def subprocess_run(command, **kwargs):
if command[0] == 'java':
events.append('java')
(folder / 'vanilla.jsonl').write_text(json.dumps(row) + '\n')
elif command[0] == 'cargo':
events.append('native-build')
else:
self.assertEqual(command[0], 'javac')
return SimpleNamespace(returncode=0)
def native_run(command, **kwargs):
events.append('native-run')
Path(kwargs['env']['HORIZON_COMMAND_OUTPUT']).write_text(json.dumps(row) + '\n')
return SimpleNamespace(returncode=0)
def admit(rows):
events.append('admit')
self.assertEqual(rows, [row])
if reject:
raise ValueError('reference rejected')
stack.enter_context(mock.patch.object(runner.subprocess, 'run', side_effect=subprocess_run))
stack.enter_context(mock.patch.object(runner.subprocess, 'check_output', return_value=json.dumps(
{'target_directory': str(folder / 'target')})))
stack.enter_context(mock.patch.object(runner, 'run_game', side_effect=native_run))
with redirect_stdout(io.StringIO()):
if reject:
with self.assertRaisesRegex(ValueError, 'reference rejected'):
runner.main(reference_admission=admit)
else:
runner.main(reference_admission=admit)
return events
def test_normal_run_admits_before_native_build_and_execution(self):
self.assertEqual(self.run_fixture(), ['java', 'admit', 'native-build', 'native-run'])
def test_rejected_reference_never_builds_or_runs_native(self):
self.assertEqual(self.run_fixture(reject=True), ['java', 'admit'])
def test_reference_only_still_admits_without_native(self):
self.assertEqual(self.run_fixture(reference_only=True), ['java', 'admit'])
def test_archived_binary_keeps_admission_and_skips_build(self):
self.assertEqual(self.run_fixture(binary=True), ['java', 'admit', 'native-run'])
class SpecializedHarnessRoutingTests(unittest.TestCase):
def run_fixture(self, name, mode, invalid=False):
with tempfile.TemporaryDirectory() as directory, ExitStack() as stack:
folder = Path(directory)
stack.enter_context(mock.patch.object(sys, 'path', [str(TOOLS), *sys.path]))
module = importlib.import_module('test_' + name + '_parity')
runner = module.runner
# Pot admission independently requires two unchanged before/after sources.
program = [dict(op='cmd', cmd='/source ' + source, expect_result=1,
source_snapshot=source, snapshot_phase=phase)
for source in ['a', 'b'] for phase in ['before', 'after']]
rows = [dict(cmd=op['cmd'], messages=[], results=[[True, 1]]) for op in program]
if invalid:
rows[0]['results'] = [[True, True]] # bool is not an integer callback.
original = copy.deepcopy(rows)
argv = ['harness.py', '--work-dir', str(folder)]
if mode == 'reference-only':
argv.append('--reference-only')
if mode == 'reuse':
argv += ['--reference-dir', str(folder), '--binary', 'archived-game']
stack.enter_context(mock.patch.object(sys, 'argv', argv))
stack.enter_context(mock.patch.object(module, 'cases', return_value=program))
stack.enter_context(mock.patch.object(runner.corpus_module, 'group_setup', return_value=[]))
stack.enter_context(mock.patch.object(runner.corpus_module, 'groups'))
events = []
def outputs():
text = ''.join(json.dumps(row) + '\n' for row in rows)
(folder / 'vanilla.jsonl').write_text(text)
if mode != 'reference-only':
(folder / 'rust.jsonl').write_text(text)
def fresh(*, reference_admission):
events.append('fresh')
reference_admission(rows)
events.append('admitted')
outputs()
def reuse(reference, output, actual_program):
self.assertEqual(reference, folder.resolve())
self.assertEqual(output, folder.resolve())
self.assertEqual(actual_program, program)
events.append('reuse')
outputs()
stack.enter_context(mock.patch.object(runner, 'main', side_effect=fresh))
stack.enter_context(mock.patch.object(module, 'native_from_reference', side_effect=reuse))
with redirect_stdout(io.StringIO()):
if invalid:
with self.assertRaises(AssertionError):
module.main()
else:
self.assertFalse(module.main())
self.assertEqual(rows, original, 'admission must not normalize or alter raw reference rows')
return events
def test_both_harnesses_support_normal_reference_only_and_frozen_routes(self):
for name in ['loaded_pig_tags', 'loot_mine_pot_components']:
for mode in ['normal', 'reference-only', 'reuse']:
with self.subTest(harness=name, mode=mode):
self.assertEqual(self.run_fixture(name, mode),
['reuse'] if mode == 'reuse' else ['fresh', 'admitted'])
def test_both_normal_routes_reject_typed_admission_before_native(self):
for name in ['loaded_pig_tags', 'loot_mine_pot_components']:
with self.subTest(harness=name):
self.assertEqual(self.run_fixture(name, 'normal', invalid=True), ['fresh'])
class MobDiscardArgumentsTests(unittest.TestCase):
def parse(self, argv, environment):
with mock.patch.object(sys, 'path', [str(TOOLS), *sys.path]), mock.patch.dict(os.environ, environment, clear=True):
module = importlib.import_module('test_mob_discard_parity')
return module, module.parse_args(argv)
def test_normal_cli_uses_prepared_resources_without_environment(self):
module, args = self.parse([], {})
self.assertEqual(args.resources, module.lab.RESOURCES)
def test_environment_selects_explicit_resource_directory(self):
_, args = self.parse([], {'HORIZON_TEST_RESOURCES': '/fixture/environment'})
self.assertEqual(args.resources, Path('/fixture/environment'))
def test_empty_environment_uses_prepared_resources(self):
module, args = self.parse([], {'HORIZON_TEST_RESOURCES': ''})
self.assertEqual(args.resources, module.lab.RESOURCES)
def test_cli_resource_directory_overrides_environment(self):
_, args = self.parse(['--resources', '/fixture/argument'], {'HORIZON_TEST_RESOURCES': '/fixture/environment'})
self.assertEqual(args.resources, Path('/fixture/argument'))
+8 -3
View File
@@ -80,7 +80,7 @@ def install_datapack_fixtures(world, predicates, functions, jar, advancements=No
target.write_text(json.dumps(recipe), encoding='utf-8')
def main():
def main(*, reference_admission=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--prepare', action='store_true')
parser.add_argument('--raid-village-fixture', action='store_true', help='match the existing development command trees in the isolated Java/native fixture processes')
@@ -376,6 +376,11 @@ def main():
any(len(r.get('bits', [])) != 3 or
any(re.fullmatch(r'[0-9a-f]{1,16}', b) is None for b in r['bits']) for r in direct)):
sys.exit('Java direct underwater reference prerequisite failed')
# Specialized harnesses qualify the fresh reference before any native build
# or execution. Their strict raw-record checks also apply to normal CI runs.
if reference_admission is not None:
reference_admission([json.loads(line) for line in
(build / 'vanilla.jsonl').read_text(encoding='utf-8').splitlines()])
if args.reference_only:
reference = [json.loads(line) for line in (build / 'vanilla.jsonl').read_text().splitlines()]
recorded = [op for op in script if op['op'] in ('player_state', 'player_loop', 'map_state', 'frame_map_state', 'mob_anger', 'raid_state', 'menu_state') or
@@ -447,8 +452,8 @@ def main():
native = json.loads((build / 'rust-client-config.json').read_text())
if native.get('render_setting') != 2 or native.get('simulation_setting') != 2 or not native.get('joined'):
sys.exit(f'native settings/join prerequisite failed: {native}')
vanilla = [json.loads(l) for l in (build / 'vanilla.jsonl').open(encoding='utf-8')]
rust = [json.loads(l) for l in (build / 'rust.jsonl').open(encoding='utf-8')]
vanilla = [json.loads(l) for l in (build / 'vanilla.jsonl').read_text(encoding='utf-8').splitlines()]
rust = [json.loads(l) for l in (build / 'rust.jsonl').read_text(encoding='utf-8').splitlines()]
if len(vanilla) != len(rust):
sys.exit(f'record count differs: vanilla {len(vanilla)} rust {len(rust)}')
recorded_ops = [op for op in script if op['op'] in ('falling', 'destroy_progress', 'weather_state', 'chunk_ready', 'server_distances', 'map_state', 'frame_map_state', 'light', 'player_state', 'player_loop', 'mob_anger', 'raid_state', 'menu_state') or
+2 -2
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""Actual pig Tags load/save/mutation proof; complete raw list order is diagnostic.
The default mode validates Java before building or running native.
Admit fresh Java with --reference-only --work-dir REF. Reuse exact REF bytes for
both normal binaries with --reference-dir REF --binary GAME --work-dir NEW_OUT.
No output masks, ordering normalization or synthetic tag observer are applied.
@@ -89,8 +90,7 @@ def main():
sys.argv.append("--allow-gaps")
folder = Path(sys.argv[sys.argv.index("--work-dir") + 1]).resolve()
if reference is None:
assert "--reference-only" in sys.argv, "admit fresh Java, then reuse its frozen reference"
runner.main()
runner.main(reference_admission=lambda rows: admit(program, rows))
else:
assert "--reference-only" not in sys.argv
native_from_reference(reference, folder, program)
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""Actual loot-mine pot component boundary; raw snapshots and clocks retained.
The default mode validates Java before building or running native.
Admit fresh Java with --reference-only --work-dir REF; compare each archived
normal Game against --reference-dir REF with a fresh --work-dir OUT.
"""
@@ -92,8 +93,7 @@ def main():
sys.argv.append("--allow-gaps")
folder = Path(sys.argv[sys.argv.index("--work-dir") + 1]).resolve()
if reference is None:
assert "--reference-only" in sys.argv, "admit Java before the native pair"
runner.main()
runner.main(reference_admission=lambda rows: admit(program, rows))
else:
assert "--reference-only" not in sys.argv
native_from_reference(reference, folder, program)
+9 -3
View File
@@ -3,18 +3,24 @@
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import zipfile
import lab
def main():
def parse_args(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--game-dir', type=Path, default=lab.GAME)
parser.add_argument('--resources', type=Path, required=True)
parser.add_argument('--resources', type=Path,
default=Path(os.environ.get('HORIZON_TEST_RESOURCES') or lab.RESOURCES))
parser.add_argument('--java-home', type=Path)
args = parser.parse_args()
return parser.parse_args(argv)
def main():
args = parse_args()
build = lab.LOCAL / 'rust-mob-discard-parity'
build.mkdir(parents=True, exist_ok=True)
report = build / 'report.json'