"""Native caret/QWERTY fixture using an owned emulator without Inspector.""" import argparse,hashlib,importlib.util,json,math,pathlib,shutil,subprocess,time 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;emu.APPS=['inputmethod'] PROBE=''' ''' 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);fixture=args.work/'fixture';fixture.mkdir(parents=True,exist_ok=True) shutil.copytree(APP/'src',fixture/'src',dirs_exist_ok=True);shutil.copy2(APP/'glyphix.config.js',fixture/'glyphix.config.js') metadata=json.loads((fixture/'src/manifest.json').read_text(encoding='utf-8'));metadata['router']['entry']='native-probe';metadata['router']['pages']['native-probe']={'component':'index'} (fixture/'src/manifest.json').write_text(json.dumps(metadata,ensure_ascii=False),encoding='utf-8') (fixture/'src/native-probe').mkdir(exist_ok=True);(fixture/'src/native-probe/index.ux').write_text(PROBE,encoding='utf-8') source_hash=hashlib.sha256((APP/'src/components/CrabInput/index.ux').read_bytes()).hexdigest() assert (fixture/'src/components/CrabInput/index.ux').read_bytes()==(APP/'src/components/CrabInput/index.ux').read_bytes() shell=shutil.which('pwsh')or'powershell';stable=pathlib.Path(r'C:\Users\herry\AppData\Local\Programs\PowerShell\7\pwsh.exe') if stable.exists():shell=str(stable) with (args.output/'fixture-build.log').open('w',encoding='utf-8')as log: result=subprocess.run([shell,'-NoProfile','-File',str(APP.parent/'build-rtos-game.ps1'),'-ProjectRoot',str(fixture)],stdout=log,stderr=subprocess.STDOUT) assert result.returncode==0,'See fixture-build.log' report={'method':'owned native console; production keyboard copied byte-for-byte into an auto-running fixture','production_keyboard_sha256':source_hash,'production_keyboard_path':'src/components/CrabInput/index.ux','production_route_sha256':hashlib.sha256((APP/'src/keyboard/index.ux').read_bytes()).hexdigest(),'inspector_used':False,'cases':[],'profiles':[],'passed':False} def rectangle(value): if isinstance(value,dict)and'rect'in value:value=value['rect'] return [value.get('left',value.get('x',0)),value.get('top',value.get('y',0)),value.get('width',value.get('w',0)),value.get('height',value.get('h',0))] for profile in args.profiles: image,artifacts=emu.prepare(profile,args.work) device='generic-watch-'+('466x466'if profile.startswith('round')else'390x450');package=metadata['package'] binary=fixture/'.glyphix-work'/('dist/'+device+'-quickapp/'+package+'/bundle.pkg'if profile.endswith('-quickapp')else'image/'+device+'/apps/'+package+'.pkg') shutil.copy2(binary,image/'apps'/(package+'.pkg')) command=[str(emu.ROOT/'temp/glyphix-v0.7.2-windows-x64/bin/glyphix-emu.exe'),'--config','pkgs.db','--target',package] if profile.startswith('round'):command+=['--width','624','--height','624','--dpi','326','--font-size','32','--round','--surface','surface/surface-466x466.json'] else:command+=['--width','390','--height','450','--dpi','321','--font-size','29'] command+=['--memory-profile','8192','--font-map','font-faces','--font-family','sans-serif'] log_path=args.output/(profile+'-console.log');seen=set();finished=False;captures=0 with log_path.open('w',encoding='utf-8')as log: process=subprocess.Popen(command,cwd=image,stdin=subprocess.PIPE,stdout=log,stderr=subprocess.STDOUT,creationflags=subprocess.CREATE_NO_WINDOW) try: deadline=time.monotonic()+100 while time.monotonic()width or y+h>height:failures.append(key['key']) if profile.startswith('round')and max(math.hypot(tx-233,ty-233)for tx in [x,x+w]for ty in [y,y+h])>231:failures.append(key['key']) state.update(failures=failures,passed=len(state['keys'])==26 and not failures) captures+=1;report['cases'].append(state);native.save(args.output/'caret-results.json',report);print(json.dumps(state,ensure_ascii=False),flush=True) if 'PROBE_DONE'in line:finished=True if finished:break time.sleep(.05) assert finished and captures==24,'Native fixture did not finish; see '+str(log_path) assert 'terminated:'not in log_path.read_text(encoding='utf-8',errors='replace'),'Native assertion failed' for state in report['cases']: if state['profile']!=profile:continue filename=profile+'-'+state['name']+'.png';snapshot=image/filename assert snapshot.is_file() and snapshot.read_bytes().startswith(b'\x89PNG\r\n\x1a\n'),'Screenshot missing: '+filename shutil.copy2(snapshot,args.output/filename);state['screenshot']=filename report['profiles'].append({'profile':profile,'native_case_switch_passed':True,'captures':captures,'fixture_binary_sha256':hashlib.sha256(binary.read_bytes()).hexdigest()}) finally: if process.poll()is None:process.terminate() process.wait(timeout=10);process.stdin.close() report['passed']=all(row['passed']for row in report['cases']);native.save(args.output/'caret-results.json',report) assert report['passed'],'See caret-results.json' if __name__=='__main__':main()