runtests.py 9.96 KB
Newer Older
1 2 3 4 5 6 7 8
# 
# (c) Simon Marlow 2002
#

import sys
import os
import string
import getopt
9
import platform
10
import time
11
import re
12

13 14 15 16 17 18 19 20 21 22 23
# We don't actually need subprocess in runtests.py, but:
# * We do need it in testlibs.py
# * We can't import testlibs.py until after we have imported ctypes
# * If we import ctypes before subprocess on cygwin, then sys.exit(0)
#   says "Aborted" and we fail with exit code 134.
# So we import it here first, so that the testsuite doesn't appear to fail.
try:
    import subprocess
except:
    pass

24 25
from testutil import *
from testglobals import *
26

27 28 29 30 31
# Readline sometimes spews out ANSI escapes for some values of TERM,
# which result in test failures. Thus set TERM to a nice, simple, safe
# value.
os.environ['TERM'] = 'vt100'

32
global config
33
config = getConfig() # get it from testglobals
34 35 36 37 38 39 40 41 42

# -----------------------------------------------------------------------------
# cmd-line options

long_options = [
  "config=",  		# config file
  "rootdir=", 		# root of tree containing tests (default: .)
  "output-summary=", 	# file in which to save the (human-readable) summary
  "only=",		# just this test (can be give multiple --only= flags)
43
  "way=",		# just this way
44
  "skipway=",		# skip this way
ei@vuokko.info's avatar
ei@vuokko.info committed
45
  "threads=",           # threads to run simultaneously
46
  "check-files-written", # check files aren't written by multiple tests
47
  "verbose=",          # verbose (0,1,2 so far)
48
  "skip-perf-tests",       # skip performance tests
49 50 51 52 53 54 55 56 57 58 59 60 61 62
  ]

opts, args = getopt.getopt(sys.argv[1:], "e:", long_options)
       
for opt,arg in opts:
    if opt == '--config':
        execfile(arg)

    # -e is a string to execute from the command line.  For example:
    # testframe -e 'config.compiler=ghc-5.04'
    if opt == '-e':
        exec arg

    if opt == '--rootdir':
63
        config.rootdirs.append(arg)
64 65 66 67 68 69 70

    if opt == '--output-summary':
        config.output_summary = arg

    if opt == '--only':
        config.only.append(arg)

71
    if opt == '--way':
krc's avatar
krc committed
72
        if (arg not in config.run_ways and arg not in config.compile_ways and arg not in config.other_ways):
73 74 75
            sys.stderr.write("ERROR: requested way \'" +
                             arg + "\' does not exist\n")
            sys.exit(1)
76 77 78 79
        config.cmdline_ways = [arg] + config.cmdline_ways
        if (arg in config.other_ways):
            config.run_ways = [arg] + config.run_ways
            config.compile_ways = [arg] + config.compile_ways
ei@vuokko.info's avatar
ei@vuokko.info committed
80

81 82 83 84 85 86 87 88 89
    if opt == '--skipway':
        if (arg not in config.run_ways and arg not in config.compile_ways and arg not in config.other_ways):
            sys.stderr.write("ERROR: requested way \'" +
                             arg + "\' does not exist\n")
            sys.exit(1)
        config.other_ways = filter(neq(arg), config.other_ways)
        config.run_ways = filter(neq(arg), config.run_ways)
        config.compile_ways = filter(neq(arg), config.compile_ways)

ei@vuokko.info's avatar
ei@vuokko.info committed
90
    if opt == '--threads':
Ian Lynagh's avatar
Ian Lynagh committed
91 92 93
        config.threads = int(arg)
        config.use_threads = 1

94 95 96
    if opt == '--check-files-written':
        config.check_files_written = True

97 98 99
    if opt == '--skip-perf-tests':
        config.skip_perf_tests = True

100
    if opt == '--verbose':
101 102
        if arg not in ["0","1","2","3","4"]:
            sys.stderr.write("ERROR: requested verbosity %s not supported, use 0,1,2,3 or 4" % arg)
103 104 105 106
            sys.exit(1)
        config.verbose = int(arg)


Ian Lynagh's avatar
Ian Lynagh committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
if config.use_threads == 1:
    # Trac #1558 says threads don't work in python 2.4.4, but do
    # in 2.5.2. Probably >= 2.5 is sufficient, but let's be
    # conservative here.
    # Some versions of python have things like '1c1' for some of
    # these components (see trac #3091), but int() chokes on the
    # 'c1', so we drop it.
    (maj, min, pat) = platform.python_version_tuple()
    # We wrap maj, min, and pat in str() to work around a bug in python
    # 2.6.1
    maj = int(re.sub('[^0-9].*', '', str(maj)))
    min = int(re.sub('[^0-9].*', '', str(min)))
    pat = int(re.sub('[^0-9].*', '', str(pat)))
    if (maj, min, pat) < (2, 5, 2):
        print "Warning: Ignoring request to use threads as python version < 2.5.2"
        config.use_threads = 0
123 124 125 126 127 128
    # We also need to disable threads for python 2.7.2, because of
    # this bug: http://bugs.python.org/issue13817
    elif (maj, min, pat) == (2, 7, 2):
        print "Warning: Ignoring request to use threads as python version is 2.7.2"
        print "See http://bugs.python.org/issue13817 for details."
        config.use_threads = 0
Ian Lynagh's avatar
Ian Lynagh committed
129 130 131
    if windows:
        print "Warning: Ignoring request to use threads as running on Windows"
        config.use_threads = 0
132

Ian Lynagh's avatar
Ian Lynagh committed
133 134
config.cygwin = False
config.msys = False
135

Ian Lynagh's avatar
Ian Lynagh committed
136
if windows:
137 138 139 140
    h = os.popen('uname -s', 'r')
    v = h.read()
    h.close()
    if v.startswith("CYGWIN"):
Ian Lynagh's avatar
Ian Lynagh committed
141
        config.cygwin = True
142 143 144
    elif v.startswith("MINGW"):
# msys gives "MINGW32"
# msys2 gives "MINGW_NT-6.2"
Ian Lynagh's avatar
Ian Lynagh committed
145
        config.msys = True
146
    else:
147
        raise Exception("Can't detect Windows terminal type")
Ian Lynagh's avatar
Ian Lynagh committed
148

149 150
# Try to use UTF8
if windows:
151
    import ctypes
152
    if config.cygwin:
Ian Lynagh's avatar
Ian Lynagh committed
153 154 155 156 157 158 159
        # Is this actually right? Which calling convention does it use?
        # As of the time of writing, ctypes.windll doesn't exist in the
        # cygwin python, anyway.
        mydll = ctypes.cdll
    else:
        mydll = ctypes.windll

160 161 162
    # This actually leaves the terminal in codepage 65001 (UTF8) even
    # after python terminates. We ought really remember the old codepage
    # and set it back.
Ian Lynagh's avatar
Ian Lynagh committed
163
    if mydll.kernel32.SetConsoleCP(65001) == 0:
164
        raise Exception("Failure calling SetConsoleCP(65001)")
Ian Lynagh's avatar
Ian Lynagh committed
165
    if mydll.kernel32.SetConsoleOutputCP(65001) == 0:
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        raise Exception("Failure calling SetConsoleOutputCP(65001)")
else:
    # Try and find a utf8 locale to use
    # First see if we already have a UTF8 locale
    h = os.popen('locale | grep LC_CTYPE | grep -i utf', 'r')
    v = h.read()
    h.close()
    if v == '':
        # We don't, so now see if 'locale -a' works
        h = os.popen('locale -a', 'r')
        v = h.read()
        h.close()
        if v != '':
            # If it does then use the first utf8 locale that is available
            h = os.popen('locale -a | grep -i "utf8\|utf-8" 2>/dev/null', 'r')
            v = h.readline().strip()
            h.close()
            if v != '':
                os.environ['LC_ALL'] = v
                print "setting LC_ALL to", v
            else:
                print 'WARNING: No UTF8 locale found.'
                print 'You may get some spurious test failures.'

190 191 192
# This has to come after arg parsing as the args can change the compiler
get_compiler_info()

193 194 195 196
# Can't import this earlier as we need to know if threading will be
# enabled or not
from testlib import *

197 198
# On Windows we need to set $PATH to include the paths to all the DLLs
# in order for the dynamic library tests to work.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
if windows or darwin:
    pkginfo = getStdout([config.ghc_pkg, 'dump'])
    topdir = config.libdir
    for line in pkginfo.split('\n'):
        if line.startswith('library-dirs:'):
            path = line.rstrip()
            path = re.sub('^library-dirs: ', '', path)
            path = re.sub('\\$topdir', topdir, path)
            if path.startswith('"'):
                path = re.sub('^"(.*)"$', '\\1', path)
                path = re.sub('\\\\(.)', '\\1', path)
            if windows:
                if config.cygwin:
                    # On cygwin we can't put "c:\foo" in $PATH, as : is a
                    # field separator. So convert to /cygdrive/c/foo instead.
                    # Other pythons use ; as the separator, so no problem.
                    path = re.sub('([a-zA-Z]):', '/cygdrive/\\1', path)
                    path = re.sub('\\\\', '/', path)
                os.environ['PATH'] = os.pathsep.join([path, os.environ.get("PATH", "")])
            else:
                # darwin
                os.environ['DYLD_LIBRARY_PATH'] = os.pathsep.join([path, os.environ.get("DYLD_LIBRARY_PATH", "")])
221

222 223 224 225 226 227
global testopts_local
testopts_local.x = TestOptions()

if config.use_threads:
    t.lock = threading.Lock()
    t.thread_pool = threading.Condition(t.lock)
228
    t.lockFilesWritten = threading.Lock()
229 230
    t.running_threads = 0

231 232 233 234 235 236
# if timeout == -1 then we try to calculate a sensible value
if config.timeout == -1:
    config.timeout = int(read_no_crs(config.top + '/timeout/calibrate.out'))

print 'Timeout is ' + str(config.timeout)

237 238 239
# -----------------------------------------------------------------------------
# The main dude

240 241 242 243
if config.rootdirs == []:
    config.rootdirs = ['.']

t_files = findTFiles(config.rootdirs)
244 245 246 247 248

print 'Found', len(t_files), '.T files...'

t = getTestRun()

249
# Avoid cmd.exe built-in 'date' command on Windows
250
t.start_time = time.localtime()
251

252
print 'Beginning test run at', time.strftime("%c %Z",t.start_time)
253 254 255 256 257

# set stdout to unbuffered (is this the best way to do it?)
sys.stdout.flush()
sys.stdout = os.fdopen(sys.__stdout__.fileno(), "w", 0)

258
# First collect all the tests to be run
259
for file in t_files:
260
    if_verbose(2, '====> Scanning %s' % file)
261 262 263 264
    newTestDir(os.path.dirname(file))
    try:
        execfile(file)
    except:
265
        print '*** framework failure: found an error while executing ', file, ':'
266
        t.n_framework_failures = t.n_framework_failures + 1
267
        traceback.print_exc()
268

ian@well-typed.com's avatar
ian@well-typed.com committed
269 270
if config.list_broken:
    global brokens
271
    print ''
ian@well-typed.com's avatar
ian@well-typed.com committed
272
    print 'Broken tests:'
273
    print (' '.join(map (lambda (b, d, n) : '#' + str(b) + '(' + d + '/' + n + ')', brokens)))
274 275 276 277 278
    print ''

    if t.n_framework_failures != 0:
        print 'WARNING:', str(t.n_framework_failures), 'framework failures!'
        print ''
ian@well-typed.com's avatar
ian@well-typed.com committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
else:
    # Now run all the tests
    if config.use_threads:
        t.running_threads=0
    for oneTest in parallelTests:
        if stopping():
            break
        oneTest()
    if config.use_threads:
        t.thread_pool.acquire()
        while t.running_threads>0:
            t.thread_pool.wait()
        t.thread_pool.release()
    config.use_threads = False
    for oneTest in aloneTests:
        if stopping():
            break
        oneTest()
ei@vuokko.info's avatar
ei@vuokko.info committed
297
        
ian@well-typed.com's avatar
ian@well-typed.com committed
298
    summary(t, sys.stdout)
299

ian@well-typed.com's avatar
ian@well-typed.com committed
300 301
    if config.output_summary != '':
        summary(t, open(config.output_summary, 'w'))
302 303 304

sys.exit(0)