scripts: update makeheader script for the _EVDEVK keysym defines
[platform/upstream/libxkbcommon.git] / test / xkeyboard-config-test.py.in
1 #!/usr/bin/env python3
2 import argparse
3 import sys
4 import subprocess
5 import os
6 import io
7 import xml.etree.ElementTree as ET
8 from multiprocessing import Pool
9
10
11 verbose = True
12
13 DEFAULT_RULES_XML = '@XKB_CONFIG_ROOT@/rules/evdev.xml'
14
15 # Meson needs to fill this in so we can call the tool in the buildir.
16 EXTRA_PATH = '@MESON_BUILD_ROOT@'
17 os.environ['PATH'] = ':'.join([EXTRA_PATH, os.getenv('PATH')])
18
19
20 def noop_progress_bar(x, total):
21     return x
22
23
24 # The function generating the progress bar (if any).
25 progress_bar = noop_progress_bar
26 if os.isatty(sys.stdout.fileno()):
27     try:
28         from tqdm import tqdm
29         progress_bar = tqdm
30
31         verbose = False
32     except ImportError:
33         pass
34
35
36 def xkbcommontool(rmlvo):
37     try:
38         r = rmlvo.get('r', 'evdev')
39         m = rmlvo.get('m', 'pc105')
40         l = rmlvo.get('l', 'us')
41         v = rmlvo.get('v', None)
42         o = rmlvo.get('o', None)
43         args = [
44             'xkbcli-compile-keymap',  # this is run in the builddir
45             '--verbose',
46             '--rules', r,
47             '--model', m,
48             '--layout', l,
49         ]
50         if v is not None:
51             args += ['--variant', v]
52         if o is not None:
53             args += ['--options', o]
54
55         success = True
56         out = io.StringIO()
57         if verbose:
58             print(':: {}'.format(' '.join(args)), file=out)
59
60         try:
61             output = subprocess.check_output(args, stderr=subprocess.STDOUT,
62                                              universal_newlines=True)
63             if verbose:
64                 print(output, file=out)
65
66             if "unrecognized keysym" in output:
67                 for line in output.split('\n'):
68                     if "unrecognized keysym" in line:
69                         print('ERROR: {}'.format(line))
70                 success = False
71         except subprocess.CalledProcessError as err:
72             print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
73             print(err.output, file=out)
74             success = False
75
76         return success, out.getvalue()
77     except KeyboardInterrupt:
78         pass
79
80
81 def xkbcomp(rmlvo):
82     try:
83         r = rmlvo.get('r', 'evdev')
84         m = rmlvo.get('m', 'pc105')
85         l = rmlvo.get('l', 'us')
86         v = rmlvo.get('v', None)
87         o = rmlvo.get('o', None)
88         args = ['setxkbmap', '-print']
89         if r is not None:
90             args.append('-rules')
91             args.append('{}'.format(r))
92         if m is not None:
93             args.append('-model')
94             args.append('{}'.format(m))
95         if l is not None:
96             args.append('-layout')
97             args.append('{}'.format(l))
98         if v is not None:
99             args.append('-variant')
100             args.append('{}'.format(v))
101         if o is not None:
102             args.append('-option')
103             args.append('{}'.format(o))
104
105         success = True
106         out = io.StringIO()
107         if verbose:
108             print(':: {}'.format(' '.join(args)), file=out)
109
110         try:
111             xkbcomp_args = ['xkbcomp', '-xkb', '-', '-']
112
113             setxkbmap = subprocess.Popen(args, stdout=subprocess.PIPE)
114             xkbcomp = subprocess.Popen(xkbcomp_args, stdin=setxkbmap.stdout,
115                                        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
116                                        universal_newlines=True)
117             setxkbmap.stdout.close()
118             stdout, stderr = xkbcomp.communicate()
119             if xkbcomp.returncode != 0:
120                 print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
121                 success = False
122             if xkbcomp.returncode != 0 or verbose:
123                 print(stdout, file=out)
124                 print(stderr, file=out)
125
126         # This catches setxkbmap errors.
127         except subprocess.CalledProcessError as err:
128             print('ERROR: Failed to compile: {}'.format(' '.join(args)), file=out)
129             print(err.output, file=out)
130             success = False
131
132         return success, out.getvalue()
133     except KeyboardInterrupt:
134         pass
135
136
137 def parse(path):
138     root = ET.fromstring(open(path).read())
139     layouts = root.findall('layoutList/layout')
140
141     options = [
142         e.text
143         for e in root.findall('optionList/group/option/configItem/name')
144     ]
145
146     combos = []
147     for l in layouts:
148         layout = l.find('configItem/name').text
149         combos.append({'l': layout})
150
151         variants = l.findall('variantList/variant')
152         for v in variants:
153             variant = v.find('configItem/name').text
154
155             combos.append({'l': layout, 'v': variant})
156             for option in options:
157                 combos.append({'l': layout, 'v': variant, 'o': option})
158
159     return combos
160
161
162 def run(combos, tool, njobs):
163     failed = False
164     with Pool(njobs) as p:
165         results = p.imap_unordered(tool, combos)
166         for success, output in progress_bar(results, total=len(combos)):
167             if not success:
168                 failed = True
169             if output:
170                 print(output, file=sys.stdout if success else sys.stderr)
171     return failed
172
173
174 def main(args):
175     tools = {
176         'libxkbcommon': xkbcommontool,
177         'xkbcomp': xkbcomp,
178     }
179
180     parser = argparse.ArgumentParser(
181         description='Tool to test all layout/variant/option combinations.'
182     )
183     parser.add_argument('path', metavar='/path/to/evdev.xml',
184                         nargs='?', type=str,
185                         default=DEFAULT_RULES_XML,
186                         help='Path to xkeyboard-config\'s evdev.xml')
187     parser.add_argument('--tool', choices=tools.keys(),
188                         type=str, default='libxkbcommon',
189                         help='parsing tool to use')
190     parser.add_argument('--jobs', '-j', type=int,
191                         default=os.cpu_count() * 4,
192                         help='number of processes to use')
193     args = parser.parse_args()
194
195     tool = tools[args.tool]
196
197     combos = parse(args.path)
198     failed = run(combos, tool, args.jobs)
199     sys.exit(failed)
200
201
202 if __name__ == '__main__':
203     try:
204         main(sys.argv)
205     except KeyboardInterrupt:
206         print('Exiting after Ctrl+C')