Index: tools/telemetry/telemetry/unittest/json_results.py |
diff --git a/tools/telemetry/telemetry/unittest/json_results.py b/tools/telemetry/telemetry/unittest/json_results.py |
index 80d7cbe128401313c369a92c4b814472f7d37f9c..256e5c93efecd3d9773c6ce337a606abe3bb0357 100644 |
--- a/tools/telemetry/telemetry/unittest/json_results.py |
+++ b/tools/telemetry/telemetry/unittest/json_results.py |
@@ -2,7 +2,9 @@ |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import functools |
import json |
+import re |
import time |
import unittest |
import urllib2 |
@@ -87,7 +89,8 @@ def FullResults(args, suite, results): |
all_test_names = AllTestNames(suite) |
sets_of_passing_test_names = map(PassingTestNames, results) |
- sets_of_failing_test_names = map(FailedTestNames, results) |
+ sets_of_failing_test_names = map(functools.partial(FailedTestNames, suite), |
+ results) |
# TODO(crbug.com/405379): This handles tests that are skipped via the |
# unittest skip decorators (like skipUnless). The tests that are skipped via |
@@ -97,7 +100,7 @@ def FullResults(args, suite, results): |
- sets_of_failing_test_names[0]) |
num_tests = len(all_test_names) |
- num_failures = NumFailuresAfterRetries(results) |
+ num_failures = NumFailuresAfterRetries(suite, results) |
num_skips = len(skipped_tests) |
num_passes = num_tests - num_failures - num_skips |
full_results['num_failures_by_type'] = { |
@@ -160,12 +163,36 @@ def AllTestNames(suite): |
return test_names |
-def NumFailuresAfterRetries(results): |
- return len(FailedTestNames(results[-1])) |
+def NumFailuresAfterRetries(suite, results): |
+ return len(FailedTestNames(suite, results[-1])) |
-def FailedTestNames(result): |
- return set(test.id() for test, _ in result.failures + result.errors) |
+def FailedTestNames(suite, result): |
+ failed_test_names = set() |
+ for test, error in result.failures + result.errors: |
+ if isinstance(test, unittest.TestCase): |
+ failed_test_names.add(test.id()) |
+ elif isinstance(test, unittest.suite._ErrorHolder): # pylint: disable=W0212 |
+ # If there's an error in setUpClass or setUpModule, unittest gives us an |
+ # _ErrorHolder object. We can parse the object's id for the class or |
+ # module that failed, then find all tests in that class or module. |
+ match = re.match('setUp[a-zA-Z]+ \\((.+)\\)', test.id()) |
+ assert match, "Don't know how to retry after this error:\n%s" % error |
+ module_or_class = match.groups()[0] |
+ failed_test_names |= _FindChildren(module_or_class, AllTestNames(suite)) |
+ else: |
+ assert False, 'Unknown test type: %s' % test.__class__ |
+ return failed_test_names |
+ |
+ |
+def _FindChildren(parent, potential_children): |
+ children = set() |
+ parent_name_parts = parent.split('.') |
+ for potential_child in potential_children: |
+ child_name_parts = potential_child.split('.') |
+ if parent_name_parts == child_name_parts[:len(parent_name_parts)]: |
+ children.add(potential_child) |
+ return children |
def PassingTestNames(result): |