Index: tools/sharding_supervisor/sharding_supervisor_old.py |
diff --git a/tools/sharding_supervisor/sharding_supervisor_old.py b/tools/sharding_supervisor/sharding_supervisor_old.py |
deleted file mode 100755 |
index 717efd047c9104008a627205f5528963570eeb29..0000000000000000000000000000000000000000 |
--- a/tools/sharding_supervisor/sharding_supervisor_old.py |
+++ /dev/null |
@@ -1,670 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Shards a given test suite and runs the shards in parallel. |
- |
-ShardingSupervisor is called to process the command line options and creates |
-the specified number of worker threads. These threads then run each shard of |
-the test in a separate process and report on the results. When all the shards |
-have been completed, the supervisor reprints any lines indicating a test |
-failure for convenience. If only one shard is to be run, a single subprocess |
-is started for that shard and the output is identical to gtest's output. |
-""" |
- |
-import itertools |
-import optparse |
-import os |
-import Queue |
-import random |
-import re |
-import sys |
-import threading |
- |
-from stdio_buffer import StdioBuffer |
-from xml.dom import minidom |
- |
-# Add tools/ to path |
-BASE_PATH = os.path.dirname(os.path.abspath(__file__)) |
-sys.path.append(os.path.join(BASE_PATH, "..")) |
-try: |
- import find_depot_tools # pylint: disable=F0401,W0611 |
- # Fixes a bug in Windows where some shards die upon starting |
- # TODO(charleslee): actually fix this bug |
- import subprocess2 as subprocess |
-except ImportError: |
- # Unable to find depot_tools, so just use standard subprocess |
- import subprocess |
- |
-SS_USAGE = "python %prog [options] path/to/test [gtest_args]" |
-SS_DEFAULT_NUM_CORES = 4 |
-SS_DEFAULT_SHARDS_PER_CORE = 5 # num_shards = cores * SHARDS_PER_CORE |
-SS_DEFAULT_RUNS_PER_CORE = 1 # num_workers = cores * RUNS_PER_CORE |
-SS_DEFAULT_RETRY_PERCENT = 5 # --retry-failed ignored if more than 5% fail |
-SS_DEFAULT_TIMEOUT = 530 # Slightly less than buildbot's default 600 seconds |
- |
- |
-def DetectNumCores(): |
- """Detects the number of cores on the machine. |
- |
- Returns: |
- The number of cores on the machine or DEFAULT_NUM_CORES if it could not |
- be found. |
- """ |
- try: |
- # Override on some Chromium Valgrind bots. |
- if "CHROME_VALGRIND_NUMCPUS" in os.environ: |
- return int(os.environ["CHROME_VALGRIND_NUMCPUS"]) |
- # Linux, Unix, MacOS |
- if hasattr(os, "sysconf"): |
- if "SC_NPROCESSORS_ONLN" in os.sysconf_names: |
- # Linux, Unix |
- return int(os.sysconf("SC_NPROCESSORS_ONLN")) |
- else: |
- # OSX |
- return int(os.popen2("sysctl -n hw.ncpu")[1].read()) |
- # Windows |
- return int(os.environ["NUMBER_OF_PROCESSORS"]) |
- except ValueError: |
- return SS_DEFAULT_NUM_CORES |
- |
- |
-def GetGTestOutput(args): |
- """Extracts gtest_output from the args. Returns none if not present.""" |
- |
- for arg in args: |
- if '--gtest_output=' in arg: |
- return arg.split('=')[1] |
- return None |
- |
- |
-def AppendToGTestOutput(gtest_args, value): |
- args = gtest_args[:] |
- current_value = GetGTestOutput(args) |
- if not current_value: |
- return gtest_args |
- |
- current_arg = '--gtest_output=' + current_value |
- args.remove(current_arg) |
- args.append('--gtest_output=' + current_value + value) |
- return args |
- |
- |
-def RemoveGTestOutput(gtest_args): |
- args = gtest_args[:] |
- current_value = GetGTestOutput(args) |
- if not current_value: |
- return gtest_args |
- |
- args.remove('--gtest_output=' + current_value) |
- return args |
- |
- |
-def AppendToXML(final_xml, generic_path, shard): |
- """Combine the shard xml file with the final xml file.""" |
- |
- path = generic_path + str(shard) |
- |
- try: |
- with open(path) as shard_xml_file: |
- shard_xml = minidom.parse(shard_xml_file) |
- except IOError: |
- # If the shard crashed, gtest will not have generated an xml file. |
- return final_xml |
- |
- if not final_xml: |
- # Out final xml is empty, let's prepopulate it with the first one we see. |
- return shard_xml |
- |
- shard_node = shard_xml.documentElement |
- final_node = final_xml.documentElement |
- |
- testcases = shard_node.getElementsByTagName('testcase') |
- final_testcases = final_node.getElementsByTagName('testcase') |
- |
- final_testsuites = final_node.getElementsByTagName('testsuite') |
- final_testsuites_by_name = dict( |
- (suite.getAttribute('name'), suite) for suite in final_testsuites) |
- |
- for testcase in testcases: |
- name = testcase.getAttribute('name') |
- classname = testcase.getAttribute('classname') |
- failures = testcase.getElementsByTagName('failure') |
- status = testcase.getAttribute('status') |
- elapsed = testcase.getAttribute('time') |
- |
- # don't bother updating the final xml if there is no data. |
- if status == 'notrun': |
- continue |
- |
- # Look in our final xml to see if it's there. |
- # There has to be a better way... |
- merged_into_final_testcase = False |
- for final_testcase in final_testcases: |
- final_name = final_testcase.getAttribute('name') |
- final_classname = final_testcase.getAttribute('classname') |
- if final_name == name and final_classname == classname: |
- # We got the same entry. |
- final_testcase.setAttribute('status', status) |
- final_testcase.setAttribute('time', elapsed) |
- for failure in failures: |
- final_testcase.appendChild(failure) |
- merged_into_final_testcase = True |
- |
- # We couldn't find an existing testcase to merge the results into, so we |
- # copy the node into the existing test suite. |
- if not merged_into_final_testcase: |
- testsuite = testcase.parentNode |
- final_testsuite = final_testsuites_by_name[testsuite.getAttribute('name')] |
- final_testsuite.appendChild(testcase) |
- |
- return final_xml |
- |
- |
-def RunShard(test, total_shards, index, gtest_args, stdout, stderr): |
- """Runs a single test shard in a subprocess. |
- |
- Returns: |
- The Popen object representing the subprocess handle. |
- """ |
- args = [test] |
- |
- # If there is a gtest_output |
- test_args = AppendToGTestOutput(gtest_args, str(index)) |
- args.extend(test_args) |
- env = os.environ.copy() |
- env["GTEST_TOTAL_SHARDS"] = str(total_shards) |
- env["GTEST_SHARD_INDEX"] = str(index) |
- |
- # Use a unique log file for each shard |
- # Allows ui_tests to be run in parallel on the same machine |
- env["CHROME_LOG_FILE"] = "chrome_log_%d" % index |
- |
- return subprocess.Popen( |
- args, stdout=stdout, |
- stderr=stderr, |
- env=env, |
- bufsize=0, |
- universal_newlines=True) |
- |
- |
-class ShardRunner(threading.Thread): |
- """Worker thread that manages a single shard at a time. |
- |
- Attributes: |
- supervisor: The ShardingSupervisor that this worker reports to. |
- counter: Called to get the next shard index to run. |
- test_start: Regex that detects when a test runs. |
- test_ok: Regex that detects a passing test. |
- test_fail: Regex that detects a failing test. |
- current_test: The name of the currently running test. |
- """ |
- |
- def __init__(self, supervisor, counter, test_start, test_ok, test_fail): |
- """Inits ShardRunner and sets the current test to nothing.""" |
- threading.Thread.__init__(self) |
- self.supervisor = supervisor |
- self.counter = counter |
- self.test_start = test_start |
- self.test_ok = test_ok |
- self.test_fail = test_fail |
- self.current_test = "" |
- |
- def ReportFailure(self, description, index, test_name): |
- """Assembles and reports a failure line to be printed later.""" |
- log_line = "%s (%i): %s\n" % (description, index, test_name) |
- self.supervisor.LogTestFailure(log_line) |
- |
- def ProcessLine(self, index, line): |
- """Checks a shard output line for test status, and reports a failure or |
- incomplete test if needed. |
- """ |
- results = self.test_start.search(line) |
- if results: |
- if self.current_test: |
- self.ReportFailure("INCOMPLETE", index, self.current_test) |
- self.current_test = results.group(1) |
- self.supervisor.IncrementTestCount() |
- return |
- |
- results = self.test_ok.search(line) |
- if results: |
- self.current_test = "" |
- return |
- |
- results = self.test_fail.search(line) |
- if results: |
- self.ReportFailure("FAILED", index, results.group(1)) |
- self.current_test = "" |
- |
- def run(self): |
- """Runs shards and outputs the results. |
- |
- Gets the next shard index from the supervisor, runs it in a subprocess, |
- and collects the output. The output is read character by character in |
- case the shard crashes without an ending newline. Each line is processed |
- as it is finished. |
- """ |
- while True: |
- try: |
- index = self.counter.get_nowait() |
- except Queue.Empty: |
- break |
- shard_running = True |
- shard = RunShard( |
- self.supervisor.test, self.supervisor.total_shards, index, |
- self.supervisor.gtest_args, subprocess.PIPE, subprocess.PIPE) |
- buf = StdioBuffer(shard) |
- # Spawn two threads to collect stdio output |
- stdout_collector_thread = buf.handle_pipe(sys.stdout, shard.stdout) |
- stderr_collector_thread = buf.handle_pipe(sys.stderr, shard.stderr) |
- while shard_running: |
- pipe, line = buf.readline() |
- if pipe is None and line is None: |
- shard_running = False |
- if not line and not shard_running: |
- break |
- self.ProcessLine(index, line) |
- self.supervisor.LogOutputLine(index, line, pipe) |
- stdout_collector_thread.join() |
- stderr_collector_thread.join() |
- if self.current_test: |
- self.ReportFailure("INCOMPLETE", index, self.current_test) |
- self.supervisor.ShardIndexCompleted(index) |
- if shard.returncode != 0: |
- self.supervisor.LogShardFailure(index) |
- |
- |
-class ShardingSupervisor(object): |
- """Supervisor object that handles the worker threads. |
- |
- Attributes: |
- test: Name of the test to shard. |
- num_shards_to_run: Total number of shards to split the test into. |
- num_runs: Total number of worker threads to create for running shards. |
- color: Indicates which coloring mode to use in the output. |
- original_order: True if shard output should be printed as it comes. |
- prefix: True if each line should indicate the shard index. |
- retry_percent: Integer specifying the max percent of tests to retry. |
- gtest_args: The options to pass to gtest. |
- failed_tests: List of statements from shard output indicating a failure. |
- failed_shards: List of shards that contained failing tests. |
- shards_completed: List of flags indicating which shards have finished. |
- shard_output: Buffer that stores output from each shard as (stdio, line). |
- test_counter: Stores the total number of tests run. |
- total_slaves: Total number of slaves running this test. |
- slave_index: Current slave to run tests for. |
- |
- If total_slaves is set, we run only a subset of the tests. This is meant to be |
- used when we want to shard across machines as well as across cpus. In that |
- case the number of shards to execute will be the same, but they will be |
- smaller, as the total number of shards in the test suite will be multiplied |
- by 'total_slaves'. |
- |
- For example, if you are on a quad core machine, the sharding supervisor by |
- default will use 20 shards for the whole suite. However, if you set |
- total_slaves to 2, it will split the suite in 40 shards and will only run |
- shards [0-19] or shards [20-39] depending if you set slave_index to 0 or 1. |
- """ |
- |
- SHARD_COMPLETED = object() |
- |
- def __init__(self, test, num_shards_to_run, num_runs, color, original_order, |
- prefix, retry_percent, timeout, total_slaves, slave_index, |
- gtest_args): |
- """Inits ShardingSupervisor with given options and gtest arguments.""" |
- self.test = test |
- # Number of shards to run locally. |
- self.num_shards_to_run = num_shards_to_run |
- # Total shards in the test suite running across all slaves. |
- self.total_shards = num_shards_to_run * total_slaves |
- self.slave_index = slave_index |
- self.num_runs = num_runs |
- self.color = color |
- self.original_order = original_order |
- self.prefix = prefix |
- self.retry_percent = retry_percent |
- self.timeout = timeout |
- self.gtest_args = gtest_args |
- self.failed_tests = [] |
- self.failed_shards = [] |
- self.shards_completed = [False] * self.num_shards_to_run |
- self.shard_output = [Queue.Queue() for _ in range(self.num_shards_to_run)] |
- self.test_counter = itertools.count() |
- |
- def ShardTest(self): |
- """Runs the test and manages the worker threads. |
- |
- Runs the test and outputs a summary at the end. All the tests in the |
- suite are run by creating (cores * runs_per_core) threads and |
- (cores * shards_per_core) shards. When all the worker threads have |
- finished, the lines saved in failed_tests are printed again. If enabled, |
- and failed tests that do not have FLAKY or FAILS in their names are run |
- again, serially, and the results are printed. |
- |
- Returns: |
- 1 if some unexpected (not FLAKY or FAILS) tests failed, 0 otherwise. |
- """ |
- |
- # Regular expressions for parsing GTest logs. Test names look like |
- # SomeTestCase.SomeTest |
- # SomeName/SomeTestCase.SomeTest/1 |
- # This regex also matches SomeName.SomeTest/1 and |
- # SomeName/SomeTestCase.SomeTest, which should be harmless. |
- test_name_regex = r"((\w+/)?\w+\.\w+(/\d+)?)" |
- |
- # Regex for filtering out ANSI escape codes when using color. |
- ansi_regex = r"(?:\x1b\[.*?[a-zA-Z])?" |
- |
- test_start = re.compile( |
- ansi_regex + r"\[\s+RUN\s+\] " + ansi_regex + test_name_regex) |
- test_ok = re.compile( |
- ansi_regex + r"\[\s+OK\s+\] " + ansi_regex + test_name_regex) |
- test_fail = re.compile( |
- ansi_regex + r"\[\s+FAILED\s+\] " + ansi_regex + test_name_regex) |
- |
- workers = [] |
- counter = Queue.Queue() |
- start_point = self.num_shards_to_run * self.slave_index |
- for i in range(start_point, start_point + self.num_shards_to_run): |
- counter.put(i) |
- |
- for i in range(self.num_runs): |
- worker = ShardRunner( |
- self, counter, test_start, test_ok, test_fail) |
- worker.start() |
- workers.append(worker) |
- if self.original_order: |
- for worker in workers: |
- worker.join() |
- else: |
- self.WaitForShards() |
- |
- # All the shards are done. Merge all the XML files and generate the |
- # main one. |
- output_arg = GetGTestOutput(self.gtest_args) |
- if output_arg: |
- xml, xml_path = output_arg.split(':', 1) |
- assert(xml == 'xml') |
- final_xml = None |
- for i in range(start_point, start_point + self.num_shards_to_run): |
- final_xml = AppendToXML(final_xml, xml_path, i) |
- |
- if final_xml: |
- with open(xml_path, 'w') as final_file: |
- final_xml.writexml(final_file) |
- |
- num_failed = len(self.failed_shards) |
- if num_failed > 0: |
- self.failed_shards.sort() |
- self.WriteText(sys.stdout, |
- "\nFAILED SHARDS: %s\n" % str(self.failed_shards), |
- "\x1b[1;5;31m") |
- else: |
- self.WriteText(sys.stdout, "\nALL SHARDS PASSED!\n", "\x1b[1;5;32m") |
- self.PrintSummary(self.failed_tests) |
- if self.retry_percent < 0: |
- return len(self.failed_shards) > 0 |
- |
- self.failed_tests = [x for x in self.failed_tests if x.find("FAILS_") < 0] |
- self.failed_tests = [x for x in self.failed_tests if x.find("FLAKY_") < 0] |
- if not self.failed_tests: |
- return 0 |
- return self.RetryFailedTests() |
- |
- def LogTestFailure(self, line): |
- """Saves a line in the lsit of failed tests to be printed at the end.""" |
- if line not in self.failed_tests: |
- self.failed_tests.append(line) |
- |
- def LogShardFailure(self, index): |
- """Records that a test in the given shard has failed.""" |
- self.failed_shards.append(index) |
- |
- def WaitForShards(self): |
- """Prints the output from each shard in consecutive order, waiting for |
- the current shard to finish before starting on the next shard. |
- """ |
- try: |
- for shard_index in range(self.num_shards_to_run): |
- while True: |
- try: |
- _, line = self.shard_output[shard_index].get(True, self.timeout) |
- except Queue.Empty: |
- # Shard timed out, notice failure and move on. |
- self.LogShardFailure(shard_index) |
- # TODO(maruel): Print last test. It'd be simpler to have the |
- # processing in the main thread. |
- # TODO(maruel): Make sure the worker thread terminates. |
- sys.stdout.write('TIMED OUT\n\n') |
- self.LogTestFailure( |
- 'FAILURE: SHARD %d TIMED OUT; %d seconds' % ( |
- shard_index, self.timeout)) |
- break |
- if line is self.SHARD_COMPLETED: |
- break |
- sys.stdout.write(line) |
- except: |
- sys.stdout.flush() |
- print 'CAUGHT EXCEPTION: dumping remaining data:' |
- for shard_index in range(self.num_shards_to_run): |
- while True: |
- try: |
- _, line = self.shard_output[shard_index].get(False) |
- except Queue.Empty: |
- # Shard timed out, notice failure and move on. |
- self.LogShardFailure(shard_index) |
- break |
- if line is self.SHARD_COMPLETED: |
- break |
- sys.stdout.write(line) |
- raise |
- |
- def LogOutputLine(self, index, line, pipe=sys.stdout): |
- """Either prints the shard output line immediately or saves it in the |
- output buffer, depending on the settings. Also optionally adds a prefix. |
- Adds a (sys.stdout, line) or (sys.stderr, line) tuple in the output queue. |
- """ |
- # Fix up the index. |
- array_index = index - (self.num_shards_to_run * self.slave_index) |
- if self.prefix: |
- line = "%i>%s" % (index, line) |
- if self.original_order: |
- pipe.write(line) |
- else: |
- self.shard_output[array_index].put((pipe, line)) |
- |
- def IncrementTestCount(self): |
- """Increments the number of tests run. This is relevant to the |
- --retry-percent option. |
- """ |
- self.test_counter.next() |
- |
- def ShardIndexCompleted(self, index): |
- """Records that a shard has finished so the output from the next shard |
- can now be printed. |
- """ |
- # Fix up the index. |
- array_index = index - (self.num_shards_to_run * self.slave_index) |
- self.shard_output[array_index].put((sys.stdout, self.SHARD_COMPLETED)) |
- |
- def RetryFailedTests(self): |
- """Reruns any failed tests serially and prints another summary of the |
- results if no more than retry_percent failed. |
- """ |
- num_tests_run = self.test_counter.next() |
- if len(self.failed_tests) > self.retry_percent * num_tests_run: |
- sys.stdout.write("\nNOT RETRYING FAILED TESTS (too many failed)\n") |
- return 1 |
- self.WriteText(sys.stdout, "\nRETRYING FAILED TESTS:\n", "\x1b[1;5;33m") |
- sharded_description = re.compile(r": (?:\d+>)?(.*)") |
- gtest_filters = [sharded_description.search(line).group(1) |
- for line in self.failed_tests] |
- sys.stdout.write("\nRETRY GTEST FILTERS: %r\n" % gtest_filters) |
- failed_retries = [] |
- |
- for test_filter in gtest_filters: |
- args = [self.test, "--gtest_filter=" + test_filter] |
- # Don't update the xml output files during retry. |
- stripped_gtests_args = RemoveGTestOutput(self.gtest_args) |
- args.extend(stripped_gtests_args) |
- sys.stdout.write("\nRETRY COMMAND: %r\n" % args) |
- rerun = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr) |
- rerun.wait() |
- if rerun.returncode != 0: |
- failed_retries.append(test_filter) |
- |
- self.WriteText(sys.stdout, "RETRY RESULTS:\n", "\x1b[1;5;33m") |
- self.PrintSummary(failed_retries) |
- return len(failed_retries) > 0 |
- |
- def PrintSummary(self, failed_tests): |
- """Prints a summary of the test results. |
- |
- If any shards had failing tests, the list is sorted and printed. Then all |
- the lines that indicate a test failure are reproduced. |
- """ |
- if failed_tests: |
- self.WriteText(sys.stdout, "FAILED TESTS:\n", "\x1b[1;5;31m") |
- for line in failed_tests: |
- sys.stdout.write(line) |
- else: |
- self.WriteText(sys.stdout, "ALL TESTS PASSED!\n", "\x1b[1;5;32m") |
- |
- def WriteText(self, pipe, text, ansi): |
- """Writes the text to the pipe with the ansi escape code, if colored |
- output is set, for Unix systems. |
- """ |
- if self.color: |
- pipe.write(ansi) |
- pipe.write(text) |
- if self.color: |
- pipe.write("\x1b[m") |
- |
- |
-def main(): |
- parser = optparse.OptionParser(usage=SS_USAGE) |
- parser.add_option( |
- "-n", "--shards_per_core", type="int", default=SS_DEFAULT_SHARDS_PER_CORE, |
- help="number of shards to generate per CPU") |
- parser.add_option( |
- "-r", "--runs_per_core", type="int", default=SS_DEFAULT_RUNS_PER_CORE, |
- help="number of shards to run in parallel per CPU") |
- parser.add_option( |
- "-c", "--color", action="store_true", |
- default=sys.platform != "win32" and sys.stdout.isatty(), |
- help="force color output, also used by gtest if --gtest_color is not" |
- " specified") |
- parser.add_option( |
- "--no-color", action="store_false", dest="color", |
- help="disable color output") |
- parser.add_option( |
- "-s", "--runshard", type="int", help="single shard index to run") |
- parser.add_option( |
- "--reorder", action="store_true", |
- help="ensure that all output from an earlier shard is printed before" |
- " output from a later shard") |
- # TODO(charleslee): for backwards compatibility with master.cfg file |
- parser.add_option( |
- "--original-order", action="store_true", |
- help="print shard output in its orginal jumbled order of execution" |
- " (useful for debugging flaky tests)") |
- parser.add_option( |
- "--prefix", action="store_true", |
- help="prefix each line of shard output with 'N>', where N is the shard" |
- " index (forced True when --original-order is True)") |
- parser.add_option( |
- "--random-seed", action="store_true", |
- help="shuffle the tests with a random seed value") |
- parser.add_option( |
- "--retry-failed", action="store_true", |
- help="retry tests that did not pass serially") |
- parser.add_option( |
- "--retry-percent", type="int", |
- default=SS_DEFAULT_RETRY_PERCENT, |
- help="ignore --retry-failed if more than this percent fail [0, 100]" |
- " (default = %i)" % SS_DEFAULT_RETRY_PERCENT) |
- parser.add_option( |
- "-t", "--timeout", type="int", default=SS_DEFAULT_TIMEOUT, |
- help="timeout in seconds to wait for a shard (default=%default s)") |
- parser.add_option( |
- "--total-slaves", type="int", default=1, |
- help="if running a subset, number of slaves sharing the test") |
- parser.add_option( |
- "--slave-index", type="int", default=0, |
- help="if running a subset, index of the slave to run tests for") |
- |
- parser.disable_interspersed_args() |
- (options, args) = parser.parse_args() |
- |
- if not args: |
- parser.error("You must specify a path to test!") |
- if not os.path.exists(args[0]): |
- parser.error("%s does not exist!" % args[0]) |
- |
- num_cores = DetectNumCores() |
- |
- if options.shards_per_core < 1: |
- parser.error("You must have at least 1 shard per core!") |
- num_shards_to_run = num_cores * options.shards_per_core |
- |
- if options.runs_per_core < 1: |
- parser.error("You must have at least 1 run per core!") |
- num_runs = num_cores * options.runs_per_core |
- |
- test = args[0] |
- gtest_args = ["--gtest_color=%s" % { |
- True: "yes", False: "no"}[options.color]] + args[1:] |
- |
- if options.original_order: |
- options.prefix = True |
- |
- # TODO(charleslee): for backwards compatibility with buildbot's log_parser |
- if options.reorder: |
- options.original_order = False |
- options.prefix = True |
- |
- if options.random_seed: |
- seed = random.randint(1, 99999) |
- gtest_args.extend(["--gtest_shuffle", "--gtest_random_seed=%i" % seed]) |
- |
- if options.retry_failed: |
- if options.retry_percent < 0 or options.retry_percent > 100: |
- parser.error("Retry percent must be an integer [0, 100]!") |
- else: |
- options.retry_percent = -1 |
- |
- if options.runshard != None: |
- # run a single shard and exit |
- if (options.runshard < 0 or options.runshard >= num_shards_to_run): |
- parser.error("Invalid shard number given parameters!") |
- shard = RunShard( |
- test, num_shards_to_run, options.runshard, gtest_args, None, None) |
- shard.communicate() |
- return shard.poll() |
- |
- # When running browser_tests, load the test binary into memory before running |
- # any tests. This is needed to prevent loading it from disk causing the first |
- # run tests to timeout flakily. See: http://crbug.com/124260 |
- if "browser_tests" in test: |
- args = [test] |
- args.extend(gtest_args) |
- args.append("--warmup") |
- result = subprocess.call(args, |
- bufsize=0, |
- universal_newlines=True) |
- # If the test fails, don't run anything else. |
- if result != 0: |
- return result |
- |
- # shard and run the whole test |
- ss = ShardingSupervisor( |
- test, num_shards_to_run, num_runs, options.color, |
- options.original_order, options.prefix, options.retry_percent, |
- options.timeout, options.total_slaves, options.slave_index, gtest_args) |
- return ss.ShardTest() |
- |
- |
-if __name__ == "__main__": |
- sys.exit(main()) |