Expose the runtime block/item registry through compact material search and single-material descriptions. Preserve private block-entity data through checked edits and durable undo without sending payloads to model context. Include the completed terrain tools, isolated world/map plugin, station lift, ACP streaming and guidance fixes, local camera auto-connect, construction scripts, and their public documentation, references and verification records. Active station decoration and private runtime data remain outside this commit. Validation: 145 Maven tests, 47 Bridge tests, successful camera Gradle build, and isolated Paper verification of all 1,196 block defaults plus 5,392 independent property cases for placement, same-material edits and restoration.
200 lines
10 KiB
Python
200 lines
10 KiB
Python
"""Read-only survey completeness, cache isolation and path-height regression tests."""
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
_spec = importlib.util.spec_from_file_location('foundation_survey', Path(__file__).with_name('foundation-survey.py'))
|
|
survey = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(survey)
|
|
SCOPE = {'project_id': 'project', 'world_id': 'world', 'world_epoch': 'epoch'}
|
|
|
|
|
|
class Backend:
|
|
def __init__(self, states=None):
|
|
self.states = states or {}
|
|
self.calls = []
|
|
self.mutate = None
|
|
self.fail_call = None
|
|
self.context = {**SCOPE, 'region': {'min': {'x': -64, 'y': -64, 'z': -64},
|
|
'max': {'x': 63, 'y': 127, 'z': 63}}}
|
|
|
|
def call(self, method, **params):
|
|
self.calls.append(method)
|
|
if method == 'project_context':
|
|
return self.context
|
|
if method != 'region_inspect':
|
|
raise AssertionError('Only read-only RPC is allowed')
|
|
if self.fail_call == self.calls.count('region_inspect'):
|
|
raise RuntimeError('chunk_not_loaded')
|
|
if survey.volume(params) > 4096:
|
|
raise AssertionError('Unbounded inspection')
|
|
lo, hi = params['min'], params['max']
|
|
result = {'world_epoch': self.context['world_epoch'], 'truncated': False,
|
|
'blocks': [{'pos': {'x': x, 'y': y, 'z': z}, 'state': self.states.get((x, y, z), 'minecraft:air')}
|
|
for x in range(lo['x'], hi['x'] + 1) for y in range(lo['y'], hi['y'] + 1)
|
|
for z in range(lo['z'], hi['z'] + 1)]}
|
|
if self.mutate:
|
|
self.mutate(result)
|
|
return result
|
|
|
|
|
|
def box(lo=(-2, 64, -2), hi=(2, 68, 2)):
|
|
return survey.box_cells(dict(zip(survey.AXES, lo)), dict(zip(survey.AXES, hi)))
|
|
|
|
|
|
class SurveyTests(unittest.TestCase):
|
|
def capture(self, backend=None, cells=None):
|
|
backend = backend or Backend()
|
|
cells = cells or box()
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'survey.json.gz'
|
|
result = survey.scan(backend, cells, path)
|
|
loaded = survey.load_snapshot(path, SCOPE)
|
|
self.assertEqual(loaded.document, result.document)
|
|
return loaded
|
|
|
|
def test_negative_cell_partition_and_exact_states(self):
|
|
states = {(-2, 64, -2): 'minecraft:smooth_stone_slab[type=bottom,waterlogged=false]'}
|
|
result = self.capture(Backend(states))
|
|
self.assertEqual(result.state(-2, 64, -2), states[(-2, 64, -2)])
|
|
self.assertEqual(result.state(2, 68, 2), 'minecraft:air')
|
|
self.assertEqual(len(result.cells), 4)
|
|
with self.assertRaises(KeyError):
|
|
result.state(-3, 64, -2)
|
|
with self.assertRaises(KeyError):
|
|
result.state(17, 64, 0)
|
|
|
|
def test_columns_do_not_assume_air_in_unread_cells(self):
|
|
cells = survey.column_cells({'version': 1, 'columns': [
|
|
{'x': -20, 'z': 1, 'min_y': 64, 'max_y': 68},
|
|
{'x': 20, 'z': 1, 'min_y': 66, 'max_y': 72}]})
|
|
result = self.capture(cells=cells)
|
|
self.assertEqual(result.state(20, 69, 1), 'minecraft:air')
|
|
with self.assertRaises(KeyError):
|
|
result.state(0, 69, 1)
|
|
with self.assertRaises(KeyError):
|
|
result.state(20, 65, 1)
|
|
|
|
def test_rejects_missing_duplicate_truncated_and_wrong_epoch(self):
|
|
changes = [lambda r: r['blocks'].pop(),
|
|
lambda r: r['blocks'].append(r['blocks'][0]),
|
|
lambda r: r.update(truncated=True),
|
|
lambda r: r.update(world_epoch='different')]
|
|
for mutate in changes:
|
|
with self.subTest(mutate=mutate), tempfile.TemporaryDirectory() as directory:
|
|
backend = Backend()
|
|
backend.mutate = mutate
|
|
path = Path(directory) / 'survey.json.gz'
|
|
with self.assertRaises(RuntimeError):
|
|
survey.scan(backend, box(), path)
|
|
self.assertFalse(path.exists())
|
|
|
|
def test_out_of_area_fails_before_any_inspection(self):
|
|
backend = Backend()
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
with self.assertRaises(ValueError):
|
|
survey.scan(backend, box((-65, 64, 0), (-60, 68, 0)), Path(directory) / 'survey.json.gz')
|
|
self.assertEqual(backend.calls, ['project_context'])
|
|
|
|
def test_explicit_resume_keeps_readonly_cache_and_checks_scope(self):
|
|
backend = Backend()
|
|
backend.fail_call = 2
|
|
cells = box((-16, 64, 0), (16, 68, 0))
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'survey.json.gz'
|
|
with self.assertRaisesRegex(RuntimeError, 'chunk_not_loaded'):
|
|
survey.scan(backend, cells, path)
|
|
self.assertFalse(path.exists())
|
|
with self.assertRaisesRegex(ValueError, '--resume'):
|
|
survey.scan(backend, cells, path)
|
|
backend.context['world_epoch'] = 'another-epoch'
|
|
with self.assertRaisesRegex(ValueError, 'scope'):
|
|
survey.scan(backend, cells, path, resume=True)
|
|
backend.context['world_epoch'] = 'epoch'
|
|
backend.fail_call = None
|
|
result = survey.scan(backend, cells, path, resume=True)
|
|
self.assertTrue(result.document['resumed_cache'])
|
|
self.assertEqual(backend.calls.count('region_inspect'), 4)
|
|
with self.assertRaisesRegex(ValueError, 'fresh path'):
|
|
survey.scan(backend, cells, path, resume=True)
|
|
|
|
def test_corrupt_cache_and_missing_snapshot_indices_are_rejected(self):
|
|
snapshot = self.capture()
|
|
snapshot.document['cells'][0]['indices'].pop()
|
|
with self.assertRaisesRegex(ValueError, 'indices'):
|
|
survey.Snapshot(snapshot.document, SCOPE)
|
|
|
|
def test_foreign_scope_rejected(self):
|
|
snapshot = self.capture()
|
|
with self.assertRaisesRegex(ValueError, 'another project'):
|
|
survey.Snapshot(snapshot.document, {**SCOPE, 'project_id': 'other'})
|
|
|
|
def test_walkability_half_slab_fullblock_clearance_and_gradient(self):
|
|
states = {(0, 64, 0): 'minecraft:stone',
|
|
(1, 65, 0): 'minecraft:smooth_stone_slab[type=bottom,waterlogged=false]',
|
|
(2, 65, 0): 'minecraft:stone'}
|
|
snapshot = self.capture(Backend(states), box((0, 64, 0), (3, 69, 0)))
|
|
points = [{'x': x, 'z': 0, 'standing_y': feet, 'route': 'main'}
|
|
for x, feet in [(0, 65), (1, 65.5), (2, 66)]]
|
|
result = survey.verify_walkable(snapshot, points)
|
|
self.assertTrue(result['passed'])
|
|
self.assertEqual(result['checked_route_edges'], 2)
|
|
points[1]['standing_y'] = 66
|
|
result = survey.verify_walkable(snapshot, points)
|
|
self.assertFalse(result['passed'])
|
|
self.assertIn('missing_support_at_feet', [f['reason'] for f in result['failures']])
|
|
self.assertIn('route_step_exceeds_half_block', [f['reason'] for f in result['failures']])
|
|
|
|
def test_headroom_unknown_stairs_and_absent_observations_fail(self):
|
|
states = {(0, 64, 0): 'minecraft:stone', (0, 66, 0): 'minecraft:stone',
|
|
(1, 64, 0): 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'}
|
|
snapshot = self.capture(Backend(states), box((0, 64, 0), (1, 68, 0)))
|
|
result = survey.verify_walkable(snapshot, [{'x': x, 'z': 0, 'standing_y': 65} for x in (0, 1, 2)])
|
|
self.assertEqual([f['reason'] for f in result['failures']],
|
|
['blocked_headroom', 'unknown_support_shape', 'unobserved_block'])
|
|
|
|
def test_straight_bottom_stair_profile_all_directions(self):
|
|
sides = {'north': ((.5, .75), (.5, .25)), 'south': ((.5, .25), (.5, .75)),
|
|
'east': ((.25, .5), (.75, .5)), 'west': ((.75, .5), (.25, .5))}
|
|
for facing, (low, high) in sides.items():
|
|
with self.subTest(facing=facing):
|
|
state = f'minecraft:stone_brick_stairs[facing={facing},half=bottom,shape=straight,waterlogged=false]'
|
|
snapshot = self.capture(Backend({(0, 64, 0): state}), box((0, 64, 0), (0, 68, 0)))
|
|
points = [{'x': 0, 'z': 0, 'sub_x': sub[0], 'sub_z': sub[1], 'standing_y': feet, 'route': facing}
|
|
for sub, feet in [(low, 64.5), (high, 65)]]
|
|
result = survey.verify_walkable(snapshot, points)
|
|
self.assertTrue(result['passed'], result)
|
|
self.assertEqual(result['checked_route_edges'], 1)
|
|
points[0]['standing_y'] = 65
|
|
self.assertEqual(survey.verify_walkable(snapshot, points)['failures'][0]['reason'], 'missing_support_at_feet')
|
|
|
|
def test_stair_headroom_riser_boundary_and_unsupported_shapes(self):
|
|
base = 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'
|
|
snapshot = self.capture(Backend({(0, 64, 0): base, (0, 66, 0): 'minecraft:stone'}),
|
|
box((0, 64, 0), (0, 68, 0)))
|
|
result = survey.verify_walkable(snapshot, [{'x': 0, 'z': 0, 'sub_z': .25, 'standing_y': 65}])
|
|
self.assertEqual(result['failures'][0]['reason'], 'blocked_headroom')
|
|
self.assertIsNone(survey.vertical_shape(base, .5, .5))
|
|
self.assertIsNone(survey.vertical_shape(base.replace('straight', 'inner_left'), .5, .25))
|
|
self.assertIsNone(survey.vertical_shape(base.replace('half=bottom', 'half=top'), .5, .25))
|
|
self.assertIsNone(survey.vertical_shape(base.replace('waterlogged=false', 'waterlogged=true'), .5, .25))
|
|
|
|
def test_route_subsamples_use_physical_positions_across_blocks(self):
|
|
state = 'minecraft:stone_brick_stairs[facing=north,half=bottom,shape=straight,waterlogged=false]'
|
|
snapshot = self.capture(Backend({(0, 64, 0): state, (0, 65, -1): state}),
|
|
box((0, 64, -1), (0, 70, 0)))
|
|
points = [{'x': 0, 'z': z, 'sub_z': sub_z, 'standing_y': feet, 'route': 'stair'}
|
|
for z, sub_z, feet in [(0, .75, 64.5), (0, .25, 65), (-1, .75, 65.5), (-1, .25, 66)]]
|
|
result = survey.verify_walkable(snapshot, points)
|
|
self.assertTrue(result['passed'], result)
|
|
self.assertEqual(result['checked_route_edges'], 3)
|
|
points[1]['sub_x'] = .6
|
|
result = survey.verify_walkable(snapshot, points)
|
|
self.assertIn('non_cardinal_route_step', [f['reason'] for f in result['failures']])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|