OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 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 |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """Runs tests on VMs in parallel.""" |
| 7 |
| 8 import optparse |
| 9 import os |
| 10 import subprocess |
| 11 import sys |
| 12 |
| 13 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
| 14 from cros_build_lib import Die |
| 15 from cros_build_lib import Info |
| 16 |
| 17 |
| 18 class ParallelTestRunner(object): |
| 19 """Runs tests on VMs in parallel.""" |
| 20 |
| 21 _DEFAULT_START_SSH_PORT = 9222 |
| 22 |
| 23 def __init__(self, tests): |
| 24 self._tests = tests |
| 25 |
| 26 def _SpawnTests(self): |
| 27 """Spawns VMs and starts the test runs on them. |
| 28 |
| 29 Runs all tests in |self._tests|. Each test is executed on a separate VM. |
| 30 |
| 31 Returns: A list of test process info objects containing the following |
| 32 dictionary entries: |
| 33 'test': the test name; |
| 34 'proc': the Popen process instance for this test run. |
| 35 """ |
| 36 ssh_port = self._DEFAULT_START_SSH_PORT |
| 37 spawned_tests = [] |
| 38 # Test runs shouldn't need anything from stdin. However, it seems that |
| 39 # running with stdin leaves the terminal in a bad state so redirect from |
| 40 # /dev/null. |
| 41 dev_null = open('/dev/null') |
| 42 for test in self._tests: |
| 43 args = [ os.path.join(os.path.dirname(__file__), 'cros_run_vm_test'), |
| 44 '--snapshot', # The image is shared so don't modify it. |
| 45 '--no_graphics', |
| 46 '--ssh_port=%d' % ssh_port, |
| 47 '--test_case=%s' % test ] |
| 48 Info('Running %r...' % args) |
| 49 proc = subprocess.Popen(args, stdin=dev_null) |
| 50 test_info = { 'test': test, |
| 51 'proc': proc } |
| 52 spawned_tests.append(test_info) |
| 53 ssh_port = ssh_port + 1 |
| 54 return spawned_tests |
| 55 |
| 56 def _WaitForCompletion(self, spawned_tests): |
| 57 """Waits for tests to complete and returns a list of failed tests. |
| 58 |
| 59 Arguments: |
| 60 spawned_tests: A list of test info objects (see _SpawnTests). |
| 61 |
| 62 Returns: A list of failed test names. |
| 63 """ |
| 64 failed_tests = [] |
| 65 for test_info in spawned_tests: |
| 66 proc = test_info['proc'] |
| 67 proc.wait() |
| 68 if proc.returncode: failed_tests.append(test_info['test']) |
| 69 return failed_tests |
| 70 |
| 71 def Run(self): |
| 72 """Runs the tests in |self._tests| on separate VMs in parallel.""" |
| 73 spawned_tests = self._SpawnTests() |
| 74 failed_tests = self._WaitForCompletion(spawned_tests) |
| 75 if failed_tests: Die('Tests failed: %r' % failed_tests) |
| 76 |
| 77 |
| 78 def main(): |
| 79 usage = 'Usage: %prog [options] tests...' |
| 80 parser = optparse.OptionParser(usage=usage) |
| 81 (options, args) = parser.parse_args() |
| 82 |
| 83 if not args: |
| 84 parser.print_help() |
| 85 Die('no tests provided') |
| 86 |
| 87 runner = ParallelTestRunner(args) |
| 88 runner.Run() |
| 89 |
| 90 |
| 91 if __name__ == '__main__': |
| 92 main() |
OLD | NEW |