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
Peter Hutterer 2021-04-15 10:39:05 +10:00 committed by Ran Benita
parent 44e8d4b044
commit 1cae250052
1 changed files with 112 additions and 75 deletions

View File

@ -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')