diff options
Diffstat (limited to 'build/pymake/tests/runtests.py')
-rw-r--r-- | build/pymake/tests/runtests.py | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/build/pymake/tests/runtests.py b/build/pymake/tests/runtests.py new file mode 100644 index 000000000..ab149ecfb --- /dev/null +++ b/build/pymake/tests/runtests.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +""" +Run the test(s) listed on the command line. If a directory is listed, the script will recursively +walk the directory for files named .mk and run each. + +For each test, we run gmake -f test.mk. By default, make must exit with an exit code of 0, and must print 'TEST-PASS'. + +Each test is run in an empty directory. + +The test file may contain lines at the beginning to alter the default behavior. These are all evaluated as python: + +#T commandline: ['extra', 'params', 'here'] +#T returncode: 2 +#T returncode-on: {'win32': 2} +#T environment: {'VAR': 'VALUE} +#T grep-for: "text" +""" + +from subprocess import Popen, PIPE, STDOUT +from optparse import OptionParser +import os, re, sys, shutil, glob + +class ParentDict(dict): + def __init__(self, parent, **kwargs): + self.d = dict(kwargs) + self.parent = parent + + def __setitem__(self, k, v): + self.d[k] = v + + def __getitem__(self, k): + if k in self.d: + return self.d[k] + + return self.parent[k] + +thisdir = os.path.dirname(os.path.abspath(__file__)) + +pymake = [sys.executable, os.path.join(os.path.dirname(thisdir), 'make.py')] +manifest = os.path.join(thisdir, 'tests.manifest') + +o = OptionParser() +o.add_option('-g', '--gmake', + dest="gmake", default="gmake") +o.add_option('-d', '--tempdir', + dest="tempdir", default="_mktests") +opts, args = o.parse_args() + +if len(args) == 0: + args = [thisdir] + +makefiles = [] +for a in args: + if os.path.isfile(a): + makefiles.append(a) + elif os.path.isdir(a): + makefiles.extend(sorted(glob.glob(os.path.join(a, '*.mk')))) + +def runTest(makefile, make, logfile, options): + """ + Given a makefile path, test it with a given `make` and return + (pass, message). + """ + + if os.path.exists(opts.tempdir): shutil.rmtree(opts.tempdir) + os.mkdir(opts.tempdir, 0755) + + logfd = open(logfile, 'w') + p = Popen(make + options['commandline'], stdout=logfd, stderr=STDOUT, env=options['env']) + logfd.close() + retcode = p.wait() + + if retcode != options['returncode']: + return False, "FAIL (returncode=%i)" % retcode + + logfd = open(logfile) + stdout = logfd.read() + logfd.close() + + if stdout.find('TEST-FAIL') != -1: + print stdout + return False, "FAIL (TEST-FAIL printed)" + + if options['grepfor'] and stdout.find(options['grepfor']) == -1: + print stdout + return False, "FAIL (%s not in output)" % options['grepfor'] + + if options['returncode'] == 0 and stdout.find('TEST-PASS') == -1: + print stdout + return False, 'FAIL (No TEST-PASS printed)' + + if options['returncode'] != 0: + return True, 'PASS (retcode=%s)' % retcode + + return True, 'PASS' + +print "%-30s%-28s%-28s" % ("Test:", "gmake:", "pymake:") + +gmakefails = 0 +pymakefails = 0 + +tre = re.compile('^#T (gmake |pymake )?([a-z-]+)(?:: (.*))?$') + +for makefile in makefiles: + # For some reason, MAKEFILE_LIST uses native paths in GNU make on Windows + # (even in MSYS!) so we pass both TESTPATH and NATIVE_TESTPATH + cline = ['-C', opts.tempdir, '-f', os.path.abspath(makefile), 'TESTPATH=%s' % thisdir.replace('\\','/'), 'NATIVE_TESTPATH=%s' % thisdir] + if sys.platform == 'win32': + #XXX: hack so we can specialize the separator character on windows. + # we really shouldn't need this, but y'know + cline += ['__WIN32__=1'] + + options = { + 'returncode': 0, + 'grepfor': None, + 'env': dict(os.environ), + 'commandline': cline, + 'pass': True, + 'skip': False, + } + + gmakeoptions = ParentDict(options) + pymakeoptions = ParentDict(options) + + dmap = {None: options, 'gmake ': gmakeoptions, 'pymake ': pymakeoptions} + + mdata = open(makefile) + for line in mdata: + line = line.strip() + m = tre.search(line) + if m is None: + break + + make, key, data = m.group(1, 2, 3) + d = dmap[make] + if data is not None: + data = eval(data) + if key == 'commandline': + assert make is None + d['commandline'].extend(data) + elif key == 'returncode': + d['returncode'] = data + elif key == 'returncode-on': + if sys.platform in data: + d['returncode'] = data[sys.platform] + elif key == 'environment': + for k, v in data.iteritems(): + d['env'][k] = v + elif key == 'grep-for': + d['grepfor'] = data + elif key == 'fail': + d['pass'] = False + elif key == 'skip': + d['skip'] = True + else: + print >>sys.stderr, "%s: Unexpected #T key: %s" % (makefile, key) + sys.exit(1) + + mdata.close() + + if gmakeoptions['skip']: + gmakepass, gmakemsg = True, '' + else: + gmakepass, gmakemsg = runTest(makefile, [opts.gmake], + makefile + '.gmakelog', gmakeoptions) + + if gmakeoptions['pass']: + if not gmakepass: + gmakefails += 1 + else: + if gmakepass: + gmakefails += 1 + gmakemsg = "UNEXPECTED PASS" + else: + gmakemsg = "KNOWN FAIL" + + if pymakeoptions['skip']: + pymakepass, pymakemsg = True, '' + else: + pymakepass, pymakemsg = runTest(makefile, pymake, + makefile + '.pymakelog', pymakeoptions) + + if pymakeoptions['pass']: + if not pymakepass: + pymakefails += 1 + else: + if pymakepass: + pymakefails += 1 + pymakemsg = "UNEXPECTED PASS" + else: + pymakemsg = "OK (known fail)" + + print "%-30.30s%-28.28s%-28.28s" % (os.path.basename(makefile), + gmakemsg, pymakemsg) + +print +print "Summary:" +print "%-30s%-28s%-28s" % ("", "gmake:", "pymake:") + +if gmakefails == 0: + gmakemsg = 'PASS' +else: + gmakemsg = 'FAIL (%i failures)' % gmakefails + +if pymakefails == 0: + pymakemsg = 'PASS' +else: + pymakemsg = 'FAIL (%i failures)' % pymakefails + +print "%-30.30s%-28.28s%-28.28s" % ('', gmakemsg, pymakemsg) + +shutil.rmtree(opts.tempdir) + +if gmakefails or pymakefails: + sys.exit(1) |