Files
RustRaptor/tools/test_hd_worm_cleanup.py
raptorreplica 00304e25c1 Remaster fifteen original pictures with reproducible artwork cleanup
Add the final boss picture, three coherent terrain remasters and complete DSHP20G3/WORMRITE catalogue families. Preserve generated parents, portable recipes, derivation receipts and separate artistic/context reviews.

Validate 5500 HD pictures with zero rejects and all 45 mods. Review three unchanged 1080p SDL controls, preserve existing runtime/data bindings, and update Linux/Windows shared-data metadata and progress records. Cleanup tools pass 101 tests plus 12 TARGT input-safety tests; TARGT artwork remains pending.
2026-10-18 11:35:24 +11:00

141 lines
7.7 KiB
Python

"""Focused synthetic alignment/form/animation/provenance checks, no game data."""
import copy
import hashlib
import json
from pathlib import Path
import struct
import tempfile
import unittest
import numpy as np
from PIL import Image
import hd_worm_cleanup as cleanup
class WormCleanup(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.sources = []
for phase in range(2):
cells = np.full((8, 8), 256, dtype=np.uint16)
cells[1:7, 1:7] = 10
cells[2:6, 2:6] = 20+phase
cells[3, 6] = 30
cells[6, 2] = 30
self.sources.append(cells)
payload = bytearray(struct.pack('<5i', 0, 0, 0, 8, 8))
for y, x in np.argwhere(cells < 256):
payload += struct.pack('<4i', int(x), int(y), int(y*8+x), 1)+bytes([cells[y, x]])
payload += struct.pack('<4i', 0, 0, -1, 0)
(self.root/f'Source{phase}').write_bytes(payload)
palette = bytearray(768)
for index, rgb in [(10, [12, 12, 12]), (20, [25, 15, 12]), (21, [36, 24, 18]), (30, [0, 0, 14])]:
palette[index*3:index*3+3] = bytes(rgb)
(self.root/'palette').write_bytes(palette)
self.raw = np.zeros((64, 128, 4), dtype=np.uint8)
for phase in range(2):
for y in range(8, 56):
for x in range(8, 56):
warm = 16 <= x < 48 and 16 <= y < 48
rgb = [70+x*2, 30+y, 25+x//2] if warm else [25+x//2+phase*8, 25+y//2+phase*8, 25+(x+y)//4+phase*8]
self.raw[y, x+phase*64] = [*rgb, 255]
self.save_raw()
bind = lambda p: {'path':p,'sha256':cleanup.sha((self.root/p).read_bytes())}
self.recipe = dict(format_version=1, label='synthetic only', palette=bind('palette'), output_scale=8,
feather_logical_pixels=1, frames=[dict(asset=f'3003{phase}', source=bind(f'Source{phase}'),
parent_raw=bind('raw.png'), logical_canvas=[8,8], origin=[0,0],
atlas_grid=dict(columns=2,rows=1,column=phase,row=0),
raw_painted_bounds=[8+phase*64,8,56+phase*64,56]) for phase in range(2)])
def save_raw(self):
Image.fromarray(self.raw).save(self.root/'raw.png')
def rebind_raw(self, recipe):
for frame in recipe['frames']:
frame['parent_raw']['sha256'] = cleanup.sha((self.root/'raw.png').read_bytes())
def test_source_alpha_cues_and_continuous_generated_form_survive(self):
outputs, report = cleanup.render_family(self.recipe, self.root)
self.assertFalse(report['per_Source_cell_RGB_reconstruction'])
for source, output, frame in zip(self.sources, outputs, report['frames']):
occupied = cleanup.up(source<256,8)
self.assertTrue(np.array_equal(output[:,:,3]==255,occupied))
self.assertTrue(np.all(output[~occupied]==0))
self.assertTrue(np.all(output[24:32,48:56,:3]==[0,0,56]))
# Source warm cells are one flat color, but generated gradients must
# remain different across cells and within each cell after cleanup.
self.assertGreater(np.ptp(output[24:40,24:40,0]),15)
self.assertNotEqual(float(output[24:32,24:32,0].mean()),float(output[24:32,32:40,0].mean()))
self.assertEqual(frame['functional_blue_Source_RGB_cells'],2)
def test_stationary_generated_backing_shared_and_early_phase_dimmer(self):
outputs, report = cleanup.render_family(self.recipe, self.root)
common = cleanup.up(self.sources[0]==self.sources[1],8)
self.assertTrue(np.array_equal(outputs[0][common],outputs[1][common]))
self.assertLess(outputs[0][24:40,24:40,:3].mean(),outputs[1][24:40,24:40,:3].mean())
self.assertEqual(report['Source_changing_union_cells'],16)
def test_generated_gap_fill_is_local_and_never_source_rgb(self):
rgb=np.array([[[10,20,30],[90,80,70]],[[60,50,40],[1,2,3]]],dtype=np.uint8)
trusted=np.array([[True,False],[False,True]])
filled,count=cleanup.nearest_generated_fill(rgb,trusted,np.ones((2,2),dtype=bool))
self.assertEqual(count,2)
self.assertTrue(np.array_equal(filled[trusted],rgb[trusted]))
self.assertTrue(np.array_equal(filled[0,1],rgb[0,0]))
self.assertTrue(np.array_equal(filled[1,0],rgb[0,0]))
def test_blend_never_modifies_stationary_region(self):
mask=np.zeros((12,12),dtype=bool);mask[2:10,2:10]=True
weight=cleanup.inner_weight(mask,4)
self.assertTrue(np.all(weight[~mask]==0))
self.assertEqual(weight[2,2],0.25)
self.assertEqual(weight[5,5],1)
def test_invalid_identity_bounds_grid_origin_fail_before_output(self):
mutations=[lambda r:r['frames'][0]['source'].update(sha256='0'*64),
lambda r:r['frames'][0].update(origin=[False,0]),
lambda r:r['frames'][0].update(origin=[1,0]),
lambda r:r['frames'][0].update(raw_painted_bounds=[9,8,56,56]),
lambda r:r['frames'][0]['atlas_grid'].update(column=2),
lambda r:r['frames'][0]['atlas_grid'].update(rows=2),
lambda r:r.update(output_scale=True)]
for mutate in mutations:
with self.subTest(mutate=mutate):
recipe=copy.deepcopy(self.recipe);mutate(recipe)
with self.assertRaises(ValueError):cleanup.render_family(recipe,self.root)
def test_opaque_and_detail_free_parents_rejected(self):
self.raw[:,:,3]=255;self.save_raw();r=copy.deepcopy(self.recipe);self.rebind_raw(r)
with self.assertRaisesRegex(ValueError,'transparent alpha'):cleanup.render_family(r,self.root)
self.raw[:]=0
self.raw[8:56,8:56]=[80,50,40,255];self.raw[8:56,72:120]=[80,50,40,255]
self.save_raw();self.rebind_raw(r)
with self.assertRaisesRegex(ValueError,'no generated spatial RGB'):cleanup.render_family(r,self.root)
def test_repeatability_and_full_execution_decoder_version_bindings(self):
recipe=self.root/'recipe.json';recipe.write_text(json.dumps(self.recipe,sort_keys=True)+'\n')
original={p.name:p.read_bytes() for p in self.root.iterdir() if p.is_file()}
reports=[cleanup.run_recipe(recipe,self.root/f'derived{i}') for i in range(2)]
self.assertEqual(reports[0],reports[1])
report=reports[0];execution=report['execution']
self.assertEqual(execution['status'],'success')
self.assertEqual(execution['method'],cleanup.METHOD)
self.assertEqual(execution['recipe_sha256'],hashlib.sha256(recipe.read_bytes()).hexdigest())
self.assertEqual(execution['tool_sha256'],cleanup.sha(Path(cleanup.__file__).read_bytes()))
self.assertEqual(report['decoder_module']['sha256'],cleanup.sha(Path(cleanup.hd_frame_policy.__file__).read_bytes()))
for field in ['python_version','numpy_version','pillow_version']:self.assertTrue(report[field])
self.assertEqual(execution['source_sha256'],{f['asset']:f['source']['sha256'] for f in self.recipe['frames']})
self.assertEqual(len(execution['parent_raw_sha256']),1)
for frame in self.recipe['frames']:
name=frame['asset']+'-derived.png';p=self.root/'derived0'/name
self.assertEqual(p.read_bytes(),(self.root/'derived1'/name).read_bytes())
self.assertEqual(execution['output_sha256'][frame['asset']],cleanup.sha(p.read_bytes()))
for name,data in original.items():self.assertEqual((self.root/name).read_bytes(),data)
with self.assertRaisesRegex(ValueError,'output exists'):cleanup.run_recipe(recipe,self.root/'derived0')
if __name__=='__main__':unittest.main()