From cd43da82f88e4d77fc4f09813cf7312441c5c36f Mon Sep 17 00:00:00 2001
From: Craig Tiller <ctiller@google.com>
Date: Fri, 29 May 2015 08:41:29 -0700
Subject: [PATCH] Add fast stop

Allows run_tests to early out as soon as a test fails.
Plays well with -f.
---
 tools/run_tests/jobset.py    | 15 +++++++++++----
 tools/run_tests/run_tests.py |  5 +++++
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py
index a58071ee35..e2b03bd0ab 100755
--- a/tools/run_tests/jobset.py
+++ b/tools/run_tests/jobset.py
@@ -234,7 +234,8 @@ class Job(object):
 class Jobset(object):
   """Manages one run of jobs."""
 
-  def __init__(self, check_cancelled, maxjobs, newline_on_success, travis, cache):
+  def __init__(self, check_cancelled, maxjobs, newline_on_success, travis,
+               stop_on_failure, cache):
     self._running = set()
     self._check_cancelled = check_cancelled
     self._cancelled = False
@@ -244,6 +245,7 @@ class Jobset(object):
     self._newline_on_success = newline_on_success
     self._travis = travis
     self._cache = cache
+    self._stop_on_failure = stop_on_failure
 
   def start(self, spec):
     """Start a job. Return True on success, False on failure."""
@@ -280,8 +282,12 @@ class Jobset(object):
       for job in self._running:
         st = job.state(self._cache)
         if st == _RUNNING: continue
-        if st == _FAILURE: self._failures += 1
-        if st == _KILLED: self._failures += 1
+        if st == _FAILURE or st == _KILLED:
+          self._failures += 1
+          if self._stop_on_failure:
+            self._cancelled = True
+            for job in self._running:
+              job.kill()
         dead.add(job)
       for job in dead:
         self._completed += 1
@@ -333,10 +339,11 @@ def run(cmdlines,
         maxjobs=None,
         newline_on_success=False,
         travis=False,
+        stop_on_failure=False,
         cache=None):
   js = Jobset(check_cancelled,
               maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS,
-              newline_on_success, travis,
+              newline_on_success, travis, stop_on_failure,
               cache if cache is not None else NoCache())
   if not travis:
     cmdlines = shuffle_iteratable(cmdlines)
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 764e058aa5..76beb0b0a9 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -349,6 +349,10 @@ argp.add_argument('-l', '--language',
                   choices=['all'] + sorted(_LANGUAGES.keys()),
                   nargs='+',
                   default=['all'])
+argp.add_argument('-S', '--stop_on_failure',
+                  default=False,
+                  action='store_const',
+                  const=True)
 argp.add_argument('-a', '--antagonists', default=0, type=int)
 args = argp.parse_args()
 
@@ -457,6 +461,7 @@ def _build_and_run(check_cancelled, newline_on_success, travis, cache):
     if not jobset.run(all_runs, check_cancelled,
                       newline_on_success=newline_on_success, travis=travis,
                       maxjobs=args.jobs,
+                      stop_on_failure=args.stop_on_failure,
                       cache=cache):
       return 2
   finally:
-- 
GitLab