summaryrefslogtreecommitdiffstats
path: root/utils/test
diff options
context:
space:
mode:
Diffstat (limited to 'utils/test')
-rw-r--r--utils/test/Makefile.multi21
-rwxr-xr-xutils/test/MultiTestRunner.py331
-rw-r--r--utils/test/ProgressBar.py227
-rwxr-xr-xutils/test/TestRunner.py210
4 files changed, 789 insertions, 0 deletions
diff --git a/utils/test/Makefile.multi b/utils/test/Makefile.multi
new file mode 100644
index 0000000..3e9cd56
--- /dev/null
+++ b/utils/test/Makefile.multi
@@ -0,0 +1,21 @@
+LEVEL = ../../..
+include $(LEVEL)/Makefile.common
+
+# Test in all immediate subdirectories if unset.
+TESTDIRS ?= $(shell echo $(PROJ_SRC_DIR)/*/)
+
+ifndef TESTARGS
+ifdef VERBOSE
+TESTARGS = -v
+else
+TESTARGS = -s
+endif
+endif
+
+all::
+ @ PATH=$(ToolDir):$(LLVM_SRC_ROOT)/test/Scripts:$$PATH VG=$(VG) ../utils/test/MultiTestRunner.py $(TESTARGS) $(TESTDIRS)
+
+clean::
+ @ rm -rf Output/
+
+.PHONY: all report clean
diff --git a/utils/test/MultiTestRunner.py b/utils/test/MultiTestRunner.py
new file mode 100755
index 0000000..57650f9
--- /dev/null
+++ b/utils/test/MultiTestRunner.py
@@ -0,0 +1,331 @@
+#!/usr/bin/python
+
+"""
+MultiTestRunner - Harness for running multiple tests in the simple clang style.
+
+TODO
+--
+ - Fix Ctrl-c issues
+ - Use a timeout
+ - Detect signalled failures (abort)
+ - Better support for finding tests
+"""
+
+# TOD
+import os, sys, re, random, time
+import threading
+import ProgressBar
+import TestRunner
+from TestRunner import TestStatus
+from Queue import Queue
+
+kTestFileExtensions = set(['.mi','.i','.c','.cpp','.m','.mm','.ll'])
+
+def getTests(inputs):
+ for path in inputs:
+ if not os.path.exists(path):
+ print >>sys.stderr,"WARNING: Invalid test \"%s\""%(path,)
+ continue
+
+ if os.path.isdir(path):
+ for dirpath,dirnames,filenames in os.walk(path):
+ dotTests = os.path.join(dirpath,'.tests')
+ if os.path.exists(dotTests):
+ for ln in open(dotTests):
+ if ln.strip():
+ yield os.path.join(dirpath,ln.strip())
+ else:
+ # FIXME: This doesn't belong here
+ if 'Output' in dirnames:
+ dirnames.remove('Output')
+ for f in filenames:
+ base,ext = os.path.splitext(f)
+ if ext in kTestFileExtensions:
+ yield os.path.join(dirpath,f)
+ else:
+ yield path
+
+class TestingProgressDisplay:
+ def __init__(self, opts, numTests, progressBar=None):
+ self.opts = opts
+ self.numTests = numTests
+ self.digits = len(str(self.numTests))
+ self.current = None
+ self.lock = threading.Lock()
+ self.progressBar = progressBar
+ self.progress = 0.
+
+ def update(self, index, tr):
+ # Avoid locking overhead in quiet mode
+ if self.opts.quiet and not tr.failed():
+ return
+
+ # Output lock
+ self.lock.acquire()
+ try:
+ self.handleUpdate(index, tr)
+ finally:
+ self.lock.release()
+
+ def finish(self):
+ if self.progressBar:
+ self.progressBar.clear()
+ elif self.opts.succinct:
+ sys.stdout.write('\n')
+
+ def handleUpdate(self, index, tr):
+ if self.progressBar:
+ if tr.failed():
+ self.progressBar.clear()
+ else:
+ # Force monotonicity
+ self.progress = max(self.progress, float(index)/self.numTests)
+ self.progressBar.update(self.progress, tr.path)
+ return
+ elif self.opts.succinct:
+ if not tr.failed():
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ return
+ else:
+ sys.stdout.write('\n')
+
+ extra = ''
+ if tr.code==TestStatus.Invalid:
+ extra = ' - (Invalid test)'
+ elif tr.code==TestStatus.NoRunLine:
+ extra = ' - (No RUN line)'
+ elif tr.failed():
+ extra = ' - %s'%(TestStatus.getName(tr.code).upper(),)
+ print '%*d/%*d - %s%s'%(self.digits, index+1, self.digits,
+ self.numTests, tr.path, extra)
+
+ if tr.failed() and self.opts.showOutput:
+ TestRunner.cat(tr.testResults, sys.stdout)
+
+class TestResult:
+ def __init__(self, path, code, testResults):
+ self.path = path
+ self.code = code
+ self.testResults = testResults
+
+ def failed(self):
+ return self.code in (TestStatus.Fail,TestStatus.XPass)
+
+class TestProvider:
+ def __init__(self, opts, tests, display):
+ self.opts = opts
+ self.tests = tests
+ self.index = 0
+ self.lock = threading.Lock()
+ self.results = [None]*len(self.tests)
+ self.startTime = time.time()
+ self.progress = display
+
+ def get(self):
+ self.lock.acquire()
+ try:
+ if self.opts.maxTime is not None:
+ if time.time() - self.startTime > self.opts.maxTime:
+ return None
+ if self.index >= len(self.tests):
+ return None
+ item = self.tests[self.index],self.index
+ self.index += 1
+ return item
+ finally:
+ self.lock.release()
+
+ def setResult(self, index, result):
+ self.results[index] = result
+ self.progress.update(index, result)
+
+class Tester(threading.Thread):
+ def __init__(self, provider):
+ threading.Thread.__init__(self)
+ self.provider = provider
+
+ def run(self):
+ while 1:
+ item = self.provider.get()
+ if item is None:
+ break
+ self.runTest(item)
+
+ def runTest(self, (path,index)):
+ command = path
+ # Use hand concatentation here because we want to override
+ # absolute paths.
+ output = 'Output/' + path + '.out'
+ testname = path
+ testresults = 'Output/' + path + '.testresults'
+ TestRunner.mkdir_p(os.path.dirname(testresults))
+ numTests = len(self.provider.tests)
+ digits = len(str(numTests))
+ code = None
+ try:
+ opts = self.provider.opts
+ if opts.debugDoNotTest:
+ code = None
+ else:
+ code = TestRunner.runOneTest(path, command, output, testname,
+ opts.clang,
+ useValgrind=opts.useValgrind,
+ useDGCompat=opts.useDGCompat,
+ useScript=opts.testScript,
+ output=open(testresults,'w'))
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print 'Ctrl-C detected, goodbye.'
+ os.kill(0,9)
+
+ self.provider.setResult(index, TestResult(path, code, testresults))
+
+def detectCPUs():
+ """
+ Detects the number of CPUs on a system. Cribbed from pp.
+ """
+ # Linux, Unix and MacOS:
+ if hasattr(os, "sysconf"):
+ if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
+ # Linux & Unix:
+ ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
+ if isinstance(ncpus, int) and ncpus > 0:
+ return ncpus
+ else: # OSX:
+ return int(os.popen2("sysctl -n hw.ncpu")[1].read())
+ # Windows:
+ if os.environ.has_key("NUMBER_OF_PROCESSORS"):
+ ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
+ if ncpus > 0:
+ return ncpus
+ return 1 # Default
+
+def main():
+ global options
+ from optparse import OptionParser
+ parser = OptionParser("usage: %prog [options] {inputs}")
+ parser.add_option("-j", "--threads", dest="numThreads",
+ help="Number of testing threads",
+ type=int, action="store",
+ default=detectCPUs())
+ parser.add_option("", "--clang", dest="clang",
+ help="Program to use as \"clang\"",
+ action="store", default="clang")
+ parser.add_option("", "--vg", dest="useValgrind",
+ help="Run tests under valgrind",
+ action="store_true", default=False)
+ parser.add_option("", "--dg", dest="useDGCompat",
+ help="Use llvm dejagnu compatibility mode",
+ action="store_true", default=False)
+ parser.add_option("", "--script", dest="testScript",
+ help="Default script to use",
+ action="store", default=None)
+ parser.add_option("-v", "--verbose", dest="showOutput",
+ help="Show all test output",
+ action="store_true", default=False)
+ parser.add_option("-q", "--quiet", dest="quiet",
+ help="Suppress no error output",
+ action="store_true", default=False)
+ parser.add_option("-s", "--succinct", dest="succinct",
+ help="Reduce amount of output",
+ action="store_true", default=False)
+ parser.add_option("", "--max-tests", dest="maxTests",
+ help="Maximum number of tests to run",
+ action="store", type=int, default=None)
+ parser.add_option("", "--max-time", dest="maxTime",
+ help="Maximum time to spend testing (in seconds)",
+ action="store", type=float, default=None)
+ parser.add_option("", "--shuffle", dest="shuffle",
+ help="Run tests in random order",
+ action="store_true", default=False)
+ parser.add_option("", "--seed", dest="seed",
+ help="Seed for random number generator (default: random).",
+ action="store", default=None)
+ parser.add_option("", "--no-progress-bar", dest="useProgressBar",
+ help="Do not use curses based progress bar",
+ action="store_false", default=True)
+ parser.add_option("", "--debug-do-not-test", dest="debugDoNotTest",
+ help="DEBUG: Skip running actual test script",
+ action="store_true", default=False)
+ (opts, args) = parser.parse_args()
+
+ if not args:
+ parser.error('No inputs specified')
+
+ # FIXME: It could be worth loading these in parallel with testing.
+ allTests = list(getTests(args))
+ allTests.sort()
+
+ tests = allTests
+ if opts.seed is not None:
+ try:
+ seed = int(opts.seed)
+ except:
+ parser.error('--seed argument should be an integer')
+ random.seed(seed)
+ if opts.shuffle:
+ random.shuffle(tests)
+ if opts.maxTests is not None:
+ tests = tests[:opts.maxTests]
+
+ extra = ''
+ if len(tests) != len(allTests):
+ extra = ' of %d'%(len(allTests),)
+ header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,opts.numThreads)
+
+ progressBar = None
+ if not opts.quiet:
+ if opts.useProgressBar:
+ try:
+ tc = ProgressBar.TerminalController()
+ progressBar = ProgressBar.ProgressBar(tc, header)
+ except ValueError:
+ pass
+
+ if not progressBar:
+ print header
+
+ display = TestingProgressDisplay(opts, len(tests), progressBar)
+ provider = TestProvider(opts, tests, display)
+
+ testers = [Tester(provider) for i in range(opts.numThreads)]
+ startTime = time.time()
+ for t in testers:
+ t.start()
+ try:
+ for t in testers:
+ t.join()
+ except KeyboardInterrupt:
+ sys.exit(1)
+
+ display.finish()
+
+ if not opts.quiet:
+ print 'Testing Time: %.2fs'%(time.time() - startTime)
+
+ # List test results organized organized by kind.
+ byCode = {}
+ for t in provider.results:
+ if t:
+ if t.code not in byCode:
+ byCode[t.code] = []
+ byCode[t.code].append(t)
+ for title,code in (('Expected Failures', TestStatus.XFail),
+ ('Unexpected Passing Tests', TestStatus.XPass),
+ ('Failing Tests', TestStatus.Fail)):
+ elts = byCode.get(code)
+ if not elts:
+ continue
+ print '*'*20
+ print '%s (%d):' % (title, len(elts))
+ for tr in elts:
+ print '\t%s'%(tr.path,)
+
+ numFailures = len(byCode.get(TestStatus.Fail,[]))
+ if numFailures:
+ print '\nFailures: %d' % (numFailures,)
+
+if __name__=='__main__':
+ main()
diff --git a/utils/test/ProgressBar.py b/utils/test/ProgressBar.py
new file mode 100644
index 0000000..2e1f24a
--- /dev/null
+++ b/utils/test/ProgressBar.py
@@ -0,0 +1,227 @@
+#!/usr/bin/python
+
+# Source: http://code.activestate.com/recipes/475116/, with
+# modifications by Daniel Dunbar.
+
+import sys, re, time
+
+class TerminalController:
+ """
+ A class that can be used to portably generate formatted output to
+ a terminal.
+
+ `TerminalController` defines a set of instance variables whose
+ values are initialized to the control sequence necessary to
+ perform a given action. These can be simply included in normal
+ output to the terminal:
+
+ >>> term = TerminalController()
+ >>> print 'This is '+term.GREEN+'green'+term.NORMAL
+
+ Alternatively, the `render()` method can used, which replaces
+ '${action}' with the string required to perform 'action':
+
+ >>> term = TerminalController()
+ >>> print term.render('This is ${GREEN}green${NORMAL}')
+
+ If the terminal doesn't support a given action, then the value of
+ the corresponding instance variable will be set to ''. As a
+ result, the above code will still work on terminals that do not
+ support color, except that their output will not be colored.
+ Also, this means that you can test whether the terminal supports a
+ given action by simply testing the truth value of the
+ corresponding instance variable:
+
+ >>> term = TerminalController()
+ >>> if term.CLEAR_SCREEN:
+ ... print 'This terminal supports clearning the screen.'
+
+ Finally, if the width and height of the terminal are known, then
+ they will be stored in the `COLS` and `LINES` attributes.
+ """
+ # Cursor movement:
+ BOL = '' #: Move the cursor to the beginning of the line
+ UP = '' #: Move the cursor up one line
+ DOWN = '' #: Move the cursor down one line
+ LEFT = '' #: Move the cursor left one char
+ RIGHT = '' #: Move the cursor right one char
+
+ # Deletion:
+ CLEAR_SCREEN = '' #: Clear the screen and move to home position
+ CLEAR_EOL = '' #: Clear to the end of the line.
+ CLEAR_BOL = '' #: Clear to the beginning of the line.
+ CLEAR_EOS = '' #: Clear to the end of the screen
+
+ # Output modes:
+ BOLD = '' #: Turn on bold mode
+ BLINK = '' #: Turn on blink mode
+ DIM = '' #: Turn on half-bright mode
+ REVERSE = '' #: Turn on reverse-video mode
+ NORMAL = '' #: Turn off all modes
+
+ # Cursor display:
+ HIDE_CURSOR = '' #: Make the cursor invisible
+ SHOW_CURSOR = '' #: Make the cursor visible
+
+ # Terminal size:
+ COLS = None #: Width of the terminal (None for unknown)
+ LINES = None #: Height of the terminal (None for unknown)
+
+ # Foreground colors:
+ BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
+
+ # Background colors:
+ BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
+ BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
+
+ _STRING_CAPABILITIES = """
+ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
+ CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
+ BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
+ HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
+ _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
+ _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
+
+ def __init__(self, term_stream=sys.stdout):
+ """
+ Create a `TerminalController` and initialize its attributes
+ with appropriate values for the current terminal.
+ `term_stream` is the stream that will be used for terminal
+ output; if this stream is not a tty, then the terminal is
+ assumed to be a dumb terminal (i.e., have no capabilities).
+ """
+ # Curses isn't available on all platforms
+ try: import curses
+ except: return
+
+ # If the stream isn't a tty, then assume it has no capabilities.
+ if not term_stream.isatty(): return
+
+ # Check the terminal type. If we fail, then assume that the
+ # terminal has no capabilities.
+ try: curses.setupterm()
+ except: return
+
+ # Look up numeric capabilities.
+ self.COLS = curses.tigetnum('cols')
+ self.LINES = curses.tigetnum('lines')
+
+ # Look up string capabilities.
+ for capability in self._STRING_CAPABILITIES:
+ (attrib, cap_name) = capability.split('=')
+ setattr(self, attrib, self._tigetstr(cap_name) or '')
+
+ # Colors
+ set_fg = self._tigetstr('setf')
+ if set_fg:
+ for i,color in zip(range(len(self._COLORS)), self._COLORS):
+ setattr(self, color, curses.tparm(set_fg, i) or '')
+ set_fg_ansi = self._tigetstr('setaf')
+ if set_fg_ansi:
+ for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+ setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
+ set_bg = self._tigetstr('setb')
+ if set_bg:
+ for i,color in zip(range(len(self._COLORS)), self._COLORS):
+ setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
+ set_bg_ansi = self._tigetstr('setab')
+ if set_bg_ansi:
+ for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+ setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
+
+ def _tigetstr(self, cap_name):
+ # String capabilities can include "delays" of the form "$<2>".
+ # For any modern terminal, we should be able to just ignore
+ # these, so strip them out.
+ import curses
+ cap = curses.tigetstr(cap_name) or ''
+ return re.sub(r'\$<\d+>[/*]?', '', cap)
+
+ def render(self, template):
+ """
+ Replace each $-substitutions in the given template string with
+ the corresponding terminal control string (if it's defined) or
+ '' (if it's not).
+ """
+ return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
+
+ def _render_sub(self, match):
+ s = match.group()
+ if s == '$$': return s
+ else: return getattr(self, s[2:-1])
+
+#######################################################################
+# Example use case: progress bar
+#######################################################################
+
+class ProgressBar:
+ """
+ A 3-line progress bar, which looks like::
+
+ Header
+ 20% [===========----------------------------------]
+ progress message
+
+ The progress bar is colored, if the terminal supports color
+ output; and adjusts to the width of the terminal.
+ """
+ BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s\n'
+ HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
+
+ def __init__(self, term, header, useETA=True):
+ self.term = term
+ if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
+ raise ValueError("Terminal isn't capable enough -- you "
+ "should use a simpler progress dispaly.")
+ self.width = self.term.COLS or 75
+ self.bar = term.render(self.BAR)
+ self.header = self.term.render(self.HEADER % header.center(self.width))
+ self.cleared = 1 #: true if we haven't drawn the bar yet.
+ self.useETA = useETA
+ if self.useETA:
+ self.startTime = time.time()
+ self.update(0, '')
+
+ def update(self, percent, message):
+ if self.cleared:
+ sys.stdout.write(self.header)
+ self.cleared = 0
+ prefix = '%3d%% ' % (percent*100,)
+ suffix = ''
+ if self.useETA:
+ elapsed = time.time() - self.startTime
+ if percent > .0001 and elapsed > 1:
+ total = elapsed / percent
+ eta = int(total - elapsed)
+ h = eta//3600.
+ m = (eta//60) % 60
+ s = eta % 60
+ suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
+ barWidth = self.width - len(prefix) - len(suffix) - 2
+ n = int(barWidth*percent)
+ if len(message) < self.width:
+ message = message + ' '*(self.width - len(message))
+ else:
+ message = '... ' + message[-(self.width-4):]
+ sys.stdout.write(
+ self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
+ (self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
+ self.term.CLEAR_EOL + message)
+
+ def clear(self):
+ if not self.cleared:
+ sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL +
+ self.term.UP + self.term.CLEAR_EOL)
+ self.cleared = 1
+
+def test():
+ import time
+ tc = TerminalController()
+ p = ProgressBar(tc, 'Tests')
+ for i in range(101):
+ p.update(i/100., str(i))
+ time.sleep(.3)
+
+if __name__=='__main__':
+ test()
diff --git a/utils/test/TestRunner.py b/utils/test/TestRunner.py
new file mode 100755
index 0000000..1cb8b9d
--- /dev/null
+++ b/utils/test/TestRunner.py
@@ -0,0 +1,210 @@
+#!/usr/bin/python
+#
+# TestRunner.py - This script is used to run arbitrary unit tests. Unit
+# tests must contain the command used to run them in the input file, starting
+# immediately after a "RUN:" string.
+#
+# This runner recognizes and replaces the following strings in the command:
+#
+# %s - Replaced with the input name of the program, or the program to
+# execute, as appropriate.
+# %S - Replaced with the directory where the input resides.
+# %llvmgcc - llvm-gcc command
+# %llvmgxx - llvm-g++ command
+# %prcontext - prcontext.tcl script
+# %t - temporary file name (derived from testcase name)
+#
+
+import os
+import sys
+import subprocess
+import errno
+import re
+
+class TestStatus:
+ Pass = 0
+ XFail = 1
+ Fail = 2
+ XPass = 3
+ NoRunLine = 4
+ Invalid = 5
+
+ kNames = ['Pass','XFail','Fail','XPass','NoRunLine','Invalid']
+ @staticmethod
+ def getName(code): return TestStatus.kNames[code]
+
+def mkdir_p(path):
+ if not path:
+ pass
+ elif os.path.exists(path):
+ pass
+ else:
+ parent = os.path.dirname(path)
+ if parent != path:
+ mkdir_p(parent)
+ try:
+ os.mkdir(path)
+ except OSError,e:
+ if e.errno != errno.EEXIST:
+ raise
+
+def remove(path):
+ try:
+ os.remove(path)
+ except OSError:
+ pass
+
+def cat(path, output):
+ f = open(path)
+ output.writelines(f)
+ f.close()
+
+def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG,
+ useValgrind=False,
+ useDGCompat=False,
+ useScript=None,
+ output=sys.stdout):
+ if useValgrind:
+ VG_OUTPUT = '%s.vg'%(OUTPUT,)
+ if os.path.exists:
+ remove(VG_OUTPUT)
+ CLANG = 'valgrind --leak-check=full --quiet --log-file=%s %s'%(VG_OUTPUT, CLANG)
+
+ # Create the output directory if it does not already exist.
+ mkdir_p(os.path.dirname(OUTPUT))
+
+ # FIXME
+ #ulimit -t 40
+
+ # FIXME: Load script once
+ # FIXME: Support "short" script syntax
+
+ if useScript:
+ scriptFile = useScript
+ else:
+ # See if we have a per-dir test script.
+ dirScriptFile = os.path.join(os.path.dirname(FILENAME), 'test.script')
+ if os.path.exists(dirScriptFile):
+ scriptFile = dirScriptFile
+ else:
+ scriptFile = FILENAME
+
+ # Verify the script contains a run line.
+ for ln in open(scriptFile):
+ if 'RUN:' in ln:
+ break
+ else:
+ print >>output, "******************** TEST '%s' HAS NO RUN LINE! ********************"%(TESTNAME,)
+ output.flush()
+ return TestStatus.NoRunLine
+
+ OUTPUT = os.path.abspath(OUTPUT)
+ FILENAME = os.path.abspath(FILENAME)
+ SCRIPT = OUTPUT + '.script'
+ TEMPOUTPUT = OUTPUT + '.tmp'
+
+ substitutions = [('%s',SUBST),
+ ('%S',os.path.dirname(SUBST)),
+ ('%llvmgcc','llvm-gcc -emit-llvm -w'),
+ ('%llvmgxx','llvm-g++ -emit-llvm -w'),
+ ('%prcontext','prcontext.tcl'),
+ ('%t',TEMPOUTPUT),
+ ('clang',CLANG)]
+ scriptLines = []
+ xfailLines = []
+ for ln in open(scriptFile):
+ if 'RUN:' in ln:
+ # Isolate run parameters
+ index = ln.index('RUN:')
+ ln = ln[index+4:]
+
+ # Apply substitutions
+ for a,b in substitutions:
+ ln = ln.replace(a,b)
+
+ if useDGCompat:
+ ln = re.sub(r'\{(.*)\}', r'"\1"', ln)
+ scriptLines.append(ln)
+ elif 'XFAIL' in ln:
+ xfailLines.append(ln)
+
+ if xfailLines:
+ print >>output, "XFAILED '%s':"%(TESTNAME,)
+ output.writelines(xfailLines)
+
+ # Write script file
+ f = open(SCRIPT,'w')
+ f.write(''.join(scriptLines))
+ f.close()
+
+ outputFile = open(OUTPUT,'w')
+ p = None
+ try:
+ p = subprocess.Popen(["/bin/sh",SCRIPT],
+ cwd=os.path.dirname(FILENAME),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out,err = p.communicate()
+ outputFile.write(out)
+ outputFile.write(err)
+ SCRIPT_STATUS = p.wait()
+ except KeyboardInterrupt:
+ if p is not None:
+ os.kill(p.pid)
+ raise
+ outputFile.close()
+
+ if xfailLines:
+ SCRIPT_STATUS = not SCRIPT_STATUS
+
+ if useValgrind:
+ VG_STATUS = len(list(open(VG_OUTPUT)))
+ else:
+ VG_STATUS = 0
+
+ if SCRIPT_STATUS or VG_STATUS:
+ print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
+ print >>output, "Command: "
+ output.writelines(scriptLines)
+ if not SCRIPT_STATUS:
+ print >>output, "Output:"
+ else:
+ print >>output, "Incorrect Output:"
+ cat(OUTPUT, output)
+ if VG_STATUS:
+ print >>output, "Valgrind Output:"
+ cat(VG_OUTPUT, output)
+ print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
+ output.flush()
+ if xfailLines:
+ return TestStatus.XPass
+ else:
+ return TestStatus.Fail
+
+ if xfailLines:
+ return TestStatus.XFail
+ else:
+ return TestStatus.Pass
+
+def main():
+ _,path = sys.argv
+ command = path
+ # Use hand concatentation here because we want to override
+ # absolute paths.
+ output = 'Output/' + path + '.out'
+ testname = path
+
+ # Determine which clang to use.
+ CLANG = os.getenv('CLANG')
+ if not CLANG:
+ CLANG = 'clang'
+
+ res = runOneTest(path, command, output, testname, CLANG,
+ useValgrind=bool(os.getenv('VG')),
+ useDGCompat=bool(os.getenv('DG_COMPAT')),
+ useScript=os.getenv("TEST_SCRIPT"))
+
+ sys.exit(res == TestStatus.Fail or res == TestStatus.XPass)
+
+if __name__=='__main__':
+ main()
OpenPOWER on IntegriCloud