Files
RustRaptor/tools/test_hd_targt_rebuild.py
T
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

152 lines
9.1 KiB
Python

"""Portable synthetic behavioural tests; no engine, image service or old corpus."""
from pathlib import Path
import hashlib, json, tempfile, unittest, struct
import numpy as np
from PIL import Image
import hd_targt_rebuild as t
class RebuildTests(unittest.TestCase):
def test_main_component_excludes_disconnected_fringe(self):
a=np.zeros((20,30,4),dtype='uint8');a[4:18,7:26,3]=255;a[0,0,3]=255
self.assertEqual(t.main_painted_bounds(a),[7,4,26,18])
def test_nonuniform_mapping_keeps_source_canvas_alpha_region(self):
a=np.zeros((12,30,4),dtype='uint8');a[2:10,4:28,:3]=[90,70,50];a[2:10,4:28,3]=255
s=np.full((32,32),256,dtype='uint16');s[4:29,3:28]=195
image,b,sb=t.map_parent(a,s,4)
self.assertEqual(b,[4,2,28,10]);self.assertEqual(sb,[3,4,28,29])
self.assertEqual(image.shape,(128,128,3));self.assertTrue(np.all(image[16:116,12:112]==[90,70,50]))
self.assertEqual(int(image[:16].sum()),0)
def test_generated_correlated_texture_survives_quiet_soil(self):
y,x=np.indices((128,128));v=80+20*np.sin(x*.35)+10*np.cos(y*.8)
g=np.stack([v,v*.65,v*.5],2);src=np.full((32,32,3),[100,60,45],dtype='uint8')
out=t.broad_soil(g,src,np.ones((32,32),bool),4)
self.assertGreater(float(out[:,:,0].std()),1)
self.assertLess(float(out[:,:,0].std()),float(g[:,:,0].std()))
self.assertGreater(float(np.corrcoef((g-g.mean((0,1)))[:,:,0].ravel(),(out-out.mean((0,1)))[:,:,0].ravel())[0,1]),.8)
def test_source_pixel_checker_not_copied_into_broad_material(self):
y,x=np.indices((32,32));src=np.repeat((((x+y)%2)*140+40)[:,:,None],3,2).astype('uint8')
g=np.full((128,128,3),90.,dtype=float);out=t.broad_soil(g,src,np.ones((32,32),bool),4)
self.assertLess(float(out[:,:,0].std()),1)
def test_core_sampling_rejects_distant_warm_brown_rim(self):
a=np.zeros((128,128,3),float);a[16:120,12:16]=[110,55,12]
a[68:80,60:68]=[100,35,3]
mask=t.core_donor_mask(a,[15,17,17,20],4)
self.assertEqual(t.box(mask),[60,68,68,80])
def fixture(self,root):
def ref(p):return dict(path=str(p),sha256=hashlib.sha256(p.read_bytes()).hexdigest())
pal=np.zeros((256,3),dtype='uint8');pal[:32]=np.arange(31,-1,-1)[:,None]*2
pal[195]=[26,15,11];pal[81]=[59,44,7];pal[95]=[16,4,0]
pp=root/'palette';pp.write_bytes(pal.tobytes())
sources=[];frames=[]
for i in range(4):
s=np.full((32,32),256,dtype='uint16')
if i==0:s[:]=195
else:
s[4:29,3:28]=195
if i>=2:s[5:28,4:27]=31;s[12:25,10:22]=14;s[17:20,15:17]=95
if i==2:s[5:16,4:27]=195;s[16,4:27:2]=81;s[17:19,4:7]=10
p=root/('TEST_PIC'+('.'+str(i) if i else ''))
payload=struct.pack('<iiiii',0,0,0,32,32)
for y in range(32):
xs=np.flatnonzero(s[y]<256)
if len(xs):
l,r=int(xs[0]),int(xs[-1]+1);payload+=struct.pack('<iiii',l,y,0,r-l)+s[y,l:r].astype('uint8').tobytes()
payload+=struct.pack('<iiii',0,0,-1,0);p.write_bytes(payload)
sources.append(s);frames.append(dict(asset=str(i),source=ref(p),origin=[0,0]))
raw=np.zeros((80,4*80,4),dtype='uint8')
for i in range(4):
x=i*80;a=raw[:,x:x+80];y0,y1,x0,x1=(5,75,4,76) if i==0 else (10,70,10,70)
a[y0:y1,x0:x1,:3]=[100,62,45];a[y0:y1,x0:x1,3]=255
if i>=2:
a[12:68,12:68,:3]=[8,8,8];a[32:56,28:56,:3]=[110,110,110]
a[37:49,35:47,:3]=[8,8,8];a[39:47,38:44,:3]=[90,30,3]
if i==2:
a[12:40,12:68,:3]=[100,62,45];a[40:43,12:68,:3]=[235,170,25];a[43:49,12:22,:3]=[100,100,100]
rp=root/'raw.png';Image.fromarray(raw).save(rp)
recipe=dict(format_version=1,method=t.METHOD,output_scale=4,palette=ref(pp),raw_png=ref(rp),layout=dict(columns=4,rows=1),
frames=frames,soil_donor_frame=0,closed_donor_frame=1,module_donor_frame=3,warning_donor_frame=2)
return recipe,sources
def test_full_assembly_exact_alpha_warning_position_static_and_normalized_execution(self):
with tempfile.TemporaryDirectory() as td:
p=Path(td);recipe,sources=self.fixture(p);outputs,result=t.render(recipe,p)
for o,s in zip(outputs,sources):self.assertTrue(np.array_equal(o[:,:,3],np.repeat(np.repeat(s<256,4,0),4,1)*255))
self.assertEqual(result['frames'][2]['warning_Source_y'],16)
strip=outputs[2][16*4:17*4,4*4:27*4,:3]
self.assertGreater(float(strip[:,:,0].mean()),float(strip[:,:,2].mean())*2)
static=np.all(np.stack(sources)[1:]==sources[1],axis=0)&(sources[1]<256);big=np.repeat(np.repeat(static,4,0),4,1)
self.assertTrue(np.array_equal(outputs[1][big],outputs[3][big]))
rp=p/'recipe.json';rp.write_text(json.dumps(recipe));result=t.run(rp,p/'new')
self.assertEqual(set(result['execution']),{'status','method','recipe_sha256','tool_sha256','source_sha256','parent_raw_sha256','output_sha256'})
self.assertEqual(result['execution']['parent_raw_sha256'],[recipe['raw_png']['sha256']])
self.assertEqual(len(result['execution']['output_sha256']),4)
bindings=result['implementation_bindings']
decoder=Path(bindings['decoder']['path'])
self.assertEqual(bindings['decoder']['sha256'],hashlib.sha256(decoder.read_bytes()).hexdigest())
self.assertEqual(set(bindings['runtime']),{'python','numpy','pillow','executable'})
self.assertTrue(all(type(v) is str and v for v in bindings['runtime'].values()))
with self.assertRaisesRegex(ValueError,'no overwrite'):t.run(rp,p/'new')
def test_bad_hash_fails_before_creating_output(self):
with tempfile.TemporaryDirectory() as td:
p=Path(td);recipe,_=self.fixture(p);recipe['frames'][2]['source']['sha256']='0'*64
rp=p/'recipe.json';rp.write_text(json.dumps(recipe))
with self.assertRaisesRegex(ValueError,'SHA mismatch'):t.run(rp,p/'absent')
self.assertFalse((p/'absent').exists())
def test_duplicate_partition_origin_and_bool_scale_rejected(self):
with tempfile.TemporaryDirectory() as td:
recipe,_=self.fixture(Path(td))
for mutation in ('duplicate','partition','origin','boolscale'):
r=json.loads(json.dumps(recipe))
if mutation=='duplicate':r['frames'][1]['asset']=r['frames'][0]['asset']
if mutation=='partition':r['frames'].pop()
if mutation=='origin':r['frames'][0]['origin']=[1,0]
if mutation=='boolscale':r['output_scale']=True
with self.assertRaises(ValueError):t.validate_recipe(r)
def test_path_escape_and_absolute_asset_fail_before_output_or_sentinel_write(self):
with tempfile.TemporaryDirectory() as td:
p=Path(td);recipe,_=self.fixture(p)
sentinels=[p/'sentinel-derived.png',p/'absolute-sentinel-derived.png']
for sentinel in sentinels:sentinel.write_bytes(b'KEEP-EXISTING-ART-EXACT')
for i,bad in enumerate(['../sentinel',str(p/'absolute-sentinel'),'0/../sentinel','../0','',None,False,'0x10152','ABC','0001','100000000']):
r=json.loads(json.dumps(recipe));r['frames'][0]['asset']=bad
rp=p/('bad'+str(i)+'.json');rp.write_text(json.dumps(r));out=p/('absent'+str(i))
with self.assertRaisesRegex(ValueError,'canonical lowercase'):t.run(rp,out)
self.assertFalse(out.exists())
for sentinel in sentinels:self.assertEqual(sentinel.read_bytes(),b'KEEP-EXISTING-ART-EXACT')
def test_boolean_version_and_origin_are_not_integer_identity(self):
with tempfile.TemporaryDirectory() as td:
recipe,_=self.fixture(Path(td))
for mutate in ('version','origin'):
r=json.loads(json.dumps(recipe))
if mutate=='version':r['format_version']=True
else:r['frames'][0]['origin']=[False,0]
with self.assertRaises(ValueError):t.validate_recipe(r)
def test_complete_numeric_asset_and_Source_picture_order(self):
with tempfile.TemporaryDirectory() as td:
recipe,_=self.fixture(Path(td))
for mutate in ('gap','reversed','source_swapped'):
r=json.loads(json.dumps(recipe))
if mutate=='gap':r['frames'][-1]['asset']='5'
if mutate=='reversed':r['frames'][0]['asset'],r['frames'][1]['asset']=r['frames'][1]['asset'],r['frames'][0]['asset']
if mutate=='source_swapped':r['frames'][1]['source'],r['frames'][2]['source']=r['frames'][2]['source'],r['frames'][1]['source']
with self.assertRaisesRegex(ValueError,'order'):t.validate_recipe(r)
def test_repeated_synthetic_render_is_byte_deterministic(self):
with tempfile.TemporaryDirectory() as td:
p=Path(td);recipe,_=self.fixture(p)
a,ma=t.render(recipe,p);b,mb=t.render(recipe,p)
self.assertEqual(ma,mb)
for x,y in zip(a,b):self.assertEqual(x.tobytes(),y.tobytes())
if __name__=='__main__':unittest.main()