DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (4a108e94d3e2)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 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)