xref: /qemu/tests/qapi-schema/test-qapi.py (revision f01338cce692ac54109f09bc6c7b5567611e2d24)
1*f01338ccSMarkus Armbruster#!/usr/bin/env python
298626572SMarkus Armbruster#
398626572SMarkus Armbruster# QAPI parser test harness
498626572SMarkus Armbruster#
598626572SMarkus Armbruster# Copyright (c) 2013 Red Hat Inc.
698626572SMarkus Armbruster#
798626572SMarkus Armbruster# Authors:
898626572SMarkus Armbruster#  Markus Armbruster <armbru@redhat.com>
998626572SMarkus Armbruster#
1098626572SMarkus Armbruster# This work is licensed under the terms of the GNU GPL, version 2 or later.
1198626572SMarkus Armbruster# See the COPYING file in the top-level directory.
1298626572SMarkus Armbruster#
1398626572SMarkus Armbruster
14ef9d9108SDaniel P. Berrangefrom __future__ import print_function
15*f01338ccSMarkus Armbrusterimport argparse
16*f01338ccSMarkus Armbrusterimport difflib
17*f01338ccSMarkus Armbrusterimport os
1898626572SMarkus Armbrusterimport sys
19*f01338ccSMarkus Armbrusterif sys.version_info[0] < 3:
20*f01338ccSMarkus Armbruster    from cStringIO import StringIO
21*f01338ccSMarkus Armbrusterelse:
22*f01338ccSMarkus Armbruster    from io import StringIO
23181feaf3SMarkus Armbrusterfrom qapi.common import QAPIError, QAPISchema, QAPISchemaVisitor
2498626572SMarkus Armbruster
2598626572SMarkus Armbruster
26156402e5SMarkus Armbrusterclass QAPISchemaTestVisitor(QAPISchemaVisitor):
27cf40a0a5SMarkus Armbruster
28cf40a0a5SMarkus Armbruster    def visit_module(self, name):
29cf40a0a5SMarkus Armbruster        print('module %s' % name)
30cf40a0a5SMarkus Armbruster
31cf40a0a5SMarkus Armbruster    def visit_include(self, name, info):
32cf40a0a5SMarkus Armbruster        print('include %s' % name)
33cf40a0a5SMarkus Armbruster
341962bd39SMarc-André Lureau    def visit_enum_type(self, name, info, ifcond, members, prefix):
351e381b65SMarc-André Lureau        print('enum %s' % name)
36156402e5SMarkus Armbruster        if prefix:
37ef9d9108SDaniel P. Berrange            print('    prefix %s' % prefix)
381e381b65SMarc-André Lureau        for m in members:
391e381b65SMarc-André Lureau            print('    member %s' % m.name)
406cc32b0eSMarc-André Lureau            self._print_if(m.ifcond, indent=8)
41fbf09a2fSMarc-André Lureau        self._print_if(ifcond)
42156402e5SMarkus Armbruster
43ca0ac758SMarkus Armbruster    def visit_array_type(self, name, info, ifcond, element_type):
44ca0ac758SMarkus Armbruster        if not info:
45ca0ac758SMarkus Armbruster            return              # suppress built-in arrays
46ca0ac758SMarkus Armbruster        print('array %s %s' % (name, element_type.name))
47ca0ac758SMarkus Armbruster        self._print_if(ifcond)
48ca0ac758SMarkus Armbruster
496a8c0b51SKevin Wolf    def visit_object_type(self, name, info, ifcond, base, members, variants,
506a8c0b51SKevin Wolf                          features):
51ef9d9108SDaniel P. Berrange        print('object %s' % name)
52156402e5SMarkus Armbruster        if base:
53ef9d9108SDaniel P. Berrange            print('    base %s' % base.name)
54156402e5SMarkus Armbruster        for m in members:
55b736e25aSMarkus Armbruster            print('    member %s: %s optional=%s'
56b736e25aSMarkus Armbruster                  % (m.name, m.type.name, m.optional))
57ccadd6bcSMarc-André Lureau            self._print_if(m.ifcond, 8)
58156402e5SMarkus Armbruster        self._print_variants(variants)
59fbf09a2fSMarc-André Lureau        self._print_if(ifcond)
608aa3a33eSKevin Wolf        if features:
618aa3a33eSKevin Wolf            for f in features:
628aa3a33eSKevin Wolf                print('    feature %s' % f.name)
638aa3a33eSKevin Wolf                self._print_if(f.ifcond, 8)
64156402e5SMarkus Armbruster
65fbf09a2fSMarc-André Lureau    def visit_alternate_type(self, name, info, ifcond, variants):
66ef9d9108SDaniel P. Berrange        print('alternate %s' % name)
67156402e5SMarkus Armbruster        self._print_variants(variants)
68fbf09a2fSMarc-André Lureau        self._print_if(ifcond)
69156402e5SMarkus Armbruster
70fbf09a2fSMarc-André Lureau    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
71d6fe3d02SIgor Mammedov                      success_response, boxed, allow_oob, allow_preconfig):
72b736e25aSMarkus Armbruster        print('command %s %s -> %s'
73b736e25aSMarkus Armbruster              % (name, arg_type and arg_type.name,
74b736e25aSMarkus Armbruster                 ret_type and ret_type.name))
75b736e25aSMarkus Armbruster        print('   gen=%s success_response=%s boxed=%s oob=%s preconfig=%s'
76b736e25aSMarkus Armbruster              % (gen, success_response, boxed, allow_oob, allow_preconfig))
77fbf09a2fSMarc-André Lureau        self._print_if(ifcond)
78156402e5SMarkus Armbruster
79fbf09a2fSMarc-André Lureau    def visit_event(self, name, info, ifcond, arg_type, boxed):
80ef9d9108SDaniel P. Berrange        print('event %s %s' % (name, arg_type and arg_type.name))
81ef9d9108SDaniel P. Berrange        print('   boxed=%s' % boxed)
82fbf09a2fSMarc-André Lureau        self._print_if(ifcond)
83156402e5SMarkus Armbruster
84156402e5SMarkus Armbruster    @staticmethod
85156402e5SMarkus Armbruster    def _print_variants(variants):
86156402e5SMarkus Armbruster        if variants:
87ef9d9108SDaniel P. Berrange            print('    tag %s' % variants.tag_member.name)
88156402e5SMarkus Armbruster            for v in variants.variants:
89ef9d9108SDaniel P. Berrange                print('    case %s: %s' % (v.name, v.type.name))
90a2724280SMarc-André Lureau                QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
91156402e5SMarkus Armbruster
92fbf09a2fSMarc-André Lureau    @staticmethod
93fbf09a2fSMarc-André Lureau    def _print_if(ifcond, indent=4):
94fbf09a2fSMarc-André Lureau        if ifcond:
95fbf09a2fSMarc-André Lureau            print('%sif %s' % (' ' * indent, ifcond))
96fbf09a2fSMarc-André Lureau
97181feaf3SMarkus Armbruster
98*f01338ccSMarkus Armbrusterdef test_frontend(fname):
99*f01338ccSMarkus Armbruster    schema = QAPISchema(fname)
100156402e5SMarkus Armbruster    schema.visit(QAPISchemaTestVisitor())
101818c3318SMarkus Armbruster
102818c3318SMarkus Armbruster    for doc in schema.docs:
103818c3318SMarkus Armbruster        if doc.symbol:
104ef9d9108SDaniel P. Berrange            print('doc symbol=%s' % doc.symbol)
105818c3318SMarkus Armbruster        else:
106ef9d9108SDaniel P. Berrange            print('doc freeform')
107ef9d9108SDaniel P. Berrange        print('    body=\n%s' % doc.body.text)
1082f848044SDaniel P. Berrange        for arg, section in doc.args.items():
109ef9d9108SDaniel P. Berrange            print('    arg=%s\n%s' % (arg, section.text))
110818c3318SMarkus Armbruster        for section in doc.sections:
111ef9d9108SDaniel P. Berrange            print('    section=%s\n%s' % (section.name, section.text))
112*f01338ccSMarkus Armbruster
113*f01338ccSMarkus Armbruster
114*f01338ccSMarkus Armbrusterdef test_and_diff(test_name, dir_name, update):
115*f01338ccSMarkus Armbruster    sys.stdout = StringIO()
116*f01338ccSMarkus Armbruster    try:
117*f01338ccSMarkus Armbruster        test_frontend(os.path.join(dir_name, test_name + '.json'))
118*f01338ccSMarkus Armbruster    except QAPIError as err:
119*f01338ccSMarkus Armbruster        if err.info.fname is None:
120*f01338ccSMarkus Armbruster            print("%s" % err, file=sys.stderr)
121*f01338ccSMarkus Armbruster            return 2
122*f01338ccSMarkus Armbruster        errstr = str(err) + '\n'
123*f01338ccSMarkus Armbruster        if dir_name:
124*f01338ccSMarkus Armbruster            errstr = errstr.replace(dir_name + '/', '')
125*f01338ccSMarkus Armbruster        actual_err = errstr.splitlines(True)
126*f01338ccSMarkus Armbruster    else:
127*f01338ccSMarkus Armbruster        actual_err = []
128*f01338ccSMarkus Armbruster    finally:
129*f01338ccSMarkus Armbruster        actual_out = sys.stdout.getvalue().splitlines(True)
130*f01338ccSMarkus Armbruster        sys.stdout.close()
131*f01338ccSMarkus Armbruster        sys.stdout = sys.__stdout__
132*f01338ccSMarkus Armbruster
133*f01338ccSMarkus Armbruster    mode = 'r+' if update else 'r'
134*f01338ccSMarkus Armbruster    try:
135*f01338ccSMarkus Armbruster        outfp = open(os.path.join(dir_name, test_name + '.out'), mode)
136*f01338ccSMarkus Armbruster        errfp = open(os.path.join(dir_name, test_name + '.err'), mode)
137*f01338ccSMarkus Armbruster        expected_out = outfp.readlines()
138*f01338ccSMarkus Armbruster        expected_err = errfp.readlines()
139*f01338ccSMarkus Armbruster    except IOError as err:
140*f01338ccSMarkus Armbruster        print("%s: can't open '%s': %s"
141*f01338ccSMarkus Armbruster              % (sys.argv[0], err.filename, err.strerror),
142*f01338ccSMarkus Armbruster              file=sys.stderr)
143*f01338ccSMarkus Armbruster        return 2
144*f01338ccSMarkus Armbruster
145*f01338ccSMarkus Armbruster    if actual_out == expected_out and actual_err == expected_err:
146*f01338ccSMarkus Armbruster        return 0
147*f01338ccSMarkus Armbruster
148*f01338ccSMarkus Armbruster    print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'),
149*f01338ccSMarkus Armbruster          file=sys.stderr)
150*f01338ccSMarkus Armbruster    out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name)
151*f01338ccSMarkus Armbruster    err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name)
152*f01338ccSMarkus Armbruster    sys.stdout.writelines(out_diff)
153*f01338ccSMarkus Armbruster    sys.stdout.writelines(err_diff)
154*f01338ccSMarkus Armbruster
155*f01338ccSMarkus Armbruster    if not update:
156*f01338ccSMarkus Armbruster        return 1
157*f01338ccSMarkus Armbruster
158*f01338ccSMarkus Armbruster    try:
159*f01338ccSMarkus Armbruster        outfp.truncate(0)
160*f01338ccSMarkus Armbruster        outfp.seek(0)
161*f01338ccSMarkus Armbruster        outfp.writelines(actual_out)
162*f01338ccSMarkus Armbruster        errfp.truncate(0)
163*f01338ccSMarkus Armbruster        errfp.seek(0)
164*f01338ccSMarkus Armbruster        errfp.writelines(actual_err)
165*f01338ccSMarkus Armbruster    except IOError as err:
166*f01338ccSMarkus Armbruster        print("%s: can't write '%s': %s"
167*f01338ccSMarkus Armbruster              % (sys.argv[0], err.filename, err.strerror),
168*f01338ccSMarkus Armbruster              file=sys.stderr)
169*f01338ccSMarkus Armbruster        return 2
170*f01338ccSMarkus Armbruster
171*f01338ccSMarkus Armbruster    return 0
172*f01338ccSMarkus Armbruster
173*f01338ccSMarkus Armbruster
174*f01338ccSMarkus Armbrusterdef main(argv):
175*f01338ccSMarkus Armbruster    parser = argparse.ArgumentParser(
176*f01338ccSMarkus Armbruster        description='QAPI schema tester')
177*f01338ccSMarkus Armbruster    parser.add_argument('-d', '--dir', action='store', default='',
178*f01338ccSMarkus Armbruster                        help="directory containing tests")
179*f01338ccSMarkus Armbruster    parser.add_argument('-u', '--update', action='store_true',
180*f01338ccSMarkus Armbruster                        help="update expected test results")
181*f01338ccSMarkus Armbruster    parser.add_argument('tests', nargs='*', metavar='TEST', action='store')
182*f01338ccSMarkus Armbruster    args = parser.parse_args()
183*f01338ccSMarkus Armbruster
184*f01338ccSMarkus Armbruster    status = 0
185*f01338ccSMarkus Armbruster    for t in args.tests:
186*f01338ccSMarkus Armbruster        (dir_name, base_name) = os.path.split(t)
187*f01338ccSMarkus Armbruster        dir_name = dir_name or args.dir
188*f01338ccSMarkus Armbruster        test_name = os.path.splitext(base_name)[0]
189*f01338ccSMarkus Armbruster        status |= test_and_diff(test_name, dir_name, args.update)
190*f01338ccSMarkus Armbruster
191*f01338ccSMarkus Armbruster    exit(status)
192*f01338ccSMarkus Armbruster
193*f01338ccSMarkus Armbruster
194*f01338ccSMarkus Armbrusterif __name__ == '__main__':
195*f01338ccSMarkus Armbruster    main(sys.argv)
196*f01338ccSMarkus Armbruster    exit(0)
197