Changeset: 23e73746eb06 for MonetDB URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=23e73746eb06 Added Files: clients/Tests/malcheck.sh clients/Tests/malcheck.stable.err clients/Tests/malcheck.stable.out clients/Tests/malcheck.timeout testing/malcheck.py Modified Files: clients/Tests/All testing/Makefile.ag testing/Mtest.py.in Branch: Oct2014 Log Message:
Added test to check correspondence between C and MAL signatures. Note, the stable.out file should always be empty! diffs (287 lines): diff --git a/clients/Tests/All b/clients/Tests/All --- a/clients/Tests/All +++ b/clients/Tests/All @@ -6,3 +6,4 @@ HAVE_CFITSIO&HAVE_GEOM&!HAVE_GSL&!HAVE_L HAVE_GEOM&HAVE_GSL&HAVE_SAMTOOLS?SQL-dump_all HAVE_GEOM&!HAVE_GSL&!HAVE_SAMTOOLS?SQL-dump_geom !HAVE_GEOM&!HAVE_GSL&!HAVE_SAMTOOLS?SQL-dump_none +MERCURIAL?malcheck diff --git a/clients/Tests/malcheck.sh b/clients/Tests/malcheck.sh new file mode 100755 --- /dev/null +++ b/clients/Tests/malcheck.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd $TSTSRCBASE +hg locate -I monetdb5 -I geom -I sql -X '**/Tests/**' '*.[ch]' '*.mal' | python -c 'import MonetDBtesting.malcheck' diff --git a/clients/Tests/malcheck.stable.err b/clients/Tests/malcheck.stable.err new file mode 100644 --- /dev/null +++ b/clients/Tests/malcheck.stable.err @@ -0,0 +1,12 @@ +stderr of test 'malcheck` in directory 'clients` itself: + + +# 15:15:51 > +# 15:15:51 > "./malcheck.sh" "malcheck" +# 15:15:51 > + + +# 15:16:26 > +# 15:16:26 > "Done." +# 15:16:26 > + diff --git a/clients/Tests/malcheck.stable.out b/clients/Tests/malcheck.stable.out new file mode 100644 --- /dev/null +++ b/clients/Tests/malcheck.stable.out @@ -0,0 +1,12 @@ +stdout of test 'malcheck` in directory 'clients` itself: + + +# 15:15:51 > +# 15:15:51 > "./malcheck.sh" "malcheck" +# 15:15:51 > + + +# 15:16:26 > +# 15:16:26 > "Done." +# 15:16:26 > + diff --git a/clients/Tests/malcheck.timeout b/clients/Tests/malcheck.timeout new file mode 100644 --- /dev/null +++ b/clients/Tests/malcheck.timeout @@ -0,0 +1,1 @@ +8 diff --git a/testing/Makefile.ag b/testing/Makefile.ag --- a/testing/Makefile.ag +++ b/testing/Makefile.ag @@ -45,7 +45,7 @@ scripts_py = { headers_python = { HEADERS = py DIR = $(prefix)/$(PYTHON2_LIBDIR)/MonetDBtesting - SOURCES = trace.py process.py monet_options.py.in __init__.py listexports.py.in exportutils.py + SOURCES = trace.py process.py monet_options.py.in __init__.py listexports.py.in exportutils.py malcheck.py } scripts_sh = { diff --git a/testing/Mtest.py.in b/testing/Mtest.py.in --- a/testing/Mtest.py.in +++ b/testing/Mtest.py.in @@ -344,6 +344,7 @@ CONDITIONALS = { 'HAVE_JDBCTESTS_JAR' : "", 'HAVE_JDBCTESTS_DIR' : "", 'HAVE_JDBCTESTS' : "", + 'MERCURIAL' : "", } # a bunch of classes to help with generating (X)HTML files @@ -2762,7 +2763,7 @@ def DoIt(env, SERVER, CALL, TST, EXT, PR Srvr.extend(PROLOGUE) # enable r integration in server - if CONDITIONALS['HAVE_LIBR'] == '#': + if CONDITIONALS['HAVE_LIBR']: Srvr.extend(['--set', 'embedded_r=yes']) pSrvr, pSrvrTimer = LaunchIt(Srvr, '\nio.printf("\\nReady.\\n");\n', SrvrOut, SrvrErr, TIMEOUT) @@ -3653,6 +3654,7 @@ def main(argv) : else: ErrXit("Illegal "+p+": directory '"+env[p]+"' does not exist!") + global TSTTRGBASE TSTTRGBASE = env['TSTTRGBASE'] global TSTSRCBASE @@ -3668,6 +3670,19 @@ def main(argv) : global TSTPREF TSTPREF = env['TSTPREF'] + # check whether we have a Mercurial clone + BACK = os.getcwd() + try: + os.chdir(TSTSRCBASE) + proc = process.Popen(['hg', 'root'], stdout = process.PIPE) + out, err = proc.communicate() + if proc.returncode == 0: + CONDITIONALS['MERCURIAL'] = '#' # True + proc = None + except: + pass + os.chdir(BACK) + # read '.Mapprove.rc' if THISFILE == 'Mapprove.py': f = os.path.join(TSTTRGBASE, TSTPREF, '.Mapprove.rc') diff --git a/testing/malcheck.py b/testing/malcheck.py new file mode 100644 --- /dev/null +++ b/testing/malcheck.py @@ -0,0 +1,166 @@ +import re, sys + +import exportutils + +# recognize MAL "command" declarations +comreg = re.compile(r'\bcommand\s+(?P<malf>[a-zA-Z_][a-zA-Z_0-9.]*)\s*(?:{[^}]*}\s*)?\(\s*(?P<args>[^()]*)\)\s*(?P<rets>\([^()]*\)|:bat\[[^]]*\]|:[a-zA-Z_][a-zA-Z_0-9]*|)\s+address\s+(?P<func>[a-zA-Z_][a-zA-Z_0-9]*)\b') + +# recognize MAL "pattern" declarations +patreg = re.compile(r'\bpattern\s+(?P<malf>[a-zA-Z_][a-zA-Z_0-9.]*)\s*(?:{[^}]*}\s*)?\(\s*(?P<args>[^()]*)\)\s*(?P<rets>\([^()]*\)|:bat\[[^]]*\]|:[a-zA-Z_][a-zA-Z_0-9]*|)\s+address\s+(?P<func>[a-zA-Z_][a-zA-Z_0-9]*)\b') + +treg = re.compile(r':(bat\[[^]]*\]|[a-zA-Z_][a-zA-Z_0-9]*)') + +cmtre = re.compile(r'/\*.*?\*/|//[^\n]*', re.DOTALL) +expre = re.compile(r'\b[a-zA-Z_0-9]+export\s+(?P<decl>[^;]*;)', re.MULTILINE) +nmere = re.compile(r'\b(?P<name>[a-zA-Z_][a-zA-Z_0-9]*)\s*[[(;]') + +freg = re.compile(r'str (?P<name>\w+)\((?P<args>[^()]*)\)') +creg = re.compile(r'\bconst\b') +sreg = re.compile(r'\bchar\s*\*') +areg = re.compile(r'\w+') + +mappings = { + 'zrule': 'rule', + 'timezone': 'tzone', + 'streams': 'Stream', + 'bstream': 'Bstream', + 'any_1': 'void', + 'any_2': 'void', + 'any_3': 'void', + 'any_4': 'void', + 'sqlblob': 'blob', +} +cmappings = { + 'sqlblob': 'blob', +} + +defre = re.compile(r'^[ \t]*#[ \t]*define[ \t]+(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)\((?P<args>[a-zA-Z0-9_, \t]*)\)[ \t]*(?P<def>.*)$', re.MULTILINE) + +cldef = re.compile(r'^[ \t]*#', re.MULTILINE) + +malfuncs = [] +malpats = [] +decls = {} +pdecls = {} + +def process(f): + data = open(f).read() + if f.endswith('.mal'): + res = comreg.search(data) + while res is not None: + malf, args, rets, func = res.groups() + if malf not in ('del', 'cmp', 'fromstr', 'fix', 'heap', 'hash', 'length', 'null', 'nequal', 'put', 'storage', 'tostr', 'unfix', 'read', 'write', 'epilogue'): + rtypes = [] + atypes = [] + if not rets: + rets = ':void' + tres = treg.search(rets) + while tres is not None: + typ = tres.group(1) + if typ.startswith('bat['): + typ = 'bat' + rtypes.append(mappings.get(typ, typ)) + tres = treg.search(rets, tres.end(0)) + tres = treg.search(args) + while tres is not None: + typ = tres.group(1) + if typ.startswith('bat['): + typ = 'bat' + atypes.append(mappings.get(typ, typ)) + tres = treg.search(args, tres.end(0)) + malfuncs.append((tuple(rtypes), tuple(atypes), malf, func, f)) + res = comreg.search(data, res.end(0)) + res = patreg.search(data) + while res is not None: + malf, args, rets, func = res.groups() + malpats.append((malf, func, f)) + res = patreg.search(data, res.end(0)) + elif f.endswith('.h') or f.endswith('.c'): + # remove C comments + res = cmtre.search(data) + while res is not None: + data = data[:res.start(0)] + ' ' + data[res.end(0):] + res = cmtre.search(data, res.start(0)) + # remove \ <newline> combo's + data = data.replace('\\\n', '') + + data = exportutils.preprocess(data) + + res = expre.search(data) + while res is not None: + pos = res.end(0) + decl = exportutils.normalize(res.group('decl')) + res = nmere.search(decl) + if decl.startswith('char *'): + decl = 'str ' + decl[6:] + if '(' in decl and decl.startswith('str '): + res = freg.match(decl) + if res is not None: + name, args = res.groups() + args = map(lambda x: x.strip(), args.split(',')) + if len(args) == 4 and \ + args[0].startswith('Client ') and \ + args[1].startswith('MalBlkPtr ') and \ + args[2].startswith('MalStkPtr ') and \ + args[3].startswith('InstrPtr '): + pdecls[name] = f + else: + a = [] + for arg in args: + if '(' in arg: + # complicated (function pointer) argument + break + if creg.search(arg) is not None: + rdonly = True + arg = creg.sub('', arg) + else: + rdonly = False + arg = arg.strip() + if arg.startswith('ptr ') and not '*' in arg: + arg = 'void *' + arg[4:] + if '*' not in arg: + break + arg = sreg.sub('str', arg) + typ = areg.match(arg).group(0) + a.append((cmappings.get(typ, typ), rdonly)) + else: + decls[name] = (tuple(a), f) + res = expre.search(data, pos) + +if len(sys.argv) > 1: + files = sys.argv[1:] +else: + files = map(lambda x: x.strip(), sys.stdin.readlines()) +for f in files: + f = f.strip() + process(f) + +for rtypes, atypes, malf, func, f in malfuncs: + if not decls.has_key(func): + print '%s: missing for MAL command %s in %s' % (func, malf, f) + else: + args, funcf = decls[func] + if len(args) != len(rtypes) + len(atypes): + print '%s in %s: argument count mismatch for %s %s' % (func, funcf, malf, f) + else: + args = list(args) + i = 0 + for t in rtypes: + i = i + 1 + if t != args[0][0] or args[0][1]: + print '%s in %s: return %d type mismatch for %s %s (%s vs %s)' % (func, funcf, i, malf, f, t, args[0][0]) + del args[0] + i = 0 + for t in atypes: + i = i + 1 + # special dispensation for these functions: they + # handle str and json arguments both correctly + if func in ('JSONstr2json', 'JSONisvalid', 'JSONisobject', 'JSONisarray') and t in ('str', 'json'): + t = args[0][0] + if t != args[0][0]: + print '%s in %s: argument %d type mismatch for %s %s (%s vs %s)' % (func, funcf, i, malf, f, t, args[0][0]) + del args[0] + +for malf, func, f in malpats: + if not pdecls.has_key(func): + print '%s: missing for MAL pattern %s in %s' % (func, malf, f) _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list