diff options
Diffstat (limited to 'testsuite/driver')
-rw-r--r-- | testsuite/driver/junit.py | 38 | ||||
-rw-r--r-- | testsuite/driver/runtests.py | 240 | ||||
-rw-r--r-- | testsuite/driver/testglobals.py | 47 | ||||
-rw-r--r-- | testsuite/driver/testlib.py | 150 | ||||
-rw-r--r-- | testsuite/driver/testutil.py | 19 |
5 files changed, 277 insertions, 217 deletions
diff --git a/testsuite/driver/junit.py b/testsuite/driver/junit.py new file mode 100644 index 0000000000..f2dbebb96a --- /dev/null +++ b/testsuite/driver/junit.py @@ -0,0 +1,38 @@ +from datetime import datetime +import xml.etree.ElementTree as ET + +def junit(t): + testsuites = ET.Element('testsuites') + testsuite = ET.SubElement(testsuites, 'testsuite', + id = "0", + package = 'ghc', + tests = str(t.total_tests), + failures = str(len(t.unexpected_failures) + len(t.unexpected_stat_failures)), + errors = str(len(t.framework_failures)), + timestamp = datetime.now().isoformat()) + + for res_type, group in [('stat failure', t.unexpected_stat_failures), + ('unexpected failure', t.unexpected_failures)]: + for (directory, testname, reason, way) in group: + testcase = ET.SubElement(testsuite, 'testcase', + classname = testname, + name = way) + result = ET.SubElement(testcase, 'failure', + type = res_type, + message = reason) + + for (directory, testname, reason, way) in t.framework_failures: + testcase = ET.SubElement(testsuite, 'testcase', + classname = testname, + name = way) + result = ET.SubElement(testcase, 'error', + type = "framework failure", + message = reason) + + for (directory, testname, way) in t.expected_passes: + testcase = ET.SubElement(testsuite, 'testcase', + classname = testname, + name = way) + + return ET.ElementTree(testsuites) + diff --git a/testsuite/driver/runtests.py b/testsuite/driver/runtests.py index 7e4f375a2c..b956239d2a 100644 --- a/testsuite/driver/runtests.py +++ b/testsuite/driver/runtests.py @@ -4,17 +4,16 @@ # (c) Simon Marlow 2002 # -from __future__ import print_function - +import argparse import signal import sys import os -import string -import getopt +import io import shutil import tempfile import time import re +import traceback # We don't actually need subprocess in runtests.py, but: # * We do need it in testlibs.py @@ -24,13 +23,15 @@ import re # So we import it here first, so that the testsuite doesn't appear to fail. import subprocess -from testutil import * -from testglobals import * +from testutil import getStdout, Watcher +from testglobals import getConfig, ghc_env, getTestRun, TestOptions, brokens +from junit import junit # 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' +ghc_env['TERM'] = 'vt100' global config config = getConfig() # get it from testglobals @@ -41,81 +42,69 @@ def signal_handler(signal, frame): # ----------------------------------------------------------------------------- # cmd-line options -long_options = [ - "configfile=", # config file - "config=", # config field - "rootdir=", # root of tree containing tests (default: .) - "summary-file=", # file in which to save the (human-readable) summary - "no-print-summary=", # should we print the summary? - "only=", # just this test (can be give multiple --only= flags) - "way=", # just this way - "skipway=", # skip this way - "threads=", # threads to run simultaneously - "check-files-written", # check files aren't written by multiple tests - "verbose=", # verbose (0,1,2 so far) - "skip-perf-tests", # skip performance tests - ] - -opts, args = getopt.getopt(sys.argv[1:], "e:", long_options) - -for opt,arg in opts: - if opt == '--configfile': - exec(open(arg).read()) - - # -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 == '--config': - field, value = arg.split('=', 1) - setattr(config, field, value) - - if opt == '--rootdir': - config.rootdirs.append(arg) - - if opt == '--summary-file': - config.summary_file = arg - - if opt == '--no-print-summary': - config.no_print_summary = True - - if opt == '--only': - config.run_only_some_tests = True - config.only.add(arg) - - if opt == '--way': - 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.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 - - 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 = [w for w in config.other_ways if w != arg] - config.run_ways = [w for w in config.run_ways if w != arg] - config.compile_ways = [w for w in config.compile_ways if w != arg] - - if opt == '--threads': - config.threads = int(arg) - config.use_threads = 1 - - if opt == '--skip-perf-tests': - config.skip_perf_tests = True - - if opt == '--verbose': - if arg not in ["0","1","2","3","4","5"]: - sys.stderr.write("ERROR: requested verbosity %s not supported, use 0,1,2,3,4 or 5" % arg) - sys.exit(1) - config.verbose = int(arg) - +parser = argparse.ArgumentParser(description="GHC's testsuite driver") + +parser.add_argument("-e", action='append', help="A string to execute from the command line.") +parser.add_argument("--config-file", action="append", help="config file") +parser.add_argument("--config", action='append', help="config field") +parser.add_argument("--rootdir", action='append', help="root of tree containing tests (default: .)") +parser.add_argument("--summary-file", help="file in which to save the (human-readable) summary") +parser.add_argument("--no-print-summary", action="store_true", help="should we print the summary?") +parser.add_argument("--only", action="append", help="just this test (can be give multiple --only= flags)") +parser.add_argument("--way", action="append", help="just this way") +parser.add_argument("--skipway", action="append", help="skip this way") +parser.add_argument("--threads", type=int, help="threads to run simultaneously") +parser.add_argument("--verbose", type=int, choices=[0,1,2,3,4,5], help="verbose (Values 0 through 5 accepted)") +parser.add_argument("--skip-perf-tests", action="store_true", help="skip performance tests") +parser.add_argument("--junit", type=argparse.FileType('wb'), help="output testsuite summary in JUnit format") + +args = parser.parse_args() + +for e in args.e: + exec(e) + +for arg in args.config_file: + exec(open(arg).read()) + +for arg in args.config: + field, value = arg.split('=', 1) + setattr(config, field, value) + +all_ways = config.run_ways+config.compile_ways+config.other_ways +config.rootdirs = args.rootdir +config.summary_file = args.summary_file +config.no_print_summary = args.no_print_summary + +if args.only: + config.only = args.only + config.run_only_some_tests = True + +if args.way: + for way in args.way: + if way not in all_ways: + print('WARNING: Unknown WAY %s in --way' % way) + else: + config.cmdline_ways += [way] + if way in config.other_ways: + config.run_ways += [way] + config.compile_ways += [way] + +if args.skipway: + for way in args.skipway: + if way not in all_ways: + print('WARNING: Unknown WAY %s in --skipway' % way) + + config.other_ways = [w for w in config.other_ways if w not in args.skipway] + config.run_ways = [w for w in config.run_ways if w not in args.skipway] + config.compile_ways = [w for w in config.compile_ways if w not in args.skipway] + +if args.threads: + config.threads = args.threads + config.use_threads = True + +if args.verbose is not None: + config.verbose = args.verbose +config.skip_perf_tests = args.skip_perf_tests config.cygwin = False config.msys = False @@ -161,7 +150,7 @@ else: h.close() if v == '': # We don't, so now see if 'locale -a' works - h = os.popen('locale -a', 'r') + h = os.popen('locale -a | grep -F .', 'r') v = h.read() h.close() if v != '': @@ -171,6 +160,7 @@ else: h.close() if v != '': os.environ['LC_ALL'] = v + ghc_env['LC_ALL'] = v print("setting LC_ALL to", v) else: print('WARNING: No UTF8 locale found.') @@ -183,14 +173,30 @@ get_compiler_info() # enabled or not from testlib import * +def format_path(path): + if windows: + if os.pathsep == ':': + # If using msys2 python instead of mingw we have to change the drive + # letter representation. Otherwise it thinks we're adding two env + # variables E and /Foo when we add E:/Foo. + path = re.sub('([a-zA-Z]):', '/\\1', path) + 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) + return path + # On Windows we need to set $PATH to include the paths to all the DLLs # in order for the dynamic library tests to work. if windows or darwin: - pkginfo = str(getStdout([config.ghc_pkg, 'dump'])) + pkginfo = getStdout([config.ghc_pkg, 'dump']) topdir = config.libdir if windows: - mingw = os.path.join(topdir, '../mingw/bin') - os.environ['PATH'] = os.pathsep.join([os.environ.get("PATH", ""), mingw]) + mingw = os.path.abspath(os.path.join(topdir, '../mingw/bin')) + mingw = format_path(mingw) + ghc_env['PATH'] = os.pathsep.join([ghc_env.get("PATH", ""), mingw]) for line in pkginfo.split('\n'): if line.startswith('library-dirs:'): path = line.rstrip() @@ -203,18 +209,12 @@ if windows or darwin: 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", "")]) + path = format_path(path) + ghc_env['PATH'] = os.pathsep.join([path, ghc_env.get("PATH", "")]) else: # darwin - os.environ['DYLD_LIBRARY_PATH'] = os.pathsep.join([path, os.environ.get("DYLD_LIBRARY_PATH", "")]) + ghc_env['DYLD_LIBRARY_PATH'] = os.pathsep.join([path, ghc_env.get("DYLD_LIBRARY_PATH", "")]) -global testopts_local testopts_local.x = TestOptions() # if timeout == -1 then we try to calculate a sensible value @@ -286,51 +286,63 @@ for name in config.only: framework_fail(name, '', 'test not found') else: # Let user fix .T file errors before reporting on unfound tests. - # The reson the test can not be found is likely because of those + # The reason the test can not be found is likely because of those # .T file errors. pass if config.list_broken: - global brokens print('') print('Broken tests:') print(' '.join(map (lambda bdn: '#' + str(bdn[0]) + '(' + bdn[1] + '/' + bdn[2] + ')', brokens))) print('') if t.framework_failures: - print('WARNING:', len(framework_failures), 'framework failures!') + print('WARNING:', len(t.framework_failures), 'framework failures!') print('') else: # completion watcher watcher = Watcher(len(parallelTests)) # Now run all the tests - for oneTest in parallelTests: - if stopping(): - break - oneTest(watcher) - - # wait for parallel tests to finish - if not stopping(): - watcher.wait() - - # Run the following tests purely sequential - config.use_threads = False - for oneTest in aloneTests: - if stopping(): - break - oneTest(watcher) + try: + for oneTest in parallelTests: + if stopping(): + break + oneTest(watcher) + + # wait for parallel tests to finish + if not stopping(): + watcher.wait() + + # Run the following tests purely sequential + config.use_threads = False + for oneTest in aloneTests: + if stopping(): + break + oneTest(watcher) + except KeyboardInterrupt: + pass # flush everything before we continue sys.stdout.flush() summary(t, sys.stdout, config.no_print_summary) - if config.summary_file != '': + if config.summary_file: with open(config.summary_file, 'w') as file: summary(t, file) -cleanup_and_exit(0) + if args.junit: + junit(t).write(args.junit) + +if len(t.unexpected_failures) > 0 or \ + len(t.unexpected_stat_failures) > 0 or \ + len(t.framework_failures) > 0: + exitcode = 1 +else: + exitcode = 0 + +cleanup_and_exit(exitcode) # Note [Running tests in /tmp] # diff --git a/testsuite/driver/testglobals.py b/testsuite/driver/testglobals.py index fc050e6908..311e39be7f 100644 --- a/testsuite/driver/testglobals.py +++ b/testsuite/driver/testglobals.py @@ -1,4 +1,4 @@ -# +# # (c) Simon Marlow 2002 # @@ -9,7 +9,7 @@ # variable config below. The fields of the structure are filled in by # the appropriate config script(s) for this compiler/platform, in # ../config. -# +# # Bits of the structure may also be filled in from the command line, # via the build system, using the '-e' option to runtests. @@ -27,7 +27,9 @@ class TestConfig: self.only = set() # Accept new output which differs from the sample? - self.accept = 0 + self.accept = False + self.accept_platform = False + self.accept_os = False # File in which to save the summary self.summary_file = '' @@ -41,9 +43,6 @@ class TestConfig: # with --verbose=0. self.no_print_summary = False - # File in which to save the times - self.times_file = '' - # What platform are we running on? self.platform = '' self.os = '' @@ -70,7 +69,7 @@ class TestConfig: # Flags we always give to this compiler self.compiler_always_flags = [] - + # Which ways to run tests (when compiling and running respectively) # Other ways are added from the command line if we have the appropriate # libraries. @@ -103,16 +102,22 @@ class TestConfig: # Do we have SMP support? self.have_smp = False + # Is gdb avaliable? + self.have_gdb = False + + # Is readelf available? + self.have_readelf = False + # Are we testing an in-tree compiler? self.in_tree_compiler = True # the timeout program self.timeout_prog = '' self.timeout = 300 - + # threads self.threads = 1 - self.use_threads = 0 + self.use_threads = False # Should we skip performance tests self.skip_perf_tests = False @@ -123,6 +128,12 @@ config = TestConfig() def getConfig(): return config +import os +# Hold our modified GHC testrunning environment so we don't poison the current +# python's environment. +global ghc_env +ghc_env = os.environ.copy() + # ----------------------------------------------------------------------------- # Information about the current test run @@ -140,6 +151,7 @@ class TestRun: self.framework_failures = [] self.framework_warnings = [] + self.expected_passes = [] self.unexpected_passes = [] self.unexpected_failures = [] self.unexpected_stat_failures = [] @@ -156,7 +168,7 @@ def getTestRun(): class TestOptions: def __init__(self): # skip this test? - self.skip = 0 + self.skip = False # skip these ways self.omit_ways = [] @@ -181,7 +193,7 @@ class TestOptions: self.ignore_stderr = False # Backpack test - self.compile_backpack = 0 + self.compile_backpack = False # We sometimes want to modify the compiler_always_flags, so # they are copied from config.compiler_always_flags when we @@ -218,15 +230,15 @@ class TestOptions: self.alone = False # Does this test use a literate (.lhs) file? - self.literate = 0 + self.literate = False # Does this test use a .c, .m or .mm file? - self.c_src = 0 - self.objc_src = 0 - self.objcpp_src = 0 + self.c_src = False + self.objc_src = False + self.objcpp_src = False # Does this test use a .cmm file? - self.cmm_src = 0 + self.cmm_src = False # Should we put .hi/.o files in a subdirectory? self.outputdir = None @@ -234,9 +246,6 @@ class TestOptions: # Command to run before the test self.pre_cmd = None - # Command to run for extra cleaning - self.clean_cmd = None - # Command wrapper: a function to apply to the command before running it self.cmd_wrapper = None diff --git a/testsuite/driver/testlib.py b/testsuite/driver/testlib.py index 26e3d17679..ff6a8c8e74 100644 --- a/testsuite/driver/testlib.py +++ b/testsuite/driver/testlib.py @@ -3,13 +3,9 @@ # (c) Simon Marlow 2002 # -from __future__ import print_function - import io import shutil import os -import errno -import string import re import traceback import time @@ -18,33 +14,30 @@ import copy import glob import sys from math import ceil, trunc +from pathlib import PurePath import collections import subprocess -from testglobals import * -from testutil import * +from testglobals import config, ghc_env, default_testopts, brokens, t +from testutil import strip_quotes, lndir, link_or_copy_file extra_src_files = {'T4198': ['exitminus1.c']} # TODO: See #12223 +global pool_sema if config.use_threads: import threading - try: - import thread - except ImportError: # Python 3 - import _thread as thread + pool_sema = threading.BoundedSemaphore(value=config.threads) global wantToStop wantToStop = False -global pool_sema -if config.use_threads: - pool_sema = threading.BoundedSemaphore(value=config.threads) - def stopNow(): global wantToStop wantToStop = True + def stopping(): return wantToStop + # Options valid for the current test only (these get reset to # testdir_testopts after each test). @@ -91,7 +84,7 @@ def normal( name, opts ): return; def skip( name, opts ): - opts.skip = 1 + opts.skip = True def expect_fail( name, opts ): # The compiler, testdriver, OS or platform is missing a certain @@ -143,7 +136,8 @@ def _reqlib( name, opts, lib ): cmd = strip_quotes(config.ghc_pkg) p = subprocess.Popen([cmd, '--no-user-package-db', 'describe', lib], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + env=ghc_env) # read from stdout and stderr to avoid blocking due to # buffers filling p.communicate() @@ -209,7 +203,6 @@ def _expect_broken_for( name, opts, bug, ways ): opts.expect_fail_for = ways def record_broken(name, opts, bug): - global brokens me = (bug, opts.testdir, name) if not me in brokens: brokens.append(me) @@ -412,6 +405,12 @@ def compiler_profiled( ): def compiler_debugged( ): return config.compiler_debugged +def have_gdb( ): + return config.have_gdb + +def have_readelf( ): + return config.have_readelf + # --- def high_memory_usage(name, opts): @@ -424,19 +423,19 @@ def multi_cpu_race(name, opts): # --- def literate( name, opts ): - opts.literate = 1; + opts.literate = True def c_src( name, opts ): - opts.c_src = 1; + opts.c_src = True def objc_src( name, opts ): - opts.objc_src = 1; + opts.objc_src = True def objcpp_src( name, opts ): - opts.objcpp_src = 1; + opts.objcpp_src = True def cmm_src( name, opts ): - opts.cmm_src = 1; + opts.cmm_src = True def outputdir( odir ): return lambda name, opts, d=odir: _outputdir(name, opts, d) @@ -454,12 +453,6 @@ def _pre_cmd( name, opts, cmd ): # ---- -def clean_cmd( cmd ): - # TODO. Remove all calls to clean_cmd. - return lambda _name, _opts: None - -# ---- - def cmd_prefix( prefix ): return lambda name, opts, p=prefix: _cmd_prefix(name, opts, prefix) @@ -497,7 +490,6 @@ def no_check_hp(name, opts): def filter_stdout_lines( regex ): """ Filter lines of stdout with the given regular expression """ - import re def f( name, opts ): _normalise_fun(name, opts, lambda s: '\n'.join(re.findall(regex, s))) return f @@ -520,6 +512,19 @@ def normalise_errmsg_fun( *fs ): def _normalise_errmsg_fun( name, opts, *fs ): opts.extra_errmsg_normaliser = join_normalisers(opts.extra_errmsg_normaliser, fs) +def check_errmsg(needle): + def norm(str): + if needle in str: + return "%s contained in -ddump-simpl\n" % needle + else: + return "%s not contained in -ddump-simpl\n" % needle + return normalise_errmsg_fun(norm) + +def grep_errmsg(needle): + def norm(str): + return "".join(filter(lambda l: re.search(needle, l), str.splitlines(True))) + return normalise_errmsg_fun(norm) + def normalise_whitespace_fun(f): return lambda name, opts: _normalise_whitespace_fun(name, opts, f) @@ -607,8 +612,9 @@ def newTestDir(tempdir, dir): testdir_suffix = '.run' def _newTestDir(name, opts, tempdir, dir): + testdir = os.path.join('', *(p for p in PurePath(dir).parts if p != '..')) opts.srcdir = os.path.join(os.getcwd(), dir) - opts.testdir = os.path.join(tempdir, dir, name + testdir_suffix) + opts.testdir = os.path.join(tempdir, testdir, name + testdir_suffix) opts.compiler_always_flags = config.compiler_always_flags # ----------------------------------------------------------------------------- @@ -796,7 +802,7 @@ def do_test(name, way, func, args, files): full_name = name + '(' + way + ')' if_verbose(2, "=====> {0} {1} of {2} {3}".format( - full_name, t.total_tests, len(allTestNames), + full_name, t.total_tests, len(allTestNames), [len(t.unexpected_passes), len(t.unexpected_failures), len(t.framework_failures)])) @@ -848,6 +854,7 @@ def do_test(name, way, func, args, files): if exit_code != 0: framework_fail(name, way, 'pre_cmd failed: {0}'.format(exit_code)) + if_verbose(1, '** pre_cmd was "{0}". Running trace'.format(override_options(opts.pre_cmd))) result = func(*[name,way] + args) @@ -863,6 +870,7 @@ def do_test(name, way, func, args, files): if passFail == 'pass': if _expect_pass(way): + t.expected_passes.append((directory, name, way)) t.n_expected_passes += 1 else: if_verbose(1, '*** unexpected pass for %s' % full_name) @@ -948,8 +956,9 @@ def ghci_script( name, way, script): # We pass HC and HC_OPTS as environment variables, so that the # script can invoke the correct compiler by using ':! $HC $HC_OPTS' - cmd = ('HC={{compiler}} HC_OPTS="{flags}" {{compiler}} {flags} {way_flags}' + cmd = ('HC={{compiler}} HC_OPTS="{flags}" {{compiler}} {way_flags} {flags}' ).format(flags=flags, way_flags=way_flags) + # NB: put way_flags before flags so that flags in all.T can overrie others getTestOpts().stdin = script return simple_run( name, way, cmd, getTestOpts().extra_run_opts ) @@ -964,19 +973,19 @@ def compile_fail( name, way, extra_hc_opts ): return do_compile( name, way, 1, '', [], extra_hc_opts ) def backpack_typecheck( name, way, extra_hc_opts ): - return do_compile( name, way, 0, '', [], "-fno-code -fwrite-interface " + extra_hc_opts, backpack=1 ) + return do_compile( name, way, 0, '', [], "-fno-code -fwrite-interface " + extra_hc_opts, backpack=True ) def backpack_typecheck_fail( name, way, extra_hc_opts ): - return do_compile( name, way, 1, '', [], "-fno-code -fwrite-interface " + extra_hc_opts, backpack=1 ) + return do_compile( name, way, 1, '', [], "-fno-code -fwrite-interface " + extra_hc_opts, backpack=True ) def backpack_compile( name, way, extra_hc_opts ): - return do_compile( name, way, 0, '', [], extra_hc_opts, backpack=1 ) + return do_compile( name, way, 0, '', [], extra_hc_opts, backpack=True ) def backpack_compile_fail( name, way, extra_hc_opts ): - return do_compile( name, way, 1, '', [], extra_hc_opts, backpack=1 ) + return do_compile( name, way, 1, '', [], extra_hc_opts, backpack=True ) def backpack_run( name, way, extra_hc_opts ): - return compile_and_run__( name, way, '', [], extra_hc_opts, backpack=1 ) + return compile_and_run__( name, way, '', [], extra_hc_opts, backpack=True ) def multimod_compile( name, way, top_mod, extra_hc_opts ): return do_compile( name, way, 0, top_mod, [], extra_hc_opts ) @@ -1206,7 +1215,7 @@ def simple_build(name, way, extra_hc_opts, should_fail, top_mod, link, addsuf, b if config.verbose >= 1 and _expect_pass(way): print('Compile failed (exit code {0}) errors were:'.format(exit_code)) actual_stderr_path = in_testdir(name, 'comp.stderr') - if_verbose_dump(1, actual_stderr_path) + dump_file(actual_stderr_path) # ToDo: if the sub-shell was killed by ^C, then exit @@ -1415,7 +1424,7 @@ def stdout_ok(name, way): expected_stdout_file, actual_stdout_file) def dump_stdout( name ): - with open(in_testdir(name, 'run.stdout')) as f: + with open(in_testdir(name, 'run.stdout'), encoding='utf8') as f: str = f.read().strip() if str: print("Stdout (", name, "):") @@ -1431,7 +1440,7 @@ def stderr_ok(name, way): whitespace_normaliser=normalise_whitespace) def dump_stderr( name ): - with open(in_testdir(name, 'run.stderr')) as f: + with open(in_testdir(name, 'run.stderr'), encoding='utf8') as f: str = f.read().strip() if str: print("Stderr (", name, "):") @@ -1553,7 +1562,7 @@ def compare_outputs(way, kind, normaliser, expected_file, actual_file, # See Note [Output comparison]. if whitespace_normaliser(expected_str) == whitespace_normaliser(actual_str): - return 1 + return True else: if config.verbose >= 1 and _expect_pass(way): print('Actual ' + kind + ' output differs from expected:') @@ -1568,29 +1577,39 @@ def compare_outputs(way, kind, normaliser, expected_file, actual_file, # See Note [Output comparison]. r = runCmd('diff -uw "{0}" "{1}"'.format(expected_normalised_path, actual_normalised_path), - print_output = 1) + print_output=True) # If for some reason there were no non-whitespace differences, # then do a full diff if r == 0: r = runCmd('diff -u "{0}" "{1}"'.format(expected_normalised_path, actual_normalised_path), - print_output = 1) + print_output=True) if config.accept and (getTestOpts().expect == 'fail' or way in getTestOpts().expect_fail_for): if_verbose(1, 'Test is expected to fail. Not accepting new output.') - return 0 + return False elif config.accept and actual_raw: - if_verbose(1, 'Accepting new output.') + if config.accept_platform: + if_verbose(1, 'Accepting new output for platform "' + + config.platform + '".') + expected_path += '-' + config.platform + elif config.accept_os: + if_verbose(1, 'Accepting new output for os "' + + config.os + '".') + expected_path += '-' + config.os + else: + if_verbose(1, 'Accepting new output.') + write_file(expected_path, actual_raw) - return 1 + return True elif config.accept: if_verbose(1, 'No output. Deleting "{0}".'.format(expected_path)) os.remove(expected_path) - return 1 + return True else: - return 0 + return False # Note [Output comparison] # @@ -1610,7 +1629,7 @@ def compare_outputs(way, kind, normaliser, expected_file, actual_file, def normalise_whitespace( str ): # Merge contiguous whitespace characters into a single space. - return ' '.join(w for w in str.split()) + return ' '.join(str.split()) callSite_re = re.compile(r', called at (.+):[\d]+:[\d]+ in [\w\-\.]+:') @@ -1730,6 +1749,7 @@ def normalise_prof (str): def normalise_slashes_( str ): str = re.sub('\\\\', '/', str) + str = re.sub('//', '/', str) return str def normalise_exe_( str ): @@ -1773,15 +1793,14 @@ def if_verbose( n, s ): if config.verbose >= n: print(s) -def if_verbose_dump( n, f ): - if config.verbose >= n: - try: - with io.open(f) as file: - print(file.read()) - except Exception: - print('') +def dump_file(f): + try: + with io.open(f) as file: + print(file.read()) + except Exception: + print('') -def runCmd(cmd, stdin=None, stdout=None, stderr=None, timeout_multiplier=1.0, print_output=0): +def runCmd(cmd, stdin=None, stdout=None, stderr=None, timeout_multiplier=1.0, print_output=False): timeout_prog = strip_quotes(config.timeout_prog) timeout = str(int(ceil(config.timeout * timeout_multiplier))) @@ -1789,9 +1808,6 @@ def runCmd(cmd, stdin=None, stdout=None, stderr=None, timeout_multiplier=1.0, pr cmd = cmd.format(**config.__dict__) if_verbose(3, cmd + ('< ' + os.path.basename(stdin) if stdin else '')) - # declare the buffers to a default - stdin_buffer = None - stdin_file = io.open(stdin, 'rb') if stdin else None stdout_buffer = b'' stderr_buffer = b'' @@ -1809,13 +1825,14 @@ def runCmd(cmd, stdin=None, stdout=None, stderr=None, timeout_multiplier=1.0, pr r = subprocess.Popen([timeout_prog, timeout, cmd], stdin=stdin_file, stdout=subprocess.PIPE, - stderr=hStdErr) + stderr=hStdErr, + env=ghc_env) stdout_buffer, stderr_buffer = r.communicate() finally: if stdin_file: stdin_file.close() - if config.verbose >= 1 and print_output >= 1: + if config.verbose >= 1 and print_output: if stdout_buffer: sys.stdout.buffer.write(stdout_buffer) if stderr_buffer: @@ -1848,7 +1865,7 @@ def gsNotWorking(): print("GhostScript not available for hp2ps tests") global gs_working -gs_working = 0 +gs_working = False if config.have_profiling: if config.gs != '': resultGood = runCmd(genGSCmd(config.confdir + '/good.ps')); @@ -1857,7 +1874,7 @@ if config.have_profiling: ' >/dev/null 2>&1') if resultBad != 0: print("GhostScript available for hp2ps tests") - gs_working = 1; + gs_working = True else: gsNotWorking(); else: @@ -1897,7 +1914,7 @@ def in_srcdir(name, suffix=''): # Finding the sample output. The filename is of the form # -# <test>.stdout[-ws-<wordsize>][-<platform>] +# <test>.stdout[-ws-<wordsize>][-<platform>|-<os>] # def find_expected_file(name, suff): basename = add_suffix(name, suff) @@ -1914,7 +1931,6 @@ def find_expected_file(name, suff): if config.msys: import stat - import time def cleanup(): testdir = getTestOpts().testdir max_attempts = 5 @@ -1968,7 +1984,7 @@ def findTFiles(roots): for root in roots: for path, dirs, files in os.walk(root, topdown=True): # Never pick up .T files in uncleaned .run directories. - dirs[:] = [dir for dir in sorted(dirs) + dirs[:] = [dir for dir in sorted(dirs) if not dir.endswith(testdir_suffix)] for filename in files: if filename.endswith('.T'): diff --git a/testsuite/driver/testutil.py b/testsuite/driver/testutil.py index dcba1777d1..15587e6960 100644 --- a/testsuite/driver/testutil.py +++ b/testsuite/driver/testutil.py @@ -1,4 +1,3 @@ -import errno import os import platform import subprocess @@ -11,8 +10,7 @@ def strip_quotes(s): return s.strip('\'"') def getStdout(cmd_and_args): - # Can't use subprocess.check_output as it's not available in Python 2.6; - # It's also not quite the same as check_output, since we also verify that + # Can't use subprocess.check_output, since we also verify that # no stderr was produced p = subprocess.Popen([strip_quotes(cmd_and_args[0])] + cmd_and_args[1:], stdout=subprocess.PIPE, @@ -23,16 +21,7 @@ def getStdout(cmd_and_args): raise Exception("Command failed: " + str(cmd_and_args)) if stderr: raise Exception("stderr from command: %s\nOutput:\n%s\n" % (cmd_and_args, stderr)) - return stdout - -def mkdirp(path): - try: - os.makedirs(path) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(path): - pass - else: - raise + return stdout.decode('utf-8') def lndir(srcdir, dstdir): # Create symlinks for all files in src directory. @@ -60,10 +49,6 @@ else: link_or_copy_file = os.symlink class Watcher(object): - global pool - global evt - global sync_lock - def __init__(self, count): self.pool = count self.evt = threading.Event() |