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

Reply via email to