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
import argparse
import multiprocessing
import sys
import subprocess
import os
import io
import xml.etree.ElementTree as ET
from multiprocessing import Pool
verbose = False
@ -37,58 +36,40 @@ def create_progress_bar(verbose):
return progress_bar
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', # 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]
class Invocation:
def __init__(self, r, m, l, v, o):
self.command = ""
self.rules = r
self.model = m
self.layout = l
self.variant = v
self.option = o
self.exitstatus = 77 # default to skipped
self.error = None
self.keymap = None # The fully compiled keymap
success = True
out = io.StringIO()
if verbose:
print(':: {}'.format(' '.join(args)), file=out)
@property
def rmlvo(self):
return self.rules, self.model, self.layout, self.variant, self.option
try:
output = subprocess.check_output(args, stderr=subprocess.STDOUT,
universal_newlines=True)
if verbose:
print(output, file=out)
def __str__(self):
s = []
rmlvo = [x or "" for x in self.rmlvo]
rmlvo = ', '.join([f'"{x}"' for x in rmlvo])
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:
for line in output.split('\n'):
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 run(self):
raise NotImplementedError
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)
class XkbCompInvocation(Invocation):
def run(self):
r, m, l, v, o = self.rmlvo
args = ['setxkbmap', '-print']
if r is not None:
args.append('-rules')
@ -106,34 +87,85 @@ def xkbcomp(rmlvo):
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,
self.command = " ".join(args + ["|"] + xkbcomp_args)
setxkbmap = subprocess.Popen(args, stdout=subprocess.PIPE,
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,
universal_newlines=True)
setxkbmap.stdout.close()
stdout, stderr = xkbcomp.communicate()
stdout, stderr = xkbcomp.communicate(stdout)
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)
self.error = "failed to compile keymap"
self.exitstatus = xkbcomp.returncode
else:
self.keymap = stdout
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:
print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
print(err.output, file=out)
success = False
self.error = "failed to compile keymap"
self.exitstatus = err.returncode
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:
pass
@ -165,13 +197,18 @@ def parse(path):
def run(combos, tool, njobs):
failed = False
with Pool(njobs) as p:
with multiprocessing.Pool(njobs) as p:
results = p.imap_unordered(tool, combos)
for success, output in progress_bar(results, total=len(combos)):
if not success:
for invocation in progress_bar(results, total=len(combos)):
if invocation.exitstatus != 0:
failed = True
if output:
print(output, file=sys.stdout if success else sys.stderr)
target = sys.stderr
else:
target = sys.stdout if verbose else None
if target:
print(invocation, file=target)
return failed
@ -214,4 +251,4 @@ if __name__ == '__main__':
try:
main(sys.argv)
except KeyboardInterrupt:
print('Exiting after Ctrl+C')
print('# Exiting after Ctrl+C')