test: rework the output for the xkeyboard-config layout tester
The previous output is largely unusable. The result in the CI test runs is a 6GB file with every compiled keymap in it and while we can grep for ERROR, it's not particularly useful. Let's change this and print out YAML instead - that can be machine-processed. This patch adds a new parent class that prints itself in YAML format, the tool invocations are child classes of that class. The result looks like this: Example output: - rmlvo: ["evdev", "pc105", "us", "haw", "grp:rwin_switch"] cmd: "xkbcli-compile-keymap --verbose --rules evdev --model pc105 --layout us --variant haw --options grp:rwin_switch" status: 0 - rmlvo: ["evdev", "pc105", "us", "foo", ""] cmd: "xkbcli-compile-keymap --verbose --rules evdev --model pc105 --layout us --variant foo" status: 1 error: "failed to compile keymap" Special status codes are: 99 for "unrecognized keysym" and 90 for "Cannot open display" in the setxkbmap case. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>master
parent
44e8d4b044
commit
1cae250052
|
@ -1,11 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
import multiprocessing
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import io
|
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from multiprocessing import Pool
|
|
||||||
|
|
||||||
|
|
||||||
verbose = False
|
verbose = False
|
||||||
|
@ -37,58 +36,40 @@ def create_progress_bar(verbose):
|
||||||
return progress_bar
|
return progress_bar
|
||||||
|
|
||||||
|
|
||||||
def xkbcommontool(rmlvo):
|
class Invocation:
|
||||||
try:
|
def __init__(self, r, m, l, v, o):
|
||||||
r = rmlvo.get('r', 'evdev')
|
self.command = ""
|
||||||
m = rmlvo.get('m', 'pc105')
|
self.rules = r
|
||||||
l = rmlvo.get('l', 'us')
|
self.model = m
|
||||||
v = rmlvo.get('v', None)
|
self.layout = l
|
||||||
o = rmlvo.get('o', None)
|
self.variant = v
|
||||||
args = [
|
self.option = o
|
||||||
'xkbcli-compile-keymap', # this is run in the builddir
|
self.exitstatus = 77 # default to skipped
|
||||||
'--verbose',
|
self.error = None
|
||||||
'--rules', r,
|
self.keymap = None # The fully compiled keymap
|
||||||
'--model', m,
|
|
||||||
'--layout', l,
|
|
||||||
]
|
|
||||||
if v is not None:
|
|
||||||
args += ['--variant', v]
|
|
||||||
if o is not None:
|
|
||||||
args += ['--options', o]
|
|
||||||
|
|
||||||
success = True
|
@property
|
||||||
out = io.StringIO()
|
def rmlvo(self):
|
||||||
if verbose:
|
return self.rules, self.model, self.layout, self.variant, self.option
|
||||||
print(':: {}'.format(' '.join(args)), file=out)
|
|
||||||
|
|
||||||
try:
|
def __str__(self):
|
||||||
output = subprocess.check_output(args, stderr=subprocess.STDOUT,
|
s = []
|
||||||
universal_newlines=True)
|
rmlvo = [x or "" for x in self.rmlvo]
|
||||||
if verbose:
|
rmlvo = ', '.join([f'"{x}"' for x in rmlvo])
|
||||||
print(output, file=out)
|
s.append(f'- rmlvo: [{rmlvo}]')
|
||||||
|
s.append(f' cmd: "{escape(self.command)}"')
|
||||||
|
s.append(f' status: {self.exitstatus}')
|
||||||
|
if self.error:
|
||||||
|
s.append(f' error: "{escape(self.error.strip())}"')
|
||||||
|
return '\n'.join(s)
|
||||||
|
|
||||||
if "unrecognized keysym" in output:
|
def run(self):
|
||||||
for line in output.split('\n'):
|
raise NotImplementedError
|
||||||
if "unrecognized keysym" in line:
|
|
||||||
print('ERROR: {}'.format(line))
|
|
||||||
success = False
|
|
||||||
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):
|
class XkbCompInvocation(Invocation):
|
||||||
try:
|
def run(self):
|
||||||
r = rmlvo.get('r', 'evdev')
|
r, m, l, v, o = self.rmlvo
|
||||||
m = rmlvo.get('m', 'pc105')
|
|
||||||
l = rmlvo.get('l', 'us')
|
|
||||||
v = rmlvo.get('v', None)
|
|
||||||
o = rmlvo.get('o', None)
|
|
||||||
args = ['setxkbmap', '-print']
|
args = ['setxkbmap', '-print']
|
||||||
if r is not None:
|
if r is not None:
|
||||||
args.append('-rules')
|
args.append('-rules')
|
||||||
|
@ -106,34 +87,85 @@ def xkbcomp(rmlvo):
|
||||||
args.append('-option')
|
args.append('-option')
|
||||||
args.append('{}'.format(o))
|
args.append('{}'.format(o))
|
||||||
|
|
||||||
success = True
|
xkbcomp_args = ['xkbcomp', '-xkb', '-', '-']
|
||||||
out = io.StringIO()
|
|
||||||
if verbose:
|
|
||||||
print(':: {}'.format(' '.join(args)), file=out)
|
|
||||||
|
|
||||||
try:
|
self.command = " ".join(args + ["|"] + xkbcomp_args)
|
||||||
xkbcomp_args = ['xkbcomp', '-xkb', '-', '-']
|
|
||||||
|
|
||||||
setxkbmap = subprocess.Popen(args, stdout=subprocess.PIPE)
|
setxkbmap = subprocess.Popen(args, stdout=subprocess.PIPE,
|
||||||
xkbcomp = subprocess.Popen(xkbcomp_args, stdin=setxkbmap.stdout,
|
stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
|
stdout, stderr = setxkbmap.communicate()
|
||||||
|
if "Cannot open display" in stderr:
|
||||||
|
self.error = stderr
|
||||||
|
self.exitstatus = 90
|
||||||
|
else:
|
||||||
|
xkbcomp = subprocess.Popen(xkbcomp_args, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
setxkbmap.stdout.close()
|
stdout, stderr = xkbcomp.communicate(stdout)
|
||||||
stdout, stderr = xkbcomp.communicate()
|
|
||||||
if xkbcomp.returncode != 0:
|
if xkbcomp.returncode != 0:
|
||||||
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
|
self.error = "failed to compile keymap"
|
||||||
success = False
|
self.exitstatus = xkbcomp.returncode
|
||||||
if xkbcomp.returncode != 0 or verbose:
|
else:
|
||||||
print(stdout, file=out)
|
self.keymap = stdout
|
||||||
print(stderr, file=out)
|
self.exitstatus = 0
|
||||||
|
|
||||||
# This catches setxkbmap errors.
|
|
||||||
|
class XkbcommonInvocation(Invocation):
|
||||||
|
def run(self):
|
||||||
|
r, m, l, v, o = self.rmlvo
|
||||||
|
args = [
|
||||||
|
'xkbcli-compile-keymap', # this is run in the builddir
|
||||||
|
'--verbose',
|
||||||
|
'--rules', r,
|
||||||
|
'--model', m,
|
||||||
|
'--layout', l,
|
||||||
|
]
|
||||||
|
if v is not None:
|
||||||
|
args += ['--variant', v]
|
||||||
|
if o is not None:
|
||||||
|
args += ['--options', o]
|
||||||
|
|
||||||
|
self.command = " ".join(args)
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(args, stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
if "unrecognized keysym" in output:
|
||||||
|
for line in output.split('\n'):
|
||||||
|
if "unrecognized keysym" in line:
|
||||||
|
self.error = line
|
||||||
|
self.exitstatus = 99 # tool doesn't generate this one
|
||||||
|
else:
|
||||||
|
self.exitstatus = 0
|
||||||
|
self.keymap = output
|
||||||
except subprocess.CalledProcessError as err:
|
except subprocess.CalledProcessError as err:
|
||||||
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
|
self.error = "failed to compile keymap"
|
||||||
print(err.output, file=out)
|
self.exitstatus = err.returncode
|
||||||
success = False
|
|
||||||
|
|
||||||
return success, out.getvalue()
|
|
||||||
|
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)
|
||||||
|
tool = XkbcommonInvocation(r, m, l, v, o)
|
||||||
|
tool.run()
|
||||||
|
return tool
|
||||||
|
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)
|
||||||
|
tool = XkbCompInvocation(r, m, l, v, o)
|
||||||
|
tool.run()
|
||||||
|
return tool
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -165,13 +197,18 @@ def parse(path):
|
||||||
|
|
||||||
def run(combos, tool, njobs):
|
def run(combos, tool, njobs):
|
||||||
failed = False
|
failed = False
|
||||||
with Pool(njobs) as p:
|
with multiprocessing.Pool(njobs) as p:
|
||||||
results = p.imap_unordered(tool, combos)
|
results = p.imap_unordered(tool, combos)
|
||||||
for success, output in progress_bar(results, total=len(combos)):
|
for invocation in progress_bar(results, total=len(combos)):
|
||||||
if not success:
|
if invocation.exitstatus != 0:
|
||||||
failed = True
|
failed = True
|
||||||
if output:
|
target = sys.stderr
|
||||||
print(output, file=sys.stdout if success else sys.stderr)
|
else:
|
||||||
|
target = sys.stdout if verbose else None
|
||||||
|
|
||||||
|
if target:
|
||||||
|
print(invocation, file=target)
|
||||||
|
|
||||||
return failed
|
return failed
|
||||||
|
|
||||||
|
|
||||||
|
@ -214,4 +251,4 @@ if __name__ == '__main__':
|
||||||
try:
|
try:
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('Exiting after Ctrl+C')
|
print('# Exiting after Ctrl+C')
|
||||||
|
|
Loading…
Reference in New Issue