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