+2010-05-21 Dirk Pranke <dpranke@chromium.org>
+
+ Reviewed by Ojan Vafai.
+
+ new-run-webkit-tests: fix handling of Ctrl-C to exit even if some
+ threads are wedged. Also, the script will print the results of the
+ tests completed when the interrupt occurs.
+
+ https://bugs.webkit.org/show_bug.cgi?id=33238
+
+ * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py:
+ * Scripts/webkitpy/layout_tests/layout_package/printing.py:
+ * Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py:
+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py:
+
2010-05-28 Darin Adler <darin@apple.com>
Ignore more Python messiness.
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Wrapper around webkitpy/layout_tests/run_webkit_tests.py"""
+import signal
import sys
import webkitpy.layout_tests.run_webkit_tests as run_webkit_tests
if __name__ == '__main__':
- sys.exit(run_webkit_tests.main())
+ try:
+ sys.exit(run_webkit_tests.main())
+ except KeyboardInterrupt:
+ # this mirrors what the shell normally does
+ sys.exit(signal.SIGINT + 128)
self._run(test_runner=None, result_summary=None)
_log.debug('%s done (%d tests)' % (self.getName(),
self.get_num_tests()))
+ except KeyboardInterrupt:
+ self._exception_info = sys.exc_info()
+ _log.debug("%s interrupted" % self.getName())
except:
# Save the exception for our caller to see.
self._exception_info = sys.exc_info()
while True:
if self._canceled:
- _log.info('Testing canceled')
+ _log.debug('Testing cancelled')
tests_run_file.close()
return
def print_timing(self, msg):
self.write(msg, 'timing')
- def print_one_line_summary(self, total, expected):
+ def print_one_line_summary(self, total, expected, unexpected):
"""Print a one-line summary of the test run to stdout.
Args:
total: total number of tests run
expected: number of expected results
+ unexpected: number of unexpected results
"""
if self.disabled('one-line-summary'):
return
- unexpected = total - expected
+ incomplete = total - expected - unexpected
+ if incomplete:
+ self._write("")
+ incomplete_str = " (%d didn't run)" % incomplete
+ expected_str = str(expected)
+ else:
+ incomplete_str = ""
+ expected_str = "All %d" % expected
+
if unexpected == 0:
- self._write("All %d tests ran as expected." % expected)
+ self._write("%s tests ran as expected%s." %
+ (expected_str, incomplete_str))
elif expected == 1:
- self._write("1 test ran as expected, %d didn't:" % unexpected)
+ self._write("1 test ran as expected, %d didn't%s:" %
+ (unexpected, incomplete_str))
else:
- self._write("%d tests ran as expected, %d didn't:" %
- (expected, unexpected))
+ self._write("%d tests ran as expected, %d didn't%s:" %
+ (expected, unexpected, incomplete_str))
self._write("")
def print_test_result(self, result, expected, exp_str, got_str):
def test_print_one_line_summary(self):
printer, err, out = self.get_printer(['--print', 'nothing'])
- printer.print_one_line_summary(1, 1)
+ printer.print_one_line_summary(1, 1, 0)
self.assertTrue(err.empty())
printer, err, out = self.get_printer(['--print', 'one-line-summary'])
- printer.print_one_line_summary(1, 1)
+ printer.print_one_line_summary(1, 1, 0)
self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"])
printer, err, out = self.get_printer(['--print', 'everything'])
- printer.print_one_line_summary(1, 1)
+ printer.print_one_line_summary(1, 1, 0)
self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"])
err.reset()
- printer.print_one_line_summary(2, 1)
+ printer.print_one_line_summary(2, 1, 1)
self.assertEquals(err.get(),
["1 test ran as expected, 1 didn't:\n", "\n"])
err.reset()
- printer.print_one_line_summary(3, 2)
+ printer.print_one_line_summary(3, 2, 1)
self.assertEquals(err.get(),
["2 tests ran as expected, 1 didn't:\n", "\n"])
+ err.reset()
+ printer.print_one_line_summary(3, 2, 0)
+ self.assertEquals(err.get(),
+ ['\n', "2 tests ran as expected (1 didn't run).\n",
+ '\n'])
+
+
def test_print_test_result(self):
result = get_result('foo.html')
printer, err, out = self.get_printer(['--print', 'nothing'])
import random
import re
import shutil
+import signal
import sys
import time
import traceback
test_timings = {}
individual_test_timings = []
thread_timings = []
+ keyboard_interrupted = False
try:
# Loop through all the threads waiting for them to finish.
for thread in threads:
self._dump_thread_states_if_necessary()
self.update_summary(result_summary)
- # This thread is done, save off the timing information.
- thread_timings.append({'name': thread.getName(),
- 'num_tests': thread.get_num_tests(),
- 'total_time': thread.get_total_time()})
- test_timings.update(thread.get_directory_timing_stats())
- individual_test_timings.extend(thread.get_test_results())
except KeyboardInterrupt:
+ keyboard_interrupted = True
for thread in threads:
thread.cancel()
- raise
+
+ if not keyboard_interrupted:
+ for thread in threads:
+ # Check whether a thread died before normal completion.
+ exception_info = thread.get_exception_info()
+ if exception_info is not None:
+ # Re-raise the thread's exception here to make it clear
+ # something went wrong. Otherwise, the tests that did not
+ # run would be assumed to have passed.
+ raise (exception_info[0], exception_info[1],
+ exception_info[2])
+
for thread in threads:
- # Check whether a TestShellThread died before normal completion.
- exception_info = thread.get_exception_info()
- if exception_info is not None:
- # Re-raise the thread's exception here to make it clear that
- # testing was aborted. Otherwise, the tests that did not run
- # would be assumed to have passed.
- raise exception_info[0], exception_info[1], exception_info[2]
-
- # FIXME: This update_summary call seems unecessary.
- # Calls are already made right after join() above,
- # as well as from the individual threads themselves.
- self.update_summary(result_summary)
- return (thread_timings, test_timings, individual_test_timings)
+ thread_timings.append({'name': thread.getName(),
+ 'num_tests': thread.get_num_tests(),
+ 'total_time': thread.get_total_time()})
+ test_timings.update(thread.get_directory_timing_stats())
+ individual_test_timings.extend(thread.get_test_results())
+ return (keyboard_interrupted, thread_timings, test_timings,
+ individual_test_timings)
def needs_http(self):
"""Returns whether the test runner needs an HTTP server."""
self._port.start_websocket_server()
# self._websocket_secure_server.Start()
- thread_timings, test_timings, individual_test_timings = (
+ keyboard_interrupted, thread_timings, test_timings, \
+ individual_test_timings = (
self._run_tests(self._test_files_list, result_summary))
# We exclude the crashes from the list of results to retry, because
failures = self._get_failures(result_summary, include_crashes=False)
retry_summary = result_summary
while (len(failures) and self._options.retry_failures and
- not self._retrying):
+ not self._retrying and not keyboard_interrupted):
_log.info('')
_log.info("Retrying %d unexpected failure(s) ..." % len(failures))
_log.info('')
self._retrying = True
retry_summary = ResultSummary(self._expectations, failures.keys())
+ # Note that we intentionally ignore the return value here.
self._run_tests(failures.keys(), retry_summary)
failures = self._get_failures(retry_summary, include_crashes=True)
sys.stderr.flush()
self._printer.print_one_line_summary(result_summary.total,
- result_summary.expected)
+ result_summary.expected,
+ result_summary.unexpected)
unexpected_results = summarize_unexpected_results(self._port,
self._expectations, result_summary, retry_summary)
if self._options.show_results and wrote_results:
self._show_results_html_file()
+ # Now that we've completed all the processing we can, we re-raise
+ # a KeyboardInterrupt if necessary so the caller can handle it.
+ if keyboard_interrupted:
+ raise KeyboardInterrupt
+
# Ignore flaky failures and unexpected passes so we don't turn the
# bot red for those.
return unexpected_results['num_regressions']
return run(port_obj, options, args)
if '__main__' == __name__:
- sys.exit(main())
+ try:
+ sys.exit(main())
+ except KeyboardInterrupt:
+ # this mirrors what the shell normally does
+ sys.exit(signal.SIGINT + 128)