Files
workbuddy_skills/draw-ui/tests/test_measure_reference.py
T
admin 237a09a5b0 修:draw-ui / oil-motion 原被当子模块指针收录 ⇒ 改为正常文件入库(两份内容原先对别人是空的)
一、问题(本轮实测)
`draw-ui` 与 `oil-motion` 目录里**各自带一个内嵌 `.git`** ⇒ 上一次提交把它们记成了 **gitlink(子模块指针)**
⇒ 仓库里只存了一个不属于任何远端的 commit id,**别人克隆下来这两份是空的** ✗(`git status` 显示 ` m draw-ui` / ` m oil-motion` = 子模块内容有改动)。

二、处置(可回退)
· 把两处的 `.git` **挪走**(⛔ 不是删除)⇒ `归档/内嵌git-20261008/{draw-ui,oil-motion}.git`;
· `git rm --cached` 掉那两个 gitlink,再 `git add` 两个目录 ⇒ **按正常文件入库**(内容才真的进仓库)。

三、副作用(如实记)
挪走 `.git` 后,这两个技能**不能再原地 `git pull` 取上游更新**(要更新得重新拉一份覆盖);
如需恢复其本地仓库,把 `归档/内嵌git-20261008/` 里的 `.git` 挪回原处即可。
2026-10-08 22:29:52 +08:00

70 lines
4.9 KiB
Python

"""通过 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='<script>bad</script>')]}).returncode,0)
svg=(self.root/'result/annotations.svg').read_text();self.assertNotIn('<script>',svg);self.assertIn('&lt;script&gt;',svg)
if __name__=='__main__':unittest.main()