4 MultiTestRunner - Harness for running multiple tests in the simple clang style.
10 - Detect signalled failures (abort)
11 - Better support for finding tests
15 import os, sys, re, random, time
19 from TestRunner import TestStatus
20 from Queue import Queue
22 kTestFileExtensions = set(['.mi','.i','.c','.cpp','.m','.mm','.ll'])
26 if not os.path.exists(path):
27 print >>sys.stderr,"WARNING: Invalid test \"%s\""%(path,)
30 if os.path.isdir(path):
31 for dirpath,dirnames,filenames in os.walk(path):
32 dotTests = os.path.join(dirpath,'.tests')
33 if os.path.exists(dotTests):
34 for ln in open(dotTests):
36 yield os.path.join(dirpath,ln.strip())
38 # FIXME: This doesn't belong here
39 if 'Output' in dirnames:
40 dirnames.remove('Output')
42 base,ext = os.path.splitext(f)
43 if ext in kTestFileExtensions:
44 yield os.path.join(dirpath,f)
48 class TestingProgressDisplay:
49 def __init__(self, opts, numTests, progressBar=None):
51 self.numTests = numTests
52 self.digits = len(str(self.numTests))
54 self.lock = threading.Lock()
55 self.progressBar = progressBar
58 def update(self, index, tr):
59 # Avoid locking overhead in quiet mode
60 if self.opts.quiet and not tr.failed():
66 self.handleUpdate(index, tr)
72 self.progressBar.clear()
73 elif self.opts.succinct:
74 sys.stdout.write('\n')
76 def handleUpdate(self, index, tr):
79 self.progressBar.clear()
82 self.progress = max(self.progress, float(index)/self.numTests)
83 self.progressBar.update(self.progress, tr.path)
85 elif self.opts.succinct:
91 sys.stdout.write('\n')
94 if tr.code==TestStatus.Invalid:
95 extra = ' - (Invalid test)'
96 elif tr.code==TestStatus.NoRunLine:
97 extra = ' - (No RUN line)'
99 extra = ' - %s'%(TestStatus.getName(tr.code).upper(),)
100 print '%*d/%*d - %s%s'%(self.digits, index+1, self.digits,
101 self.numTests, tr.path, extra)
103 if tr.failed() and self.opts.showOutput:
104 TestRunner.cat(tr.testResults, sys.stdout)
107 def __init__(self, path, code, testResults):
110 self.testResults = testResults
113 return self.code in (TestStatus.Fail,TestStatus.XPass)
116 def __init__(self, opts, tests, display):
120 self.lock = threading.Lock()
121 self.results = [None]*len(self.tests)
122 self.startTime = time.time()
123 self.progress = display
128 if self.opts.maxTime is not None:
129 if time.time() - self.startTime > self.opts.maxTime:
131 if self.index >= len(self.tests):
133 item = self.tests[self.index],self.index
139 def setResult(self, index, result):
140 self.results[index] = result
141 self.progress.update(index, result)
143 class Tester(threading.Thread):
144 def __init__(self, provider):
145 threading.Thread.__init__(self)
146 self.provider = provider
150 item = self.provider.get()
155 def runTest(self, (path,index)):
157 # Use hand concatentation here because we want to override
159 output = 'Output/' + path + '.out'
161 testresults = 'Output/' + path + '.testresults'
162 TestRunner.mkdir_p(os.path.dirname(testresults))
163 numTests = len(self.provider.tests)
164 digits = len(str(numTests))
167 opts = self.provider.opts
168 if opts.debugDoNotTest:
171 code = TestRunner.runOneTest(path, command, output, testname,
172 opts.clang, opts.clangcc,
173 useValgrind=opts.useValgrind,
174 useDGCompat=opts.useDGCompat,
175 useScript=opts.testScript,
176 output=open(testresults,'w'))
177 except KeyboardInterrupt:
178 # This is a sad hack. Unfortunately subprocess goes
179 # bonkers with ctrl-c and we start forking merrily.
180 print 'Ctrl-C detected, goodbye.'
183 self.provider.setResult(index, TestResult(path, code, testresults))
187 Detects the number of CPUs on a system. Cribbed from pp.
189 # Linux, Unix and MacOS:
190 if hasattr(os, "sysconf"):
191 if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
193 ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
194 if isinstance(ncpus, int) and ncpus > 0:
197 return int(os.popen2("sysctl -n hw.ncpu")[1].read())
199 if os.environ.has_key("NUMBER_OF_PROCESSORS"):
200 ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
207 from optparse import OptionParser
208 parser = OptionParser("usage: %prog [options] {inputs}")
209 parser.add_option("-j", "--threads", dest="numThreads",
210 help="Number of testing threads",
211 type=int, action="store",
212 default=detectCPUs())
213 parser.add_option("", "--clang", dest="clang",
214 help="Program to use as \"clang\"",
215 action="store", default=None)
216 parser.add_option("", "--clang-cc", dest="clangcc",
217 help="Program to use as \"clang-cc\"",
218 action="store", default=None)
219 parser.add_option("", "--vg", dest="useValgrind",
220 help="Run tests under valgrind",
221 action="store_true", default=False)
222 parser.add_option("", "--dg", dest="useDGCompat",
223 help="Use llvm dejagnu compatibility mode",
224 action="store_true", default=False)
225 parser.add_option("", "--script", dest="testScript",
226 help="Default script to use",
227 action="store", default=None)
228 parser.add_option("-v", "--verbose", dest="showOutput",
229 help="Show all test output",
230 action="store_true", default=False)
231 parser.add_option("-q", "--quiet", dest="quiet",
232 help="Suppress no error output",
233 action="store_true", default=False)
234 parser.add_option("-s", "--succinct", dest="succinct",
235 help="Reduce amount of output",
236 action="store_true", default=False)
237 parser.add_option("", "--max-tests", dest="maxTests",
238 help="Maximum number of tests to run",
239 action="store", type=int, default=None)
240 parser.add_option("", "--max-time", dest="maxTime",
241 help="Maximum time to spend testing (in seconds)",
242 action="store", type=float, default=None)
243 parser.add_option("", "--shuffle", dest="shuffle",
244 help="Run tests in random order",
245 action="store_true", default=False)
246 parser.add_option("", "--seed", dest="seed",
247 help="Seed for random number generator (default: random)",
248 action="store", default=None)
249 parser.add_option("", "--no-progress-bar", dest="useProgressBar",
250 help="Do not use curses based progress bar",
251 action="store_false", default=True)
252 parser.add_option("", "--debug-do-not-test", dest="debugDoNotTest",
253 help="DEBUG: Skip running actual test script",
254 action="store_true", default=False)
255 parser.add_option("", "--path", dest="path",
256 help="Additional paths to add to testing environment",
257 action="store", type=str, default=None)
259 (opts, args) = parser.parse_args()
262 parser.error('No inputs specified')
264 if opts.clang is None:
265 opts.clang = TestRunner.inferClang()
266 if opts.clangcc is None:
267 opts.clangcc = TestRunner.inferClangCC(opts.clang)
269 # FIXME: It could be worth loading these in parallel with testing.
270 allTests = list(getTests(args))
274 if opts.seed is not None:
276 seed = int(opts.seed)
278 parser.error('--seed argument should be an integer')
281 random.shuffle(tests)
282 if opts.maxTests is not None:
283 tests = tests[:opts.maxTests]
284 if opts.path is not None:
285 os.environ["PATH"] = opts.path + ":" + os.environ["PATH"];
288 if len(tests) != len(allTests):
289 extra = ' of %d'%(len(allTests),)
290 header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
295 if opts.useProgressBar:
297 tc = ProgressBar.TerminalController()
298 progressBar = ProgressBar.ProgressBar(tc, header)
305 display = TestingProgressDisplay(opts, len(tests), progressBar)
306 provider = TestProvider(opts, tests, display)
308 testers = [Tester(provider) for i in range(opts.numThreads)]
309 startTime = time.time()
315 except KeyboardInterrupt:
321 print 'Testing Time: %.2fs'%(time.time() - startTime)
323 # List test results organized organized by kind.
325 for t in provider.results:
327 if t.code not in byCode:
329 byCode[t.code].append(t)
330 for title,code in (('Expected Failures', TestStatus.XFail),
331 ('Unexpected Passing Tests', TestStatus.XPass),
332 ('Failing Tests', TestStatus.Fail)):
333 elts = byCode.get(code)
337 print '%s (%d):' % (title, len(elts))
339 print '\t%s'%(tr.path,)
341 numFailures = len(byCode.get(TestStatus.Fail,[]))
343 print '\nFailures: %d' % (numFailures,)
346 if __name__=='__main__':