Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: bin/cros_run_parallel_vm_tests.py

Issue 4422001: VM: Implement --order_output option in cros_run_parallel_vm_tests. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git@master
Patch Set: review comments Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Runs tests on VMs in parallel.""" 6 """Runs tests on VMs in parallel."""
7 7
8 import optparse 8 import optparse
9 import os 9 import os
10 import subprocess 10 import subprocess
11 import sys 11 import sys
12 import tempfile
12 13
13 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) 14 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
14 from cros_build_lib import Die 15 from cros_build_lib import Die
15 from cros_build_lib import Info 16 from cros_build_lib import Info
16 17
17 18
18 _DEFAULT_BASE_SSH_PORT = 9222 19 _DEFAULT_BASE_SSH_PORT = 9222
19 20
20 class ParallelTestRunner(object): 21 class ParallelTestRunner(object):
21 """Runs tests on VMs in parallel. 22 """Runs tests on VMs in parallel.
22 23
23 This class is a simple wrapper around cros_run_vm_test that provides an easy 24 This class is a simple wrapper around cros_run_vm_test that provides an easy
24 way to spawn several test instances in parallel and aggregate the results when 25 way to spawn several test instances in parallel and aggregate the results when
25 the tests complete. 26 the tests complete.
26 """ 27 """
27 28
28 def __init__(self, tests, base_ssh_port=_DEFAULT_BASE_SSH_PORT, board=None, 29 def __init__(self, tests, base_ssh_port=_DEFAULT_BASE_SSH_PORT, board=None,
29 image_path=None, results_dir_root=None): 30 image_path=None, order_output=False, results_dir_root=None):
30 """Constructs and initializes the test runner class. 31 """Constructs and initializes the test runner class.
31 32
32 Args: 33 Args:
33 tests: A list of test names (see run_remote_tests.sh). 34 tests: A list of test names (see run_remote_tests.sh).
34 base_ssh_port: The base SSH port. Spawned VMs listen to localhost SSH 35 base_ssh_port: The base SSH port. Spawned VMs listen to localhost SSH
35 ports incrementally allocated starting from the base one. 36 ports incrementally allocated starting from the base one.
36 board: The target board. If none, cros_run_vm_tests will use the default 37 board: The target board. If none, cros_run_vm_tests will use the default
37 board. 38 board.
38 image_path: Full path to the VM image. If none, cros_run_vm_tests will use 39 image_path: Full path to the VM image. If none, cros_run_vm_tests will use
39 the latest image. 40 the latest image.
41 order_output: If True, output of individual VMs will be piped to
42 temporary files and emitted at the end.
40 results_dir_root: The results directory root. If provided, the results 43 results_dir_root: The results directory root. If provided, the results
41 directory root for each test will be created under it with the SSH port 44 directory root for each test will be created under it with the SSH port
42 appended to the test name. 45 appended to the test name.
43 """ 46 """
44 self._tests = tests 47 self._tests = tests
45 self._base_ssh_port = base_ssh_port 48 self._base_ssh_port = base_ssh_port
46 self._board = board 49 self._board = board
47 self._image_path = image_path 50 self._image_path = image_path
51 self._order_output = order_output
48 self._results_dir_root = results_dir_root 52 self._results_dir_root = results_dir_root
49 53
50 def _SpawnTests(self): 54 def _SpawnTests(self):
51 """Spawns VMs and starts the test runs on them. 55 """Spawns VMs and starts the test runs on them.
52 56
53 Runs all tests in |self._tests|. Each test is executed on a separate VM. 57 Runs all tests in |self._tests|. Each test is executed on a separate VM.
54 58
55 Returns: 59 Returns:
56 A list of test process info objects containing the following dictionary 60 A list of test process info objects containing the following dictionary
57 entries: 61 entries:
(...skipping 11 matching lines...) Expand all
69 '--snapshot', # The image is shared so don't modify it. 73 '--snapshot', # The image is shared so don't modify it.
70 '--no_graphics', 74 '--no_graphics',
71 '--ssh_port=%d' % ssh_port, 75 '--ssh_port=%d' % ssh_port,
72 '--test_case=%s' % test ] 76 '--test_case=%s' % test ]
73 if self._board: args.append('--board=%s' % self._board) 77 if self._board: args.append('--board=%s' % self._board)
74 if self._image_path: args.append('--image_path=%s' % self._image_path) 78 if self._image_path: args.append('--image_path=%s' % self._image_path)
75 if self._results_dir_root: 79 if self._results_dir_root:
76 args.append('--results_dir_root=%s/%s.%d' % 80 args.append('--results_dir_root=%s/%s.%d' %
77 (self._results_dir_root, test, ssh_port)) 81 (self._results_dir_root, test, ssh_port))
78 Info('Running %r...' % args) 82 Info('Running %r...' % args)
79 proc = subprocess.Popen(args, stdin=dev_null) 83 output = None
84 if self._order_output:
85 output = tempfile.NamedTemporaryFile(prefix='parallel_vm_test_')
86 Info('Piping output to %s.' % output.name)
87 proc = subprocess.Popen(args, stdin=dev_null, stdout=output,
88 stderr=output)
80 test_info = { 'test': test, 89 test_info = { 'test': test,
81 'proc': proc } 90 'proc': proc,
91 'output': output }
82 spawned_tests.append(test_info) 92 spawned_tests.append(test_info)
83 ssh_port = ssh_port + 1 93 ssh_port = ssh_port + 1
84 return spawned_tests 94 return spawned_tests
85 95
86 def _WaitForCompletion(self, spawned_tests): 96 def _WaitForCompletion(self, spawned_tests):
87 """Waits for tests to complete and returns a list of failed tests. 97 """Waits for tests to complete and returns a list of failed tests.
88 98
99 If the test output was piped to a file, dumps the file contents to stdout.
100
89 Args: 101 Args:
90 spawned_tests: A list of test info objects (see _SpawnTests). 102 spawned_tests: A list of test info objects (see _SpawnTests).
91 103
92 Returns: 104 Returns:
93 A list of failed test names. 105 A list of failed test names.
94 """ 106 """
95 failed_tests = [] 107 failed_tests = []
96 for test_info in spawned_tests: 108 for test_info in spawned_tests:
97 proc = test_info['proc'] 109 proc = test_info['proc']
98 proc.wait() 110 proc.wait()
99 if proc.returncode: failed_tests.append(test_info['test']) 111 if proc.returncode: failed_tests.append(test_info['test'])
112 output = test_info['output']
113 if output:
114 test = test_info['test']
115 Info('------ START %s:%s ------' % (test, output.name))
116 output.seek(0)
117 for line in output:
118 print line,
119 Info('------ END %s:%s ------' % (test, output.name))
100 return failed_tests 120 return failed_tests
101 121
102 def Run(self): 122 def Run(self):
103 """Runs the tests in |self._tests| on separate VMs in parallel.""" 123 """Runs the tests in |self._tests| on separate VMs in parallel."""
104 spawned_tests = self._SpawnTests() 124 spawned_tests = self._SpawnTests()
105 failed_tests = self._WaitForCompletion(spawned_tests) 125 failed_tests = self._WaitForCompletion(spawned_tests)
106 if failed_tests: Die('Tests failed: %r' % failed_tests) 126 if failed_tests: Die('Tests failed: %r' % failed_tests)
107 127
108 128
109 def main(): 129 def main():
110 usage = 'Usage: %prog [options] tests...' 130 usage = 'Usage: %prog [options] tests...'
111 parser = optparse.OptionParser(usage=usage) 131 parser = optparse.OptionParser(usage=usage)
112 parser.add_option('--base_ssh_port', type='int', 132 parser.add_option('--base_ssh_port', type='int',
113 default=_DEFAULT_BASE_SSH_PORT, 133 default=_DEFAULT_BASE_SSH_PORT,
114 help='Base SSH port. Spawned VMs listen to localhost SSH ' 134 help='Base SSH port. Spawned VMs listen to localhost SSH '
115 'ports incrementally allocated starting from the base one. ' 135 'ports incrementally allocated starting from the base one. '
116 '[default: %default]') 136 '[default: %default]')
117 parser.add_option('--board', 137 parser.add_option('--board',
118 help='The target board. If none specified, ' 138 help='The target board. If none specified, '
119 'cros_run_vm_test will use the default board.') 139 'cros_run_vm_test will use the default board.')
120 parser.add_option('--image_path', 140 parser.add_option('--image_path',
121 help='Full path to the VM image. If none specified, ' 141 help='Full path to the VM image. If none specified, '
122 'cros_run_vm_test will use the latest image.') 142 'cros_run_vm_test will use the latest image.')
143 parser.add_option('--order_output', action='store_true', default=False,
144 help='Rather than emitting interleaved progress output '
145 'from the individual VMs, accumulate the outputs in '
146 'temporary files and dump them at the end.')
123 parser.add_option('--results_dir_root', 147 parser.add_option('--results_dir_root',
124 help='Root results directory. If none specified, each test ' 148 help='Root results directory. If none specified, each test '
125 'will store its results in a separate /tmp directory.') 149 'will store its results in a separate /tmp directory.')
126 (options, args) = parser.parse_args() 150 (options, args) = parser.parse_args()
127 151
128 if not args: 152 if not args:
129 parser.print_help() 153 parser.print_help()
130 Die('no tests provided') 154 Die('no tests provided')
131 155
132 runner = ParallelTestRunner(args, options.base_ssh_port, options.board, 156 runner = ParallelTestRunner(args, options.base_ssh_port, options.board,
133 options.image_path, options.results_dir_root) 157 options.image_path, options.order_output,
158 options.results_dir_root)
134 runner.Run() 159 runner.Run()
135 160
136 161
137 if __name__ == '__main__': 162 if __name__ == '__main__':
138 main() 163 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698