]> git.proxmox.com Git - mirror_qemu.git/blame - tests/qapi-schema/test-qapi.py
linux-user/hppa: Increase guest stack size to 80MB for hppa target
[mirror_qemu.git] / tests / qapi-schema / test-qapi.py
CommitLineData
c88ee46c 1#!/usr/bin/env python3
98626572
MA
2#
3# QAPI parser test harness
4#
5# Copyright (c) 2013 Red Hat Inc.
6#
7# Authors:
8# Markus Armbruster <armbru@redhat.com>
9#
10# This work is licensed under the terms of the GNU GPL, version 2 or later.
11# See the COPYING file in the top-level directory.
12#
13
e6c42b96 14
f01338cc
MA
15import argparse
16import difflib
17import os
98626572 18import sys
ed39c03e 19from io import StringIO
e6c42b96
MA
20
21from qapi.error import QAPIError
22from qapi.schema import QAPISchema, QAPISchemaVisitor
23
156402e5
MA
24
25class QAPISchemaTestVisitor(QAPISchemaVisitor):
cf40a0a5
MA
26
27 def visit_module(self, name):
28 print('module %s' % name)
29
30 def visit_include(self, name, info):
31 print('include %s' % name)
32
013b4efc 33 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
1e381b65 34 print('enum %s' % name)
156402e5 35 if prefix:
ef9d9108 36 print(' prefix %s' % prefix)
1e381b65
MAL
37 for m in members:
38 print(' member %s' % m.name)
6cc32b0e 39 self._print_if(m.ifcond, indent=8)
b6c18755 40 self._print_features(m.features, indent=8)
fbf09a2f 41 self._print_if(ifcond)
013b4efc 42 self._print_features(features)
156402e5 43
ca0ac758
MA
44 def visit_array_type(self, name, info, ifcond, element_type):
45 if not info:
46 return # suppress built-in arrays
47 print('array %s %s' % (name, element_type.name))
48 self._print_if(ifcond)
49
7b3bc9e2
MA
50 def visit_object_type(self, name, info, ifcond, features,
51 base, members, variants):
ef9d9108 52 print('object %s' % name)
156402e5 53 if base:
ef9d9108 54 print(' base %s' % base.name)
156402e5 55 for m in members:
b736e25a
MA
56 print(' member %s: %s optional=%s'
57 % (m.name, m.type.name, m.optional))
ccadd6bc 58 self._print_if(m.ifcond, 8)
84ab0086 59 self._print_features(m.features, indent=8)
156402e5 60 self._print_variants(variants)
fbf09a2f 61 self._print_if(ifcond)
2e2e0df2 62 self._print_features(features)
156402e5 63
013b4efc 64 def visit_alternate_type(self, name, info, ifcond, features, variants):
ef9d9108 65 print('alternate %s' % name)
156402e5 66 self._print_variants(variants)
fbf09a2f 67 self._print_if(ifcond)
013b4efc 68 self._print_features(features)
156402e5 69
7b3bc9e2
MA
70 def visit_command(self, name, info, ifcond, features,
71 arg_type, ret_type, gen, success_response, boxed,
04f22362 72 allow_oob, allow_preconfig, coroutine):
b736e25a
MA
73 print('command %s %s -> %s'
74 % (name, arg_type and arg_type.name,
75 ret_type and ret_type.name))
04f22362
KW
76 print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s%s'
77 % (gen, success_response, boxed, allow_oob, allow_preconfig,
78 " coroutine=True" if coroutine else ""))
fbf09a2f 79 self._print_if(ifcond)
2e2e0df2 80 self._print_features(features)
156402e5 81
013b4efc 82 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
ef9d9108 83 print('event %s %s' % (name, arg_type and arg_type.name))
758f272b 84 print(' boxed=%s' % boxed)
fbf09a2f 85 self._print_if(ifcond)
013b4efc 86 self._print_features(features)
156402e5
MA
87
88 @staticmethod
89 def _print_variants(variants):
90 if variants:
ef9d9108 91 print(' tag %s' % variants.tag_member.name)
156402e5 92 for v in variants.variants:
ef9d9108 93 print(' case %s: %s' % (v.name, v.type.name))
a2724280 94 QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
156402e5 95
fbf09a2f
MAL
96 @staticmethod
97 def _print_if(ifcond, indent=4):
9c629fa8
MA
98 # TODO Drop this hack after replacing OrderedDict by plain
99 # dict (requires Python 3.7)
100 def _massage(subcond):
101 if isinstance(subcond, str):
102 return subcond
103 if isinstance(subcond, list):
104 return [_massage(val) for val in subcond]
105 return {key: _massage(val) for key, val in subcond.items()}
106
33aa3267 107 if ifcond.is_present():
9c629fa8 108 print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond)))
fbf09a2f 109
2e2e0df2 110 @classmethod
84ab0086 111 def _print_features(cls, features, indent=4):
2e2e0df2
PK
112 if features:
113 for f in features:
84ab0086
MA
114 print('%sfeature %s' % (' ' * indent, f.name))
115 cls._print_if(f.ifcond, indent + 4)
2e2e0df2 116
181feaf3 117
f01338cc
MA
118def test_frontend(fname):
119 schema = QAPISchema(fname)
120 schema.visit(QAPISchemaTestVisitor())
121
122 for doc in schema.docs:
123 if doc.symbol:
124 print('doc symbol=%s' % doc.symbol)
125 else:
126 print('doc freeform')
127 print(' body=\n%s' % doc.body.text)
128 for arg, section in doc.args.items():
129 print(' arg=%s\n%s' % (arg, section.text))
a0418a4a
MA
130 for feat, section in doc.features.items():
131 print(' feature=%s\n%s' % (feat, section.text))
f01338cc
MA
132 for section in doc.sections:
133 print(' section=%s\n%s' % (section.name, section.text))
134
135
f333681c
MA
136def open_test_result(dir_name, file_name, update):
137 mode = 'r+' if update else 'r'
138 try:
139 fp = open(os.path.join(dir_name, file_name), mode)
140 except FileNotFoundError:
141 if not update:
142 raise
143 fp = open(os.path.join(dir_name, file_name), 'w+')
144 return fp
145
146
f01338cc
MA
147def test_and_diff(test_name, dir_name, update):
148 sys.stdout = StringIO()
149 try:
150 test_frontend(os.path.join(dir_name, test_name + '.json'))
151 except QAPIError as err:
f01338cc
MA
152 errstr = str(err) + '\n'
153 if dir_name:
154 errstr = errstr.replace(dir_name + '/', '')
155 actual_err = errstr.splitlines(True)
818c3318 156 else:
f01338cc
MA
157 actual_err = []
158 finally:
159 actual_out = sys.stdout.getvalue().splitlines(True)
160 sys.stdout.close()
161 sys.stdout = sys.__stdout__
162
f01338cc 163 try:
f333681c
MA
164 outfp = open_test_result(dir_name, test_name + '.out', update)
165 errfp = open_test_result(dir_name, test_name + '.err', update)
f01338cc
MA
166 expected_out = outfp.readlines()
167 expected_err = errfp.readlines()
436911c2 168 except OSError as err:
f01338cc
MA
169 print("%s: can't open '%s': %s"
170 % (sys.argv[0], err.filename, err.strerror),
171 file=sys.stderr)
172 return 2
173
174 if actual_out == expected_out and actual_err == expected_err:
175 return 0
176
177 print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'),
178 file=sys.stderr)
179 out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name)
180 err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name)
181 sys.stdout.writelines(out_diff)
182 sys.stdout.writelines(err_diff)
183
184 if not update:
185 return 1
186
187 try:
188 outfp.truncate(0)
189 outfp.seek(0)
190 outfp.writelines(actual_out)
191 errfp.truncate(0)
192 errfp.seek(0)
193 errfp.writelines(actual_err)
436911c2 194 except OSError as err:
f01338cc
MA
195 print("%s: can't write '%s': %s"
196 % (sys.argv[0], err.filename, err.strerror),
197 file=sys.stderr)
198 return 2
199
200 return 0
201
202
203def main(argv):
204 parser = argparse.ArgumentParser(
205 description='QAPI schema tester')
206 parser.add_argument('-d', '--dir', action='store', default='',
207 help="directory containing tests")
208 parser.add_argument('-u', '--update', action='store_true',
209 help="update expected test results")
210 parser.add_argument('tests', nargs='*', metavar='TEST', action='store')
211 args = parser.parse_args()
212
213 status = 0
214 for t in args.tests:
215 (dir_name, base_name) = os.path.split(t)
216 dir_name = dir_name or args.dir
217 test_name = os.path.splitext(base_name)[0]
218 status |= test_and_diff(test_name, dir_name, args.update)
219
220 exit(status)
221
222
223if __name__ == '__main__':
224 main(sys.argv)
225 exit(0)