+ full_results = {}
+ full_results['interrupted'] = False
+ full_results['path_delimiter'] = TEST_SEPARATOR
+ full_results['version'] = 3
+ full_results['seconds_since_epoch'] = time.time()
+ for md in metadata:
+ key, val = md.split('=', 1)
+ full_results[key] = val
+
+ all_test_names = _AllTestNames(suite)
+ failed_test_names = _FailedTestNames(result)
+
+ full_results['num_failures_by_type'] = {
+ 'FAIL': len(failed_test_names),
+ 'PASS': len(all_test_names) - len(failed_test_names),
+ }
+
+ full_results['tests'] = {}
+
+ for test_name in all_test_names:
+ value = {}
+ value['expected'] = 'PASS'
+ if test_name in failed_test_names:
+ value['actual'] = 'FAIL'
+ value['is_unexpected'] = True
+ else:
+ value['actual'] = 'PASS'
+ _AddPathToTrie(full_results['tests'], test_name, value)
+
+ return full_results
+
+
+def _AllTestNames(suite):
+ test_names = []
+ # _tests is protected pylint: disable=W0212
+ for test in suite._tests:
+ if isinstance(test, unittest.suite.TestSuite):
+ test_names.extend(_AllTestNames(test))
+ else:
+ test_names.append(test.id())
+ return test_names
+
+
+def _FailedTestNames(result):
+ return set(test.id() for test, _ in result.failures + result.errors)
+
+
+def _AddPathToTrie(trie, path, value):
+ if TEST_SEPARATOR not in path:
+ trie[path] = value
+ return
+ directory, rest = path.split(TEST_SEPARATOR, 1)
+ if directory not in trie:
+ trie[directory] = {}
+ _AddPathToTrie(trie[directory], rest, value)