import unittest,tempfile,csv,json,subprocess,sys,os from pathlib import Path import numpy as np from PIL import Image from irstd.data import sha,read_manifest class PipelineTests(unittest.TestCase): def make_fixture(self,root): rows=[];pred=root/'pred';pred.mkdir() for i,split in enumerate(['train','dev','cal','cal','test']): name=f'image{i}';x=np.full((9,9),i,np.uint8);x[4,4]=200+i y=np.zeros((9,9),np.uint8);y[4,4]=255 Image.fromarray(x).save(root/(name+'.png'));Image.fromarray(y).save(root/(name+'_mask.png')) prob=np.zeros((9,9),np.float32);prob[4,4]=.9;np.save(pred/(name+'.npy'),prob) rows.append(dict(id=name,image=name+'.png',mask=name+'_mask.png',split=split,group='g'+str(i))) manifest=root/'manifest.csv' with manifest.open('w',newline='') as f: w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows) (pred/'provenance.json').write_text(json.dumps(dict(checkpoint_sha256='synthetic_fixture_not_a_checkpoint',manifest_sha256=sha(manifest)))) return manifest,pred,rows def run_script(self,name,args): return subprocess.run([sys.executable,'scripts/'+name]+list(map(str,args)),capture_output=True,text=True) def test_cpu_calibration_evaluation_and_tamper_guard(self): with tempfile.TemporaryDirectory() as d: root=Path(d);manifest,pred,_=self.make_fixture(root) cert=root/'cert.json';out=root/'eval.json' common=['--manifest',manifest,'--predictions',pred] r=self.run_script('evaluate.py',common+['--split','cal','--calibrate','--out',cert,'--alpha','.01']) self.assertEqual(r.returncode,0,r.stderr);self.assertTrue(json.loads(cert.read_text())['reject_all']) r=self.run_script('evaluate.py',common+['--certificate',cert,'--out',out]) self.assertEqual(r.returncode,0,r.stderr);self.assertEqual(json.loads(out.read_text())['metrics']['Pd'],0.) np.save(pred/'image4.npy',np.ones((9,9),np.float32)) r=self.run_script('evaluate.py',common+['--certificate',cert,'--out',root/'tampered.json']) self.assertNotEqual(r.returncode,0);self.assertIn('invalidated',r.stderr) def test_data_audit_cli(self): with tempfile.TemporaryDirectory() as d: root=Path(d);manifest,_,_=self.make_fixture(root);out=root/'audit.json' r=self.run_script('audit_data.py',['--manifest',manifest,'--out',out]) self.assertEqual(r.returncode,0,r.stderr);report=json.loads(out.read_text()) self.assertEqual(report['area_bins']['1-4'],5);self.assertEqual(report['splits']['cal'],2) def test_group_leakage_rejected(self): with tempfile.TemporaryDirectory() as d: root=Path(d);manifest,_,rows=self.make_fixture(root);rows[-1]['group']=rows[0]['group'] with manifest.open('w',newline='') as f: w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows) with self.assertRaisesRegex(ValueError,'crosses splits'):read_manifest(manifest)