74 行
4.4 KiB
Python
74 行
4.4 KiB
Python
"""Capture all keyboard layouts and check clipping, round bounds and dark edge gaps."""
|
|
import argparse,datetime,importlib.util,json,math,pathlib,time
|
|
from PIL import Image
|
|
import layout
|
|
|
|
APP=pathlib.Path(__file__).resolve().parents[1]
|
|
spec=importlib.util.spec_from_file_location('native',APP/'tests/native.py')
|
|
native=importlib.util.module_from_spec(spec);spec.loader.exec_module(native);emu=native.emu
|
|
|
|
def dark_edges(path,profile):
|
|
im=Image.open(path).convert('RGB');points=[]
|
|
if profile.startswith('round'):
|
|
# 466px screen inside the vendor's 624px hardware skin. Stay 7px inside its edge.
|
|
im=im.crop((70,79,536,545))
|
|
for y in range(466):
|
|
for x in range(466):
|
|
radius=math.hypot(x-233,y-233)
|
|
if 224<=radius<=226 and max(im.getpixel((x,y)))<80:points.append([x,y])
|
|
else:
|
|
for y in range(4,446):
|
|
for x in [3,386]:
|
|
if max(im.getpixel((x,y)))<80:points.append([x,y])
|
|
for x in range(4,386):
|
|
for y in [3,446]:
|
|
if max(im.getpixel((x,y)))<80:points.append([x,y])
|
|
return len(points)
|
|
|
|
def main():
|
|
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--work',type=pathlib.Path,required=True);parser.add_argument('--output',type=pathlib.Path,required=True)
|
|
parser.add_argument('--profiles',nargs='+',default=emu.PROFILES);args=parser.parse_args();args.output.mkdir(parents=True,exist_ok=True)
|
|
report={'recorded_at':datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8))).isoformat(),'artifacts':{},'screens':[]}
|
|
def capture(profile,name):
|
|
# Inspector history can associate the new widget tree with the preceding
|
|
# back buffer on an otherwise idle page. Request two real redraws, restore
|
|
# the original caption, then retain only the fresh native frame.
|
|
emu.evaluate("var view=require('@system.router').default.getPages().slice(-1)[0];var captionKey=typeof view.hint==='string'?'hint':'status';var oldCaption=view[captionKey];view[captionKey]=oldCaption+' '")
|
|
time.sleep(.2)
|
|
emu.evaluate("view[captionKey]=oldCaption")
|
|
time.sleep(.4);emu.snapshot(profile+'-'+name,args.output)
|
|
time.sleep(.3);emu.snapshot(profile+'-'+name,args.output)
|
|
result=layout.inspect(args.output/(profile+'-'+name+'.tree.json'),profile)
|
|
result['dark_edge_pixels']=dark_edges(args.output/(profile+'-'+name+'.png'),profile)
|
|
result['passed']=result['passed'] and result['dark_edge_pixels']==0
|
|
result.update(profile=profile,screen=name);report['screens'].append(result)
|
|
native.save(args.output/'visual-results.json',report)
|
|
print(json.dumps(result,ensure_ascii=False),flush=True)
|
|
if not result['passed']:raise RuntimeError('Visual regression: '+profile+'-'+name)
|
|
for profile in args.profiles:
|
|
process,artifacts=emu.start(profile,args.work,'inputmethod');report['artifacts'][profile]=artifacts
|
|
try:
|
|
capture(profile,'home')
|
|
emu.evaluate("var r=require('@system.router').default,t=require('@system.test').default;var root=r.getPage(0);t.getByClass('document').click()")
|
|
time.sleep(1);emu.evaluate("var p=r.getPage(1);p.composition='64426';p.schedule()")
|
|
time.sleep(.5);capture(profile,'t9')
|
|
emu.evaluate("p.composition='shi';p.mode='py';p.renderKeys();p.schedule()")
|
|
time.sleep(.5);capture(profile,'pinyin')
|
|
emu.evaluate("p.expand()");capture(profile,'candidates')
|
|
emu.evaluate("p.closePanel();p.composition='nihao';p.schedule()")
|
|
time.sleep(.5);emu.evaluate("p.expand()");capture(profile,'phrase-candidates')
|
|
emu.evaluate("p.showTools()");capture(profile,'tools')
|
|
emu.evaluate("p.composition='';p.setMode('14');p.composition='c3754';p.schedule()")
|
|
time.sleep(.5);capture(profile,'14-key')
|
|
emu.evaluate("p.expand()");capture(profile,'14-candidates')
|
|
for mode in ['en','num','sym']:
|
|
emu.evaluate("p.composition='';p.setMode("+json.dumps(mode)+")")
|
|
capture(profile,mode)
|
|
emu.evaluate("p.symbols()");capture(profile,'sym2')
|
|
finally:
|
|
if process.poll()is None:process.terminate()
|
|
process.wait(timeout=10);process.stdin.close()
|
|
report['passed']=all(screen['passed']for screen in report['screens']);native.save(args.output/'visual-results.json',report)
|
|
|
|
if __name__=='__main__':main()
|