文件

74 行
4.5 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=require('pkg://com.sydxky.rtos.inputmethod/common/ime-lifecycle.js').activeInputs().filter(input=>input.$page===r.getPage(1))[0];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()