Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Implements test sharding logic.""" | |
| 6 | |
| 7 import logging | |
| 8 import Queue | |
| 9 import sys | |
| 10 import threading | |
| 11 | |
| 12 from pylib import android_commands | |
| 13 from pylib.base import test_result | |
| 14 from pylib import forwarder | |
| 15 | |
| 16 | |
| 17 class _Worker(threading.Thread): | |
| 18 """Runs tests from the test_queue using the given runner in a separate thread. | |
| 19 | |
| 20 Places results in the results_list. | |
| 21 """ | |
| 22 def __init__(self, runner, test_queue, out_results, out_retry): | |
| 23 """Initializes the worker. | |
| 24 | |
| 25 Args: | |
| 26 runner: A TestRunner object used to run the tests. | |
| 27 test_queue: A Queue.Queue from which to get Tests to run. | |
| 28 out_results: A list to add TestResults to. | |
| 29 out_retry: A list to add tests to retry. | |
| 30 """ | |
| 31 super(_Worker, self).__init__() | |
| 32 self.daemon = True | |
| 33 self._exc_info = None | |
| 34 self._runner = runner | |
| 35 self._test_queue = test_queue | |
| 36 self._out_results = out_results | |
| 37 self._out_retry = out_retry | |
| 38 | |
| 39 def run(self): | |
| 40 """Run tests from the queue until it is empty, storing results. | |
| 41 | |
| 42 Overriden from threading.Thread, runs in a separate thread. | |
| 43 """ | |
| 44 try: | |
| 45 while True: | |
| 46 test = self._test_queue.get(block=False) | |
| 47 result, retry = self._runner.Run(test) | |
| 48 self._out_results.append(result) | |
| 49 if retry: | |
| 50 self._out_retry.append(retry) | |
| 51 except Queue.Empty: | |
| 52 pass | |
| 53 except: | |
| 54 self._exc_info = sys.exc_info() | |
| 55 raise | |
| 56 | |
| 57 def Reraise(self): | |
| 58 """Reraise an exception raised in the thread.""" | |
| 59 if self._exc_info: | |
| 60 raise self._exc_info[0], self._exc_info[1], self._exc_info[2] | |
| 61 | |
| 62 | |
| 63 def _RunAllTests(runners, tests): | |
| 64 """Run all tests using the given TestRunners. | |
| 65 | |
| 66 Args: | |
| 67 runners: a list of TestRunner objects. | |
| 68 tests: a list of Tests to run using the given TestRunners. | |
| 69 | |
| 70 Returns: | |
| 71 Tuple: (list of TestResults, list of tests to retry) | |
| 72 """ | |
| 73 tests_queue = Queue.Queue() | |
| 74 for t in tests: | |
| 75 tests_queue.put(t) | |
| 76 workers = [] | |
| 77 results = [] | |
| 78 retry = [] | |
| 79 for r in runners: | |
| 80 worker = _Worker(r, tests_queue, results, retry) | |
| 81 worker.start() | |
| 82 workers.append(worker) | |
| 83 for w in workers: | |
| 84 w.join() | |
| 85 w.Reraise() | |
| 86 return (results, retry) | |
| 87 | |
| 88 | |
| 89 def _CreateRunners(runner_factory, devices): | |
| 90 """Creates a test runner for each device. | |
| 91 | |
| 92 Args: | |
| 93 runner_factory: callable that takes a device and returns a TestRunner. | |
| 94 devices: list of device serial numbers as strings. | |
| 95 """ | |
| 96 test_runners = [] | |
| 97 for index, device in enumerate(devices): | |
| 98 logging.warning('*' * 80) | |
| 99 logging.warning('Creating shard %d for %s', index, device) | |
| 100 logging.warning('*' * 80) | |
| 101 try: | |
| 102 test_runners.append(runner_factory(device)) | |
| 103 except android_commands.errors.DeviceUnresponsiveError as e: | |
|
frankf
2013/02/19 22:48:50
Is there a point to this except, we catch DeviceUn
craigdh
2013/02/19 23:38:30
This is called before the try block that line 132
| |
| 104 logging.critical('****Failed to create a shard: [%s]', e) | |
| 105 return test_runners | |
| 106 | |
| 107 | |
| 108 def Shard(runner_factory, devices, tests, build_type='Debug'): | |
| 109 """Run all tests on attached devices, retrying tests that don't pass. | |
| 110 | |
| 111 Args: | |
| 112 runner_factory: callable that takes a device and returns a TestRunner. | |
| 113 devices: list of attached device serial numbers as strings. | |
| 114 tests: list of Test objects to run. | |
|
frankf
2013/02/19 22:48:50
tests is just a list of strings.
craigdh
2013/02/19 23:38:30
Good catch. Originally it wasn't going to be. Fixe
| |
| 115 build_type: either 'Debug' or 'Release'. | |
|
frankf
2013/02/19 22:48:50
Side note: This is another global config that is t
| |
| 116 """ | |
| 117 final_results = test_result.TestResults() | |
| 118 forwarder.Forwarder.KillHost(build_type) | |
| 119 retries = 3 | |
| 120 tries = 0 | |
| 121 while tests: | |
| 122 devices = set(devices).intersection(android_commands.GetAttachedDevices()) | |
| 123 if not devices or tries > retries: | |
|
frankf
2013/02/19 22:48:50
tries >= retires?
craigdh
2013/02/19 23:38:30
Depends, should retries count the first try or jus
| |
| 124 results.ok = final_results.ok | |
| 125 final_results = results | |
| 126 break | |
| 127 tries += 1 | |
| 128 runners = _CreateRunners(runner_factory, devices) | |
| 129 try: | |
| 130 results_list, tests = _RunAllTests(runners, tests) | |
| 131 results = test_result.TestResults.FromTestResults(results_list) | |
| 132 except android_commands.errors.DeviceUnresponsiveError as e: | |
| 133 logging.critical('****Failed to run test: [%s]', e) | |
| 134 continue | |
| 135 final_results.ok += results.ok | |
| 136 forwarder.Forwarder.KillHost(build_type) | |
| 137 return final_results | |
| OLD | NEW |