| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 # Copyright (c) 2013 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 sys | 
|  | 9 import threading | 
|  | 10 | 
|  | 11 from pylib import android_commands | 
|  | 12 from pylib import forwarder | 
|  | 13 | 
|  | 14 import test_result | 
|  | 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 out_results. | 
|  | 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 list 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   #override | 
|  | 40   def run(self): | 
|  | 41     """Run tests from the queue in a seperate thread until it is empty. | 
|  | 42 | 
|  | 43     Adds TestResults objects to the out_results list and may add tests to the | 
|  | 44     out_retry list. | 
|  | 45     """ | 
|  | 46     try: | 
|  | 47       while True: | 
|  | 48         test = self._test_queue.pop() | 
|  | 49         result, retry = self._runner.Run(test) | 
|  | 50         self._out_results.append(result) | 
|  | 51         if retry: | 
|  | 52           self._out_retry.append(retry) | 
|  | 53     except IndexError: | 
|  | 54       pass | 
|  | 55     except: | 
|  | 56       self._exc_info = sys.exc_info() | 
|  | 57       raise | 
|  | 58 | 
|  | 59   def ReraiseIfException(self): | 
|  | 60     """Reraise exception if an exception was raised in the thread.""" | 
|  | 61     if self._exc_info: | 
|  | 62       raise self._exc_info[0], self._exc_info[1], self._exc_info[2] | 
|  | 63 | 
|  | 64 | 
|  | 65 def _RunAllTests(runners, tests): | 
|  | 66   """Run all tests using the given TestRunners. | 
|  | 67 | 
|  | 68   Args: | 
|  | 69     runners: a list of TestRunner objects. | 
|  | 70     tests: a list of Tests to run using the given TestRunners. | 
|  | 71 | 
|  | 72   Returns: | 
|  | 73     Tuple: (list of TestResults, list of tests to retry) | 
|  | 74   """ | 
|  | 75   tests_queue = list(tests) | 
|  | 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   while workers: | 
|  | 84     for w in workers[:]: | 
|  | 85       # Allow the main thread to periodically check for keyboard interrupts. | 
|  | 86       w.join(0.1) | 
|  | 87       if not w.isAlive(): | 
|  | 88         w.ReraiseIfException() | 
|  | 89         workers.remove(w) | 
|  | 90   return (results, retry) | 
|  | 91 | 
|  | 92 | 
|  | 93 def _CreateRunners(runner_factory, devices): | 
|  | 94   """Creates a test runner for each device. | 
|  | 95 | 
|  | 96   Note: if a device is unresponsive the corresponding TestRunner will not be | 
|  | 97     included in the returned list. | 
|  | 98 | 
|  | 99   Args: | 
|  | 100     runner_factory: callable that takes a device and returns a TestRunner. | 
|  | 101     devices: list of device serial numbers as strings. | 
|  | 102 | 
|  | 103   Returns: | 
|  | 104     A list of TestRunner objects. | 
|  | 105   """ | 
|  | 106   test_runners = [] | 
|  | 107   for index, device in enumerate(devices): | 
|  | 108     logging.warning('*' * 80) | 
|  | 109     logging.warning('Creating shard %d for %s', index, device) | 
|  | 110     logging.warning('*' * 80) | 
|  | 111     try: | 
|  | 112       test_runners.append(runner_factory(device)) | 
|  | 113     except android_commands.errors.DeviceUnresponsiveError as e: | 
|  | 114       logging.warning('****Failed to create a shard: [%s]', e) | 
|  | 115   return test_runners | 
|  | 116 | 
|  | 117 | 
|  | 118 def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug', | 
|  | 119                      tries=3): | 
|  | 120   """Run all tests on attached devices, retrying tests that don't pass. | 
|  | 121 | 
|  | 122   Args: | 
|  | 123     runner_factory: callable that takes a device and returns a TestRunner. | 
|  | 124     devices: list of attached device serial numbers as strings. | 
|  | 125     tests: list of tests to run. | 
|  | 126     build_type: either 'Debug' or 'Release'. | 
|  | 127     tries: number of tries before accepting failure. | 
|  | 128 | 
|  | 129   Returns: | 
|  | 130     A test_result.TestResults object. | 
|  | 131   """ | 
|  | 132   final_results = test_result.TestResults() | 
|  | 133   results = test_result.TestResults() | 
|  | 134   forwarder.Forwarder.KillHost(build_type) | 
|  | 135   try_count = 0 | 
|  | 136   while tests: | 
|  | 137     devices = set(devices).intersection(android_commands.GetAttachedDevices()) | 
|  | 138     if not devices: | 
|  | 139       # There are no visible devices attached, this is unrecoverable. | 
|  | 140       msg = 'No devices attached and visible to run tests!' | 
|  | 141       logging.critical(msg) | 
|  | 142       raise Exception(msg) | 
|  | 143     if try_count >= tries: | 
|  | 144       # We've retried too many times, return the TestResults up to this point. | 
|  | 145       results.ok = final_results.ok | 
|  | 146       final_results = results | 
|  | 147       break | 
|  | 148     try_count += 1 | 
|  | 149     runners = _CreateRunners(runner_factory, devices) | 
|  | 150     try: | 
|  | 151       results_list, tests = _RunAllTests(runners, tests) | 
|  | 152       results = test_result.TestResults.FromTestResults(results_list) | 
|  | 153       final_results.ok += results.ok | 
|  | 154     except android_commands.errors.DeviceUnresponsiveError as e: | 
|  | 155       logging.warning('****Failed to run test: [%s]', e) | 
|  | 156   forwarder.Forwarder.KillHost(build_type) | 
|  | 157   return final_results | 
| OLD | NEW | 
|---|