diff options
author | Craig Tiller <ctiller@google.com> | 2015-09-24 08:58:39 -0700 |
---|---|---|
committer | Craig Tiller <ctiller@google.com> | 2015-09-24 08:58:39 -0700 |
commit | 91318bc256991c9946f450219721665148480cd8 (patch) | |
tree | 88eb611ec06ea20ff1778aa5aee2a160cc5b016f | |
parent | ee56eb60c8aa0e36ac33b00fbd4e70a61412d2e4 (diff) |
Add retries for flaky tests (enabled by default for now)
-rwxr-xr-x | tools/run_tests/jobset.py | 50 |
1 files changed, 31 insertions, 19 deletions
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py index 2a86319125..e696a0e969 100755 --- a/tools/run_tests/jobset.py +++ b/tools/run_tests/jobset.py @@ -81,6 +81,7 @@ _CLEAR_LINE = '\x1b[2K' _TAG_COLOR = { 'FAILED': 'red', + 'FLAKE': 'red', 'WARNING': 'yellow', 'TIMEOUT': 'red', 'PASSED': 'green', @@ -131,7 +132,7 @@ class JobSpec(object): """Specifies what to run for a job.""" def __init__(self, cmdline, shortname=None, environ=None, hash_targets=None, - cwd=None, shell=False, timeout_seconds=5*60): + cwd=None, shell=False, timeout_seconds=5*60, flake_retries=5): """ Arguments: cmdline: a list of arguments to pass as the command line @@ -150,6 +151,7 @@ class JobSpec(object): self.cwd = cwd self.shell = shell self.timeout_seconds = timeout_seconds + self.flake_retries = flake_retries def identity(self): return '%r %r %r' % (self.cmdline, self.environ, self.hash_targets) @@ -167,25 +169,28 @@ class Job(object): def __init__(self, spec, bin_hash, newline_on_success, travis, add_env, xml_report): self._spec = spec self._bin_hash = bin_hash + self._newline_on_success = newline_on_success + self._travis = travis + self._add_env = add_env.copy() + self._xml_test = ET.SubElement(xml_report, 'testcase', + name=self._spec.shortname) if xml_report is not None else None + self._retries = 0 + message('START', spec.shortname, do_newline=self._travis) + self.start() + + def start(self): self._tempfile = tempfile.TemporaryFile() - env = os.environ.copy() - for k, v in spec.environ.iteritems(): - env[k] = v - for k, v in add_env.iteritems(): - env[k] = v + env = dict(os.environ) + env.update(self._spec.environ) + env.update(self._add_env) self._start = time.time() - self._process = subprocess.Popen(args=spec.cmdline, + self._process = subprocess.Popen(args=self._spec.cmdline, stderr=subprocess.STDOUT, stdout=self._tempfile, - cwd=spec.cwd, - shell=spec.shell, + cwd=self._spec.cwd, + shell=self._spec.shell, env=env) self._state = _RUNNING - self._newline_on_success = newline_on_success - self._travis = travis - self._xml_test = ET.SubElement(xml_report, 'testcase', - name=self._spec.shortname) if xml_report is not None else None - message('START', spec.shortname, do_newline=self._travis) def state(self, update_cache): """Poll current state of the job. Prints messages at completion.""" @@ -202,15 +207,22 @@ class Job(object): self._xml_test.set('time', str(elapsed)) ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout if self._process.returncode != 0: - self._state = _FAILURE - message('FAILED', '%s [ret=%d, pid=%d]' % ( + if self._retries < self._spec.flake_retries: + message('FLAKE', '%s [ret=%d, pid=%d]' % ( self._spec.shortname, self._process.returncode, self._process.pid), stdout, do_newline=True) - if self._xml_test is not None: - ET.SubElement(self._xml_test, 'failure', message='Failure').text + self._retries += 1 + self.start() + else: + self._state = _FAILURE + message('FAILED', '%s [ret=%d, pid=%d]' % ( + self._spec.shortname, self._process.returncode, self._process.pid), + stdout, do_newline=True) + if self._xml_test is not None: + ET.SubElement(self._xml_test, 'failure', message='Failure').text else: self._state = _SUCCESS - message('PASSED', '%s [time=%.1fsec]' % (self._spec.shortname, elapsed), + message('PASSED', '%s [time=%.1fsec; retries=%d]' % (self._spec.shortname, elapsed, self._retries), do_newline=self._newline_on_success or self._travis) if self._bin_hash: update_cache.finished(self._spec.identity(), self._bin_hash) |