# HG changeset patch # User Christian Ebert # Date 1352899500 0 # Node ID 0928d50a0abef37cc6fe718a969aabe12599c1c5 # Parent 337e1ceae82ebbc88e9e57de900b9a8eb7de18ba# Parent ce884838a9558b646721366ad75dcb2b02dbf39f Merge with stable diff -r 337e1ceae82e -r 0928d50a0abe tests/run-tests.py --- a/tests/run-tests.py Thu Nov 08 18:35:43 2012 +0000 +++ b/tests/run-tests.py Wed Nov 14 13:25:00 2012 +0000 @@ -55,6 +55,7 @@ import re import threading import killdaemons as killmod +import cPickle as pickle processlock = threading.Lock() @@ -175,6 +176,8 @@ parser.add_option("-t", "--timeout", type="int", help="kill errant tests after TIMEOUT seconds" " (default: $%s or %d)" % defaults['timeout']) + parser.add_option("--time", action="store_true", + help="time how long each test takes") parser.add_option("--tmpdir", type="string", help="run tests in the given temporary directory" " (implies --keep-tmpdir)") @@ -263,6 +266,10 @@ sys.stderr.write( 'warning: --timeout option ignored with --debug\n') options.timeout = 0 + if options.time: + sys.stderr.write( + 'warning: --time option ignored with --debug\n') + options.time = False if options.py3k_warnings: if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0): parser.error('--py3k-warnings can only be used on Python 2.6+') @@ -447,6 +454,14 @@ fn = os.path.join(INST, '..', '.coverage') os.environ['COVERAGE_FILE'] = fn +def outputtimes(options): + vlog('# Producing time report') + times.sort(key=lambda t: (t[1], t[0]), reverse=True) + cols = '%7.3f %s' + print '\n%-7s %s' % ('Time', 'Test') + for test, timetaken in times: + print cols % (timetaken, test) + def outputcoverage(options): vlog('# Producing coverage report') @@ -753,18 +768,13 @@ True -> passed False -> failed''' - global results, resultslock, iolock + global results, iolock testpath = os.path.join(TESTDIR, test) - def result(l, e): - resultslock.acquire() - results[l].append(e) - resultslock.release() - def skip(msg): if not options.verbose: - result('s', (test, msg)) + results['s'].append((test, msg)) else: iolock.acquire() print "\nSkipping %s: %s" % (testpath, msg) @@ -787,15 +797,15 @@ rename(testpath + ".err", testpath) else: rename(testpath + ".err", testpath + ".out") - result('p', test) + success(test) return - result('f', (test, msg)) + results['f'].append((test, msg)) def success(): - result('p', test) + results['p'].append(test) def ignore(msg): - result('i', (test, msg)) + results['i'].append((test, msg)) if (os.path.basename(test).startswith("test-") and '~' not in test and ('.' not in test or test.endswith('.py') or @@ -891,7 +901,12 @@ replacements.append((re.escape(testtmp), '$TESTTMP')) os.mkdir(testtmp) + if options.time: + starttime = time.time() ret, out = runner(testpath, testtmp, options, replacements) + if options.time: + endtime = time.time() + times.append((test, endtime - starttime)) vlog("# Ret was:", ret) mark = '.' @@ -1056,29 +1071,30 @@ childopts += ['--tmpdir', childtmp] cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job vlog(' '.join(cmdline)) - fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r') + fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'rb') os.close(wfd) signal.signal(signal.SIGINT, signal.SIG_IGN) failures = 0 - tested, skipped, failed = 0, 0, 0 + passed, skipped, failed = 0, 0, 0 skips = [] fails = [] while fps: pid, status = os.wait() fp = fps.pop(pid) - l = fp.read().splitlines() try: - test, skip, fail = map(int, l[:3]) - except ValueError: - test, skip, fail = 0, 0, 0 - split = -fail or len(l) - for s in l[3:split]: - skips.append(s.split(" ", 1)) - for s in l[split:]: - fails.append(s.split(" ", 1)) - tested += test - skipped += skip - failed += fail + childresults = pickle.load(fp) + except pickle.UnpicklingError: + pass + else: + passed += len(childresults['p']) + skipped += len(childresults['s']) + failed += len(childresults['f']) + skips.extend(childresults['s']) + fails.extend(childresults['f']) + if options.time: + childtimes = pickle.load(fp) + times.extend(childtimes) + vlog('pid %d exited, status %d' % (pid, status)) failures |= status print @@ -1093,17 +1109,19 @@ _checkhglib("Tested") print "# Ran %d tests, %d skipped, %d failed." % ( - tested, skipped, failed) + passed + failed, skipped, failed) + if options.time: + outputtimes(options) if options.anycoverage: outputcoverage(options) sys.exit(failures != 0) results = dict(p=[], f=[], s=[], i=[]) -resultslock = threading.Lock() +times = [] iolock = threading.Lock() -def runqueue(options, tests, results): +def runqueue(options, tests): for test in tests: ret = runone(options, test) if options.first and ret is not None and not ret: @@ -1129,7 +1147,7 @@ print "running all tests" tests = orig - runqueue(options, tests, results) + runqueue(options, tests) failed = len(results['f']) tested = len(results['p']) + failed @@ -1137,12 +1155,10 @@ ignored = len(results['i']) if options.child: - fp = os.fdopen(options.child, 'w') - fp.write('%d\n%d\n%d\n' % (tested, skipped, failed)) - for s in results['s']: - fp.write("%s %s\n" % s) - for s in results['f']: - fp.write("%s %s\n" % s) + fp = os.fdopen(options.child, 'wb') + pickle.dump(results, fp, pickle.HIGHEST_PROTOCOL) + if options.time: + pickle.dump(times, fp, pickle.HIGHEST_PROTOCOL) fp.close() else: print @@ -1153,6 +1169,8 @@ _checkhglib("Tested") print "# Ran %d tests, %d skipped, %d failed." % ( tested, skipped + ignored, failed) + if options.time: + outputtimes(options) if options.anycoverage: outputcoverage(options)