OLD | NEW |
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 | 12 |
13 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) | 13 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
14 from cros_build_lib import Die | 14 from cros_build_lib import Die |
15 from cros_build_lib import Info | 15 from cros_build_lib import Info |
16 | 16 |
17 | 17 |
| 18 _DEFAULT_BASE_SSH_PORT = 9222 |
| 19 |
18 class ParallelTestRunner(object): | 20 class ParallelTestRunner(object): |
19 """Runs tests on VMs in parallel.""" | 21 """Runs tests on VMs in parallel. |
20 | 22 |
21 _DEFAULT_START_SSH_PORT = 9222 | 23 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 the tests complete. |
| 26 """ |
22 | 27 |
23 def __init__(self, tests, results_dir_root=None): | 28 def __init__(self, tests, base_ssh_port=_DEFAULT_BASE_SSH_PORT, board=None, |
| 29 image_path=None, results_dir_root=None): |
24 """Constructs and initializes the test runner class. | 30 """Constructs and initializes the test runner class. |
25 | 31 |
26 Args: | 32 Args: |
27 tests: A list of test names (see run_remote_tests.sh). | 33 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 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. |
| 38 image_path: Full path to the VM image. If none, cros_run_vm_tests will use |
| 39 the latest image. |
28 results_dir_root: The results directory root. If provided, the results | 40 results_dir_root: The results directory root. If provided, the results |
29 directory root for each test will be created under it with the SSH port | 41 directory root for each test will be created under it with the SSH port |
30 appended to the test name. | 42 appended to the test name. |
31 """ | 43 """ |
32 self._tests = tests | 44 self._tests = tests |
| 45 self._base_ssh_port = base_ssh_port |
| 46 self._board = board |
| 47 self._image_path = image_path |
33 self._results_dir_root = results_dir_root | 48 self._results_dir_root = results_dir_root |
34 | 49 |
35 def _SpawnTests(self): | 50 def _SpawnTests(self): |
36 """Spawns VMs and starts the test runs on them. | 51 """Spawns VMs and starts the test runs on them. |
37 | 52 |
38 Runs all tests in |self._tests|. Each test is executed on a separate VM. | 53 Runs all tests in |self._tests|. Each test is executed on a separate VM. |
39 | 54 |
40 Returns: | 55 Returns: |
41 A list of test process info objects containing the following dictionary | 56 A list of test process info objects containing the following dictionary |
42 entries: | 57 entries: |
43 'test': the test name; | 58 'test': the test name; |
44 'proc': the Popen process instance for this test run. | 59 'proc': the Popen process instance for this test run. |
45 """ | 60 """ |
46 ssh_port = self._DEFAULT_START_SSH_PORT | 61 ssh_port = self._base_ssh_port |
47 spawned_tests = [] | 62 spawned_tests = [] |
48 # Test runs shouldn't need anything from stdin. However, it seems that | 63 # Test runs shouldn't need anything from stdin. However, it seems that |
49 # running with stdin leaves the terminal in a bad state so redirect from | 64 # running with stdin leaves the terminal in a bad state so redirect from |
50 # /dev/null. | 65 # /dev/null. |
51 dev_null = open('/dev/null') | 66 dev_null = open('/dev/null') |
52 for test in self._tests: | 67 for test in self._tests: |
53 args = [ os.path.join(os.path.dirname(__file__), 'cros_run_vm_test'), | 68 args = [ os.path.join(os.path.dirname(__file__), 'cros_run_vm_test'), |
54 '--snapshot', # The image is shared so don't modify it. | 69 '--snapshot', # The image is shared so don't modify it. |
55 '--no_graphics', | 70 '--no_graphics', |
56 '--ssh_port=%d' % ssh_port, | 71 '--ssh_port=%d' % ssh_port, |
57 '--test_case=%s' % test ] | 72 '--test_case=%s' % test ] |
| 73 if self._board: args.append('--board=%s' % self._board) |
| 74 if self._image_path: args.append('--image_path=%s' % self._image_path) |
58 if self._results_dir_root: | 75 if self._results_dir_root: |
59 args.append('--results_dir_root=%s/%s.%d' % | 76 args.append('--results_dir_root=%s/%s.%d' % |
60 (self._results_dir_root, test, ssh_port)) | 77 (self._results_dir_root, test, ssh_port)) |
61 Info('Running %r...' % args) | 78 Info('Running %r...' % args) |
62 proc = subprocess.Popen(args, stdin=dev_null) | 79 proc = subprocess.Popen(args, stdin=dev_null) |
63 test_info = { 'test': test, | 80 test_info = { 'test': test, |
64 'proc': proc } | 81 'proc': proc } |
65 spawned_tests.append(test_info) | 82 spawned_tests.append(test_info) |
66 ssh_port = ssh_port + 1 | 83 ssh_port = ssh_port + 1 |
67 return spawned_tests | 84 return spawned_tests |
(...skipping 17 matching lines...) Expand all Loading... |
85 def Run(self): | 102 def Run(self): |
86 """Runs the tests in |self._tests| on separate VMs in parallel.""" | 103 """Runs the tests in |self._tests| on separate VMs in parallel.""" |
87 spawned_tests = self._SpawnTests() | 104 spawned_tests = self._SpawnTests() |
88 failed_tests = self._WaitForCompletion(spawned_tests) | 105 failed_tests = self._WaitForCompletion(spawned_tests) |
89 if failed_tests: Die('Tests failed: %r' % failed_tests) | 106 if failed_tests: Die('Tests failed: %r' % failed_tests) |
90 | 107 |
91 | 108 |
92 def main(): | 109 def main(): |
93 usage = 'Usage: %prog [options] tests...' | 110 usage = 'Usage: %prog [options] tests...' |
94 parser = optparse.OptionParser(usage=usage) | 111 parser = optparse.OptionParser(usage=usage) |
95 parser.add_option('--results_dir_root', help='Root results directory.') | 112 parser.add_option('--base_ssh_port', type='int', |
| 113 default=_DEFAULT_BASE_SSH_PORT, |
| 114 help='Base SSH port. Spawned VMs listen to localhost SSH ' |
| 115 'ports incrementally allocated starting from the base one. ' |
| 116 '[default: %default]') |
| 117 parser.add_option('--board', |
| 118 help='The target board. If none specified, ' |
| 119 'cros_run_vm_test will use the default board.') |
| 120 parser.add_option('--image_path', |
| 121 help='Full path to the VM image. If none specified, ' |
| 122 'cros_run_vm_test will use the latest image.') |
| 123 parser.add_option('--results_dir_root', |
| 124 help='Root results directory. If none specified, each test ' |
| 125 'will store its results in a separate /tmp directory.') |
96 (options, args) = parser.parse_args() | 126 (options, args) = parser.parse_args() |
97 | 127 |
98 if not args: | 128 if not args: |
99 parser.print_help() | 129 parser.print_help() |
100 Die('no tests provided') | 130 Die('no tests provided') |
101 | 131 |
102 runner = ParallelTestRunner(args, options.results_dir_root) | 132 runner = ParallelTestRunner(args, options.base_ssh_port, options.board, |
| 133 options.image_path, options.results_dir_root) |
103 runner.Run() | 134 runner.Run() |
104 | 135 |
105 | 136 |
106 if __name__ == '__main__': | 137 if __name__ == '__main__': |
107 main() | 138 main() |
OLD | NEW |