Files
workbuddy_skills/draw-ui/tests/test_calibration.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

88 lines
5.2 KiB
Python

"""从真实CLI入口验证尺寸与截图前提,不启动浏览器。"""
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
from PIL import Image
SCRIPTS = Path(__file__).resolve().parents[1] / 'scripts'
class CalibrationTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.root = Path(self.temp.name)
for name in ('reference', 'candidate'):
Image.new('RGB', (32, 64), 'white').save(self.root / (name+'.png'))
def tearDown(self):
self.temp.cleanup()
def compare(self, *extra):
return subprocess.run([sys.executable, str(SCRIPTS/'compare_mockup.py'), '--reference', str(self.root/'reference.png'), '--candidate', str(self.root/'candidate.png'), '--out-dir', str(self.root/'comparison'), *extra], capture_output=True, text=True)
def test_equal_and_invalid_clip(self):
self.assertEqual(self.compare().returncode, 0)
metrics=json.loads((self.root/'comparison/comparison-metrics.json').read_text())
self.assertFalse(metrics['resized'])
self.assertEqual(metrics['regions'][0]['mean_abs_diff'], 0)
for clip in ['bad:0,0,33,2', '../escape:0,0,1,1', 'full:0,0,1,1', 'bad:0,0,-1,1']:
with self.subTest(clip=clip):
self.assertNotEqual(self.compare('--clip', clip).returncode, 0)
def test_width_mismatch(self):
Image.new('RGB', (33,64), 'white').save(self.root/'candidate.png')
self.assertNotEqual(self.compare('--allow-height-difference').returncode, 0)
self.assertFalse((self.root/'comparison').exists())
def test_short_page_preserves_missing_tail(self):
Image.new('RGB', (32,50), 'white').save(self.root/'candidate.png')
self.assertNotEqual(self.compare().returncode, 0)
self.assertEqual(self.compare('--allow-height-difference','--clip','tail:0,50,32,14').returncode, 0)
metrics=json.loads((self.root/'comparison/comparison-metrics.json').read_text())
self.assertEqual(metrics['height_delta'], -14)
self.assertFalse(metrics['geometry_match'])
with Image.open(metrics['unmatched_tails']['reference']) as tail:
self.assertEqual(tail.size, (32,14))
self.assertIn('status',metrics['regions'][-1])
def test_capture_contract(self):
expected={'url':'http://127.0.0.1:4187/','title':'测试页','viewport':{'width':32,'height':64},'dpr':1,'full_page':False,'screenshot_scale':'css'}
capture={**expected,'fonts_ready':True,'images_ready':True,'document_height':64,'sections':[{'name':'hero','y':2}]}
asset={'src':'http://127.0.0.1:4187/logo-v2.png','natural_width':2048}
capture['assets']={'logo':asset}
manifest={'expected':expected,'capture_metadata':'capture.json','reference':'reference.png','candidate':'candidate.png','section_positions':{'hero':1},'expected_assets':{'logo':asset}}
(self.root/'run.json').write_text(json.dumps(manifest))
variants=[({},True),({'title':'其他项目'},False),({'url':'http://127.0.0.1:9999/'},False),({'fonts_ready':False},False),({'images_ready':False},False),({'dpr':2},False),({'viewport':{'width':31,'height':64}},False),({'screenshot_scale':'device'},False),({'assets':{}},False),({'assets':{'logo':{**asset,'src':'http://127.0.0.1:4187/old.png'}}},False),({'assets':{'logo':{**asset,'natural_width':100}}},False)]
for index,(changes,valid) in enumerate(variants):
with self.subTest(changes=changes):
(self.root/'capture.json').write_text(json.dumps({**capture,**changes}))
out=self.root/f'verify-{index}'
result=subprocess.run([sys.executable,str(SCRIPTS/'verify_capture.py'),'--manifest',str(self.root/'run.json'),'--out-dir',str(out)],capture_output=True,text=True)
self.assertEqual(result.returncode==0,valid,result.stderr)
if valid:
self.assertEqual(json.loads((out/'verification.json').read_text())['section_positions'][0]['delta_y'],1)
else:
self.assertFalse(out.exists())
def test_preflight_identity(self):
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write('<title>测试页</title>'.encode())
def log_message(self,*args):
pass
server=HTTPServer(('127.0.0.1',0),Handler)
thread=Thread(target=server.serve_forever,daemon=True)
thread.start()
try:
for title,valid in [('测试页',True),('其他项目',False)]:
(self.root/'run.json').write_text(json.dumps({'expected':{'url':f'http://127.0.0.1:{server.server_port}/','title':title}}))
result=subprocess.run([sys.executable,str(SCRIPTS/'verify_capture.py'),'--manifest',str(self.root/'run.json'),'--preflight'],capture_output=True,text=True)
self.assertEqual(result.returncode==0,valid,result.stderr)
finally:
server.shutdown()
server.server_close()
thread.join()
if __name__=='__main__':
unittest.main()