]> git.proxmox.com Git - mirror_qemu.git/blob - tests/qapi-schema/test-qapi.py
Merge remote-tracking branch 'remotes/cleber/tags/python-next-pull-request' into...
[mirror_qemu.git] / tests / qapi-schema / test-qapi.py
1 #!/usr/bin/env python3
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
14
15 import argparse
16 import difflib
17 import os
18 import sys
19 from io import StringIO
20
21 from qapi.error import QAPIError
22 from qapi.schema import QAPISchema, QAPISchemaVisitor
23
24
25 class QAPISchemaTestVisitor(QAPISchemaVisitor):
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
33 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
34 print('enum %s' % name)
35 if prefix:
36 print(' prefix %s' % prefix)
37 for m in members:
38 print(' member %s' % m.name)
39 self._print_if(m.ifcond, indent=8)
40 self._print_if(ifcond)
41 self._print_features(features)
42
43 def visit_array_type(self, name, info, ifcond, element_type):
44 if not info:
45 return # suppress built-in arrays
46 print('array %s %s' % (name, element_type.name))
47 self._print_if(ifcond)
48
49 def visit_object_type(self, name, info, ifcond, features,
50 base, members, variants):
51 print('object %s' % name)
52 if base:
53 print(' base %s' % base.name)
54 for m in members:
55 print(' member %s: %s optional=%s'
56 % (m.name, m.type.name, m.optional))
57 self._print_if(m.ifcond, 8)
58 self._print_features(m.features, indent=8)
59 self._print_variants(variants)
60 self._print_if(ifcond)
61 self._print_features(features)
62
63 def visit_alternate_type(self, name, info, ifcond, features, variants):
64 print('alternate %s' % name)
65 self._print_variants(variants)
66 self._print_if(ifcond)
67 self._print_features(features)
68
69 def visit_command(self, name, info, ifcond, features,
70 arg_type, ret_type, gen, success_response, boxed,
71 allow_oob, allow_preconfig):
72 print('command %s %s -> %s'
73 % (name, arg_type and arg_type.name,
74 ret_type and ret_type.name))
75 print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s'
76 % (gen, success_response, boxed, allow_oob, allow_preconfig))
77 self._print_if(ifcond)
78 self._print_features(features)
79
80 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
81 print('event %s %s' % (name, arg_type and arg_type.name))
82 print(' boxed=%s' % boxed)
83 self._print_if(ifcond)
84 self._print_features(features)
85
86 @staticmethod
87 def _print_variants(variants):
88 if variants:
89 print(' tag %s' % variants.tag_member.name)
90 for v in variants.variants:
91 print(' case %s: %s' % (v.name, v.type.name))
92 QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
93
94 @staticmethod
95 def _print_if(ifcond, indent=4):
96 if ifcond:
97 print('%sif %s' % (' ' * indent, ifcond))
98
99 @classmethod
100 def _print_features(cls, features, indent=4):
101 if features:
102 for f in features:
103 print('%sfeature %s' % (' ' * indent, f.name))
104 cls._print_if(f.ifcond, indent + 4)
105
106
107 def test_frontend(fname):
108 schema = QAPISchema(fname)
109 schema.visit(QAPISchemaTestVisitor())
110
111 for doc in schema.docs:
112 if doc.symbol:
113 print('doc symbol=%s' % doc.symbol)
114 else:
115 print('doc freeform')
116 print(' body=\n%s' % doc.body.text)
117 for arg, section in doc.args.items():
118 print(' arg=%s\n%s' % (arg, section.text))
119 for feat, section in doc.features.items():
120 print(' feature=%s\n%s' % (feat, section.text))
121 for section in doc.sections:
122 print(' section=%s\n%s' % (section.name, section.text))
123
124
125 def test_and_diff(test_name, dir_name, update):
126 sys.stdout = StringIO()
127 try:
128 test_frontend(os.path.join(dir_name, test_name + '.json'))
129 except QAPIError as err:
130 if err.info.fname is None:
131 print("%s" % err, file=sys.stderr)
132 return 2
133 errstr = str(err) + '\n'
134 if dir_name:
135 errstr = errstr.replace(dir_name + '/', '')
136 actual_err = errstr.splitlines(True)
137 else:
138 actual_err = []
139 finally:
140 actual_out = sys.stdout.getvalue().splitlines(True)
141 sys.stdout.close()
142 sys.stdout = sys.__stdout__
143
144 mode = 'r+' if update else 'r'
145 try:
146 outfp = open(os.path.join(dir_name, test_name + '.out'), mode)
147 errfp = open(os.path.join(dir_name, test_name + '.err'), mode)
148 expected_out = outfp.readlines()
149 expected_err = errfp.readlines()
150 except IOError as err:
151 print("%s: can't open '%s': %s"
152 % (sys.argv[0], err.filename, err.strerror),
153 file=sys.stderr)
154 return 2
155
156 if actual_out == expected_out and actual_err == expected_err:
157 return 0
158
159 print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'),
160 file=sys.stderr)
161 out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name)
162 err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name)
163 sys.stdout.writelines(out_diff)
164 sys.stdout.writelines(err_diff)
165
166 if not update:
167 return 1
168
169 try:
170 outfp.truncate(0)
171 outfp.seek(0)
172 outfp.writelines(actual_out)
173 errfp.truncate(0)
174 errfp.seek(0)
175 errfp.writelines(actual_err)
176 except IOError as err:
177 print("%s: can't write '%s': %s"
178 % (sys.argv[0], err.filename, err.strerror),
179 file=sys.stderr)
180 return 2
181
182 return 0
183
184
185 def main(argv):
186 parser = argparse.ArgumentParser(
187 description='QAPI schema tester')
188 parser.add_argument('-d', '--dir', action='store', default='',
189 help="directory containing tests")
190 parser.add_argument('-u', '--update', action='store_true',
191 help="update expected test results")
192 parser.add_argument('tests', nargs='*', metavar='TEST', action='store')
193 args = parser.parse_args()
194
195 status = 0
196 for t in args.tests:
197 (dir_name, base_name) = os.path.split(t)
198 dir_name = dir_name or args.dir
199 test_name = os.path.splitext(base_name)[0]
200 status |= test_and_diff(test_name, dir_name, args.update)
201
202 exit(status)
203
204
205 if __name__ == '__main__':
206 main(sys.argv)
207 exit(0)