OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import logging | 5 import logging |
6 | 6 |
7 from devil.android import device_errors | 7 from devil.android import device_errors |
8 from pylib import valgrind_tools | 8 from pylib import valgrind_tools |
9 from pylib.base import base_test_result | 9 from pylib.base import base_test_result |
10 from pylib.base import test_run | 10 from pylib.base import test_run |
11 from pylib.base import test_collection | 11 from pylib.base import test_collection |
12 | 12 |
13 | 13 |
| 14 def handle_shard_failures(f): |
| 15 """A decorator that handles device failures for per-device functions. |
| 16 |
| 17 Args: |
| 18 f: the function being decorated. The function must take at least one |
| 19 argument, and that argument must be the device. |
| 20 """ |
| 21 def wrapper(dev, *args, **kwargs): |
| 22 try: |
| 23 return f(dev, *args, **kwargs) |
| 24 except device_errors.CommandFailedError: |
| 25 logging.exception('Shard failed: %s(%s)', f.__name__, str(dev)) |
| 26 except device_errors.CommandTimeoutError: |
| 27 logging.exception('Shard timed out: %s(%s)', f.__name__, str(dev)) |
| 28 except device_errors.DeviceUnreachableError: |
| 29 logging.exception('Shard died: %s(%s)', f.__name__, str(dev)) |
| 30 return None |
| 31 |
| 32 return wrapper |
| 33 |
| 34 |
14 class LocalDeviceTestRun(test_run.TestRun): | 35 class LocalDeviceTestRun(test_run.TestRun): |
15 | 36 |
16 def __init__(self, env, test_instance): | 37 def __init__(self, env, test_instance): |
17 super(LocalDeviceTestRun, self).__init__(env, test_instance) | 38 super(LocalDeviceTestRun, self).__init__(env, test_instance) |
18 self._tools = {} | 39 self._tools = {} |
19 | 40 |
20 #override | 41 #override |
21 def RunTests(self): | 42 def RunTests(self): |
22 tests = self._GetTests() | 43 tests = self._GetTests() |
23 | 44 |
| 45 @handle_shard_failures |
24 def run_tests_on_device(dev, tests, results): | 46 def run_tests_on_device(dev, tests, results): |
25 for test in tests: | 47 for test in tests: |
26 try: | 48 try: |
27 result = self._RunTest(dev, test) | 49 result = self._RunTest(dev, test) |
28 if isinstance(result, base_test_result.BaseTestResult): | 50 if isinstance(result, base_test_result.BaseTestResult): |
29 results.AddResult(result) | 51 results.AddResult(result) |
30 elif isinstance(result, list): | 52 elif isinstance(result, list): |
31 results.AddResults(result) | 53 results.AddResults(result) |
32 else: | 54 else: |
33 raise Exception( | 55 raise Exception( |
(...skipping 11 matching lines...) Expand all Loading... |
45 results = base_test_result.TestRunResults() | 67 results = base_test_result.TestRunResults() |
46 all_fail_results = {} | 68 all_fail_results = {} |
47 while tries < self._env.max_tries and tests: | 69 while tries < self._env.max_tries and tests: |
48 logging.info('STARTING TRY #%d/%d', tries + 1, self._env.max_tries) | 70 logging.info('STARTING TRY #%d/%d', tries + 1, self._env.max_tries) |
49 logging.info('Will run %d tests on %d devices: %s', | 71 logging.info('Will run %d tests on %d devices: %s', |
50 len(tests), len(self._env.devices), | 72 len(tests), len(self._env.devices), |
51 ', '.join(str(d) for d in self._env.devices)) | 73 ', '.join(str(d) for d in self._env.devices)) |
52 for t in tests: | 74 for t in tests: |
53 logging.debug(' %s', t) | 75 logging.debug(' %s', t) |
54 | 76 |
55 try: | 77 try_results = base_test_result.TestRunResults() |
56 try_results = base_test_result.TestRunResults() | 78 if self._ShouldShard(): |
57 if self._ShouldShard(): | 79 tc = test_collection.TestCollection(self._CreateShards(tests)) |
58 tc = test_collection.TestCollection(self._CreateShards(tests)) | 80 self._env.parallel_devices.pMap( |
59 self._env.parallel_devices.pMap( | 81 run_tests_on_device, tc, try_results).pGet(None) |
60 run_tests_on_device, tc, try_results).pGet(None) | 82 else: |
61 else: | 83 self._env.parallel_devices.pMap( |
62 self._env.parallel_devices.pMap( | 84 run_tests_on_device, tests, try_results).pGet(None) |
63 run_tests_on_device, tests, try_results).pGet(None) | |
64 except device_errors.CommandFailedError: | |
65 logging.exception('Shard terminated: command failed') | |
66 except device_errors.CommandTimeoutError: | |
67 logging.exception('Shard terminated: command timed out') | |
68 except device_errors.DeviceUnreachableError: | |
69 logging.exception('Shard terminated: device became unreachable') | |
70 | 85 |
71 for result in try_results.GetAll(): | 86 for result in try_results.GetAll(): |
72 if result.GetType() in (base_test_result.ResultType.PASS, | 87 if result.GetType() in (base_test_result.ResultType.PASS, |
73 base_test_result.ResultType.SKIP): | 88 base_test_result.ResultType.SKIP): |
74 results.AddResult(result) | 89 results.AddResult(result) |
75 else: | 90 else: |
76 all_fail_results[result.GetName()] = result | 91 all_fail_results[result.GetName()] = result |
77 | 92 |
78 results_names = set(r.GetName() for r in results.GetAll()) | 93 results_names = set(r.GetName() for r in results.GetAll()) |
79 tests = [t for t in tests if self._GetTestName(t) not in results_names] | 94 tests = [t for t in tests if self._GetTestName(t) not in results_names] |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 return test | 129 return test |
115 | 130 |
116 def _GetTests(self): | 131 def _GetTests(self): |
117 raise NotImplementedError | 132 raise NotImplementedError |
118 | 133 |
119 def _RunTest(self, device, test): | 134 def _RunTest(self, device, test): |
120 raise NotImplementedError | 135 raise NotImplementedError |
121 | 136 |
122 def _ShouldShard(self): | 137 def _ShouldShard(self): |
123 raise NotImplementedError | 138 raise NotImplementedError |
OLD | NEW |