"""通过 CLI 验证测量边界;只使用临时合成图,不声称为真实页面效果评估。"""
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from PIL import Image
SCRIPT=Path(__file__).resolve().parents[1]/'scripts'/'measure_reference.py'
class MeasureReferenceTests(unittest.TestCase):
def setUp(self):
self.tmp=tempfile.TemporaryDirectory(prefix='reference measurement ')
self.root=Path(self.tmp.name)
self.image=Image.new('RGBA',(40,40),(12,16,17,255))
self.image.save(self.root/'source.png')
def tearDown(self):self.tmp.cleanup()
def run_cli(self,data,out='result'):
p=self.root/'manifest.json';p.write_text(json.dumps({'reference':'source.png',**data}))
result=subprocess.run([sys.executable,str(SCRIPT),'--manifest',str(p),'--out-dir',str(self.root/out)],capture_output=True,text=True)
return result
def region(self,**kw):return {'name':'sample','kind':'color','box':[0,0,40,40],**kw}
def read(self,out='result'):return json.loads((self.root/out/'measurements.json').read_text())
def test_color_and_width_mapping(self):
r=self.run_cli({'css_viewport_width':20,'regions':[self.region()]})
self.assertEqual(r.returncode,0,r.stderr)
d=self.read();self.assertEqual(d['regions'][0]['color']['median_hex'],'#0C1011')
self.assertEqual(d['regions'][0]['css_box'],[0,0,20,20]);self.assertFalse(d['regions'][0]['dom_comparable'])
def test_transparent_pixels_excluded(self):
self.image.paste((255,0,0,0),(0,0,20,40));self.image.save(self.root/'source.png')
r=self.run_cli({'regions':[self.region()]});self.assertEqual(r.returncode,0,r.stderr)
c=self.read()['regions'][0]['color'];self.assertEqual(c['median_hex'],'#0C1011');self.assertEqual(c['excluded_nonopaque'],800)
def test_no_opaque_samples_rejected(self):
self.image.putalpha(0);self.image.save(self.root/'source.png')
r=self.run_cli({'regions':[self.region()]});self.assertEqual(r.returncode,2);self.assertFalse((self.root/'result').exists())
def test_boundary_and_invalid_types_rejected(self):
for box in [[-1,0,20,20],[0,0,41,40],[0,0,0,20],[0.5,0,20,20],[True,0,20,20]]:
with self.subTest(box=box):
r=self.run_cli({'regions':[self.region(box=box)]});self.assertEqual(r.returncode,2);self.assertFalse((self.root/'result').exists())
def test_inset(self):
r=self.run_cli({'regions':[self.region(inset=2)]});self.assertEqual(r.returncode,0)
self.assertEqual(self.read()['regions'][0]['color']['opaque_samples'],36*36)
r=self.run_cli({'regions':[self.region(inset=20)]},out='invalid');self.assertEqual(r.returncode,2)
def test_existing_output_refused(self):
data={'regions':[self.region()]};self.assertEqual(self.run_cli(data).returncode,0)
before=(self.root/'result/measurements.json').read_bytes()
self.assertEqual(self.run_cli(data).returncode,2);self.assertEqual(before,(self.root/'result/measurements.json').read_bytes())
def test_duplicate_names_refused(self):
self.assertEqual(self.run_cli({'regions':[self.region(),self.region()]}).returncode,2)
def test_grid_reports_unequal_widths(self):
regions=[{'name':'a','box':[0,2,10,10],'group':'cards'},{'name':'b','box':[13,2,15,10],'group':'cards'}]
self.assertEqual(self.run_cli({'regions':regions}).returncode,0)
g=self.read()['groups'][0];self.assertEqual(g['x_gaps'],[3]);self.assertEqual(g['width_spread'],5)
def test_ink_not_dom_comparable_or_grid(self):
region={'name':'title','box':[0,0,20,20],'box_type':'ink','selector':'h1'}
self.assertEqual(self.run_cli({'regions':[region]}).returncode,0)
self.assertFalse(self.read()['regions'][0]['dom_comparable'])
self.assertEqual(self.run_cli({'regions':[{**region,'group':'bad'}]},out='invalid').returncode,2)
def test_scan_includes_final_run(self):
self.image.paste((230,225,215,255),(0,30,40,40));self.image.save(self.root/'source.png')
scan={'name':'band','x':1,'y':0,'height':40,'min_channel':175,'max_channel_spread':45,'min_run':5}
self.assertEqual(self.run_cli({'vertical_scans':[scan]}).returncode,0)
self.assertEqual(self.read()['vertical_scans'][0]['runs'],[{'y_start':30,'y_end_exclusive':40,'height':10}])
def test_no_mapping_when_not_known(self):
self.assertEqual(self.run_cli({'regions':[self.region()]}).returncode,0)
self.assertNotIn('css_box',self.read()['regions'][0]);self.assertIsNone(self.read()['css_mapping']['css_per_image_pixel'])
def test_svg_escapes_labels(self):
self.assertEqual(self.run_cli({'regions':[self.region(name='')]}).returncode,0)
svg=(self.root/'result/annotations.svg').read_text();self.assertNotIn('