Index: chrome/tools/sharding_supervisor/sharding_supervisor.py |
diff --git a/chrome/tools/sharding_supervisor/sharding_supervisor.py b/chrome/tools/sharding_supervisor/sharding_supervisor.py |
deleted file mode 100755 |
index c8ce01f13b9bd12ce1708ffe63cf0fb1a77cac34..0000000000000000000000000000000000000000 |
--- a/chrome/tools/sharding_supervisor/sharding_supervisor.py |
+++ /dev/null |
@@ -1,265 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright (c) 2011 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. |
- |
-Usage: python sharding_supervisor.py [options] path/to/test [gtest_args] |
-""" |
- |
- |
-import optparse |
-import os |
-import pty |
-import Queue |
-import subprocess |
-import sys |
-import threading |
- |
- |
-DEFAULT_NUM_CORES = 4 |
-DEFAULT_SHARDS_PER_CORE = 5 # num_shards = cores * SHARDS_PER_CORE |
-DEFAULT_RUNS_PER_CORE = 1 # num_workers = cores * RUNS_PER_CORE |
- |
- |
-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: |
- # 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 DEFAULT_NUM_CORES |
- |
- |
-def RunShard(test, num_shards, index, gtest_args, stdout, stderr): |
- """Runs a single test shard in a subprocess. |
- |
- Returns: |
- The Popen object representing the subprocess handle. |
- """ |
- args = [test] |
- args.extend(gtest_args) |
- env = os.environ.copy() |
- env["GTEST_TOTAL_SHARDS"] = str(num_shards) |
- env["GTEST_SHARD_INDEX"] = str(index) |
- return subprocess.Popen( |
- args, stdout=stdout, stderr=stderr, env=env) |
- |
- |
-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. |
- """ |
- |
- def __init__(self, supervisor, counter): |
- """Inits ShardRunner with a supervisor and counter.""" |
- threading.Thread.__init__(self) |
- self.supervisor = supervisor |
- self.counter = counter |
- |
- 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. Each line is prefixed with 'N>', where N is the |
- current shard index. |
- """ |
- while True: |
- try: |
- index = self.counter.get_nowait() |
- except Queue.Empty: |
- break |
- shard = RunShard( |
- self.supervisor.test, self.supervisor.num_shards, index, |
- self.supervisor.gtest_args, subprocess.PIPE, subprocess.STDOUT) |
- while True: |
- line = shard.stdout.readline() |
- if not line: |
- if shard.poll() is not None: |
- break |
- continue |
- line = "%i>%s" % (index, line) |
- if (line.find("FAIL", 0, 20) >= 0 and line.find(".") >= 0 and |
- line.find("ms)")) < 0: |
- self.supervisor.LogLineFailure(line) |
- sys.stdout.write(line) |
- 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: 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. |
- gtest_args: The options to pass to gtest. |
- failure_log: List of statements from shard output indicating a failure. |
- failed_shards: List of shards that contained failing tests. |
- """ |
- |
- def __init__( |
- self, test, num_shards, num_runs, color, gtest_args): |
- """Inits ShardingSupervisor with given options and gtest arguments.""" |
- self.test = test |
- self.num_shards = num_shards |
- self.num_runs = num_runs |
- self.color = color |
- self.gtest_args = gtest_args |
- self.failure_log = [] |
- self.failed_shards = [] |
- |
- 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 the failure_log are printed again. |
- |
- Returns: |
- The number of shards that had failing tests. |
- """ |
- workers = [] |
- counter = Queue.Queue() |
- for i in range(self.num_shards): |
- counter.put(i) |
- for i in range(self.num_runs): |
- worker = ShardRunner(self, counter) |
- worker.start() |
- workers.append(worker) |
- for worker in workers: |
- worker.join() |
- return self.PrintSummary() |
- |
- def LogLineFailure(self, line): |
- """Saves a line in the failure log to be printed at the end. |
- |
- Args: |
- line: The line to save in the failure_log. |
- """ |
- self.failure_log.append(line) |
- |
- def LogShardFailure(self, index): |
- """Records that a test in the given shard has failed. |
- |
- Args: |
- index: The index of the failing shard. |
- """ |
- self.failed_shards.append(index) |
- |
- def PrintSummary(self): |
- """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. |
- |
- Returns: |
- The number of shards that had failing tests. |
- """ |
- sys.stderr.write("\n") |
- num_failed = len(self.failed_shards) |
- if num_failed > 0: |
- self.failed_shards.sort() |
- if self.color: |
- sys.stderr.write("\x1b[1;5;31m") |
- sys.stderr.write("FAILED SHARDS: %s\n" % str(self.failed_shards)) |
- else: |
- if self.color: |
- sys.stderr.write("\x1b[1;5;32m") |
- sys.stderr.write("ALL SHARDS PASSED!\n") |
- if self.failure_log: |
- if self.color: |
- sys.stderr.write("\x1b[1;5;31m") |
- sys.stderr.write("FAILED TESTS:\n") |
- if self.color: |
- sys.stderr.write("\x1b[0;37m") |
- for line in self.failure_log: |
- sys.stderr.write(line) |
- if self.color: |
- sys.stderr.write("\x1b[0;37m") |
- return num_failed |
- |
- |
-def main(): |
- parser = optparse.OptionParser() |
- parser.add_option( |
- "-n", "--shards_per_core", type="int", default=DEFAULT_SHARDS_PER_CORE, |
- help="number of shards to generate per CPU") |
- parser.add_option( |
- "-r", "--runs_per_core", type="int", default=DEFAULT_RUNS_PER_CORE, |
- help="number of shards to run in parallel per CPU") |
- parser.add_option( |
- "-c", "--color", action="store_true", default=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.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 = 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 |
- |
- gtest_args = ["--gtest_color=%s" % { |
- True: "yes", False: "no"}[options.color]] + args[1:] |
- |
- if options.runshard != None: |
- # run a single shard and exit |
- if (options.runshard < 0 or options.runshard >= num_shards): |
- parser.error("Invalid shard number given parameters!") |
- shard = RunShard( |
- args[0], num_shards, options.runshard, gtest_args, None, None) |
- shard.communicate() |
- return shard.poll() |
- |
- # shard and run the whole test |
- ss = ShardingSupervisor( |
- args[0], num_shards, num_runs, options.color, gtest_args) |
- return ss.ShardTest() |
- |
- |
-if __name__ == "__main__": |
- sys.exit(main()) |
- |