libxkbcommon/test/xkeyboard-config-test.py.in

201 lines
5.7 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import sys
import subprocess
import os
import io
import xml.etree.ElementTree as ET
from multiprocessing import Pool
verbose = True
DEFAULT_RULES_XML = '@XKB_CONFIG_ROOT@/rules/evdev.xml'
# Meson needs to fill this in so we can call the tool in the buildir.
EXTRA_PATH = '@MESON_BUILD_ROOT@'
os.environ['PATH'] = ':'.join([EXTRA_PATH, os.getenv('PATH')])
def noop_progress_bar(x, total):
return x
# The function generating the progress bar (if any).
progress_bar = noop_progress_bar
if os.isatty(sys.stdout.fileno()):
try:
from tqdm import tqdm
progress_bar = tqdm
verbose = False
except ImportError:
pass
def xkbcommontool(rmlvo):
try:
r = rmlvo.get('r', 'evdev')
m = rmlvo.get('m', 'pc105')
l = rmlvo.get('l', 'us')
v = rmlvo.get('v', None)
o = rmlvo.get('o', None)
args = [
'xkbcli',
'compile-keymap',
'--rules', r,
'--model', m,
'--layout', l,
]
if v is not None:
args += ['--variant', v]
if o is not None:
args += ['--options', o]
success = True
out = io.StringIO()
if verbose:
print(':: {}'.format(' '.join(args)), file=out)
try:
output = subprocess.check_output(args, stderr=subprocess.STDOUT,
universal_newlines=True)
if verbose:
print(output, file=out)
except subprocess.CalledProcessError as err:
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
print(err.output, file=out)
success = False
return success, out.getvalue()
except KeyboardInterrupt:
pass
def xkbcomp(rmlvo):
try:
r = rmlvo.get('r', 'evdev')
m = rmlvo.get('m', 'pc105')
l = rmlvo.get('l', 'us')
v = rmlvo.get('v', None)
o = rmlvo.get('o', None)
args = ['setxkbmap', '-print']
if r is not None:
args.append('-rules')
args.append('{}'.format(r))
if m is not None:
args.append('-model')
args.append('{}'.format(m))
if l is not None:
args.append('-layout')
args.append('{}'.format(l))
if v is not None:
args.append('-variant')
args.append('{}'.format(v))
if o is not None:
args.append('-option')
args.append('{}'.format(o))
success = True
out = io.StringIO()
if verbose:
print(':: {}'.format(' '.join(args)), file=out)
try:
xkbcomp_args = ['xkbcomp', '-xkb', '-', '-']
setxkbmap = subprocess.Popen(args, stdout=subprocess.PIPE)
xkbcomp = subprocess.Popen(xkbcomp_args, stdin=setxkbmap.stdout,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
setxkbmap.stdout.close()
stdout, stderr = xkbcomp.communicate()
if xkbcomp.returncode != 0:
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
success = False
if xkbcomp.returncode != 0 or verbose:
print(stdout, file=out)
print(stderr, file=out)
# This catches setxkbmap errors.
except subprocess.CalledProcessError as err:
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
print(err.output, file=out)
success = False
return success, out.getvalue()
except KeyboardInterrupt:
pass
def parse(path):
root = ET.fromstring(open(path).read())
layouts = root.findall('layoutList/layout')
options = [
e.text
for e in root.findall('optionList/group/option/configItem/name')
]
combos = []
for l in layouts:
layout = l.find('configItem/name').text
combos.append({'l': layout})
variants = l.findall('variantList/variant')
for v in variants:
variant = v.find('configItem/name').text
combos.append({'l': layout, 'v': variant})
for option in options:
combos.append({'l': layout, 'v': variant, 'o': option})
return combos
def run(combos, tool, njobs):
failed = False
with Pool(njobs) as p:
results = p.imap_unordered(tool, combos)
for success, output in progress_bar(results, total=len(combos)):
if not success:
failed = True
if output:
print(output, file=sys.stdout if success else sys.stderr)
return failed
def main(args):
tools = {
'libxkbcommon': xkbcommontool,
'xkbcomp': xkbcomp,
}
parser = argparse.ArgumentParser(
description='Tool to test all layout/variant/option combinations.'
)
parser.add_argument('path', metavar='/path/to/evdev.xml',
nargs='?', type=str,
default=DEFAULT_RULES_XML,
help='Path to xkeyboard-config\'s evdev.xml')
parser.add_argument('--tool', choices=tools.keys(),
type=str, default='libxkbcommon',
help='parsing tool to use')
parser.add_argument('--jobs', '-j', type=int,
default=os.cpu_count() * 4,
help='number of processes to use')
args = parser.parse_args()
tool = tools[args.tool]
combos = parse(args.path)
failed = run(combos, tool, args.jobs)
sys.exit(failed)
if __name__ == '__main__':
try:
main(sys.argv)
except KeyboardInterrupt:
print('Exiting after Ctrl+C')