2010-05-21 Dirk Pranke <dpranke@chromium.org>
authordpranke@chromium.org <dpranke@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 May 2010 00:14:11 +0000 (00:14 +0000)
committerdpranke@chromium.org <dpranke@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 May 2010 00:14:11 +0000 (00:14 +0000)
        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:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@60381 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKitTools/ChangeLog
WebKitTools/Scripts/new-run-webkit-tests
WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py
WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py

index 3710a880dfef9bdacec714c8f45959e9e85a4afd..1e5246d62a7a343ecabf115ba9300a5f411a0453 100644 (file)
@@ -1,3 +1,18 @@
+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.
index 0728ee0a0f4388ea95333db7642e7993b23af83e..9fcacaa9d841b01f16478769aad681fbccdb544d 100755 (executable)
 # 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)
index 09f9ac79924bb690622ca0102d106f4c06f5124f..a2e209167dc6d9c4a20e9728408f44b29231b0a4 100644 (file)
@@ -258,6 +258,9 @@ class TestShellThread(threading.Thread):
             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()
@@ -298,7 +301,7 @@ class TestShellThread(threading.Thread):
 
         while True:
             if self._canceled:
-                _log.info('Testing canceled')
+                _log.debug('Testing cancelled')
                 tests_run_file.close()
                 return
 
index 77de2e036ca0ec2cb57c0c1bcc484bbf1f24f07b..f838a7b6be8cffc9a67646830775d9e15df44583 100644 (file)
@@ -272,24 +272,35 @@ class Printer(object):
     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):
index 380421021e874db0ca5a56a7edab33805eda93ad..c8648bcfc86f008e3494e9048411c8f1ba2090c6 100644 (file)
@@ -215,27 +215,34 @@ class  Testprinter(unittest.TestCase):
 
     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'])
index 6d5543d14daca2a896faf1d1a0fd89e986d2c29d..a4a92c7e808831d9eb70487e377da1ab0c093d92 100755 (executable)
@@ -58,6 +58,7 @@ import Queue
 import random
 import re
 import shutil
+import signal
 import sys
 import time
 import traceback
@@ -680,6 +681,7 @@ class TestRunner:
         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:
@@ -697,30 +699,30 @@ class TestRunner:
                     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."""
@@ -752,7 +754,8 @@ class TestRunner:
             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
@@ -760,12 +763,13 @@ class TestRunner:
         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)
 
@@ -782,7 +786,8 @@ class TestRunner:
         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)
@@ -800,6 +805,11 @@ class TestRunner:
         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']
@@ -1665,4 +1675,8 @@ def main():
     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)