123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- #!/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)
|