summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xchrome/tools/test/smoketests.py38
-rw-r--r--tools/python/google/process_utils.py86
2 files changed, 123 insertions, 1 deletions
diff --git a/chrome/tools/test/smoketests.py b/chrome/tools/test/smoketests.py
index 50ddd83..f0513df 100755
--- a/chrome/tools/test/smoketests.py
+++ b/chrome/tools/test/smoketests.py
@@ -109,6 +109,39 @@ def _MakeSubstitutions(list, options):
return [word % substitutions for word in list]
+def RunTestsInShards(test_command, verbose=True):
+ """Runs a test in shards. The number of shards is equal to
+ NUMBER_OF_PROCESSORS.
+
+ Args:
+ test_command: the test command to run, which is a list of one or more
+ strings.
+ verbose: if True, combines stdout and stderr into stdout.
+ Otherwise, prints only the command's stderr to stdout.
+
+ Returns:
+ The first shard process's exit status.
+
+ Raises:
+ CommandNotFound if the command executable could not be found.
+ """
+ processor_count = 2
+ try:
+ processor_count = int(os.environ['NUMBER_OF_PROCESSORS'])
+ except KeyError:
+ print 'No NUMBER_OF_PROCESSORS defined. Use 2 instances.'
+
+ commands = []
+ for i in xrange(processor_count):
+ command = [test_command[j] for j in xrange(len(test_command))]
+ # To support sharding, the test executable needs to provide --batch-count
+ # --batch-index command line switches.
+ command.append('--batch-count=%s' % processor_count)
+ command.append('--batch-index=%d' % i)
+ commands.append(command)
+ return google.process_utils.RunCommandsInParallel(commands, verbose)[0][0]
+
+
def main(options, args):
"""Runs all the selected tests for the given build type and target."""
options.build_type = options.build_type.lower()
@@ -184,7 +217,10 @@ def main(options, args):
print
print 'Running %s:' % test,
try:
- result = google.process_utils.RunCommand(command, options.verbose)
+ if test == 'ui':
+ result = RunTestsInShards(command, options.verbose)
+ else:
+ result = google.process_utils.RunCommand(command, options.verbose)
except google.process_utils.CommandNotFound, e:
print '%s' % e
raise
diff --git a/tools/python/google/process_utils.py b/tools/python/google/process_utils.py
index 5024ec7..64c92ea 100644
--- a/tools/python/google/process_utils.py
+++ b/tools/python/google/process_utils.py
@@ -133,3 +133,89 @@ def RunCommand(command, verbose=True):
"""
return RunCommandFull(command, verbose)[0]
+def RunCommandsInParallel(commands, verbose=True, collect_output=False,
+ print_output=True):
+ """Runs a list of commands in parallel, waits for all commands to terminate
+ and returns their status. If specified, the ouput of commands can be
+ returned and/or printed.
+
+ Args:
+ commands: the list of commands to run, each as a list of one or more
+ strings.
+ verbose: if True, combines stdout and stderr into stdout.
+ Otherwise, prints only the command's stderr to stdout.
+ collect_output: if True, collects the output of the each command as a list
+ of lines and returns it.
+ print_output: if True, prints the output of each command.
+
+ Returns:
+ A list of tuples consisting of each command's exit status and output. If
+ collect_output is False, the output will be [].
+
+ Raises:
+ CommandNotFound if any of the command executables could not be found.
+ """
+
+ command_num = len(commands)
+ outputs = [[] for i in xrange(command_num)]
+ procs = [None for i in xrange(command_num)]
+ eofs = [False for i in xrange(command_num)]
+
+ for command in commands:
+ print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n',
+
+ if verbose:
+ out = subprocess.PIPE
+ err = subprocess.STDOUT
+ else:
+ out = file(os.devnull, 'w')
+ err = subprocess.PIPE
+
+ for i in xrange(command_num):
+ try:
+ command = commands[i]
+ procs[i] = subprocess.Popen(command, stdout=out, stderr=err, bufsize=1)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ raise CommandNotFound('Unable to find "%s"' % command[0])
+ raise
+ # We could consider terminating the processes already started.
+ # But Popen.kill() is only available in version 2.6.
+ # For now the clean up is done by KillAll.
+
+ while True:
+ eof_all = True
+ for i in xrange(command_num):
+ if eofs[i]:
+ continue
+ if verbose:
+ read_from = procs[i].stdout
+ else:
+ read_from = procs[i].stderr
+ line = read_from.readline()
+ if line:
+ eof_all = False
+ line = line.rstrip()
+ outputs[i].append(line)
+ if print_output:
+ # Windows Python converts \n to \r\n automatically whenever it
+ # encounters it written to a text file (including stdout). The only
+ # way around it is to write to a binary file, which isn't feasible
+ # for stdout. So we end up with \r\n here even though we explicitly
+ # write \n. (We could write \r instead, which doesn't get converted
+ # to \r\n, but that's probably more troublesome for people trying to
+ # read the files.)
+ print line + '\n',
+ else:
+ eofs[i] = True
+ if eof_all:
+ break
+
+ # Make sure the process terminates.
+ for i in xrange(command_num):
+ procs[i].wait()
+
+ if not verbose:
+ out.close()
+
+ return [(procs[i].returncode, outputs[i]) for i in xrange(command_num)]