1# Copyright 2020 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 5import logging 6from collections import OrderedDict 7import os 8 9import result_sink_util 10 11LOGGER = logging.getLogger(__name__) 12 13 14class StdJson(): 15 16 def __init__(self, **kwargs): 17 """Module for storing the results in standard JSON format. 18 19 https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md 20 """ 21 22 self.tests = OrderedDict() 23 self.result_sink = result_sink_util.ResultSinkClient() 24 self._shard_index = os.getenv('GTEST_SHARD_INDEX', 0) 25 26 if 'passed' in kwargs: 27 self.mark_all_passed(kwargs['passed']) 28 if 'failed' in kwargs: 29 self.mark_all_failed(kwargs['failed']) 30 if 'flaked' in kwargs: 31 self.mark_all_passed(kwargs['flaked'], flaky=True) 32 33 def _init_test(self, expected, actual, is_unexpected=False): 34 """Returns a dict of test result info used as values in self.tests dict.""" 35 test = { 36 'expected': expected, 37 'actual': actual, 38 'shard': self._shard_index, 39 } 40 if is_unexpected: 41 test['is_unexpected'] = True 42 43 return test 44 45 def finalize(self): 46 """Teardown and finalizing tasks needed after all results are reported.""" 47 LOGGER.info('Finalizing in standard json util.') 48 self.result_sink.close() 49 50 def mark_passed(self, test, flaky=False): 51 """Sets test as passed 52 53 Params: 54 test (str): a test in format "{TestCase}/{testMethod}" 55 56 If flaky=True, or if 'FAIL' already set in 'actual', 57 apply is_flaky=True for all test(s). 58 """ 59 if not test: 60 LOGGER.warn('Empty or None test name passed to standard_json_util') 61 return 62 63 result_sink_test_result = result_sink_util.compose_test_result( 64 test, 'PASS', True) 65 self.result_sink.post(result_sink_test_result) 66 67 if test in self.tests: 68 self.tests[test]['actual'] = self.tests[test]['actual'] + " PASS" 69 else: 70 self.tests[test] = self._init_test('PASS', 'PASS') 71 72 if flaky or 'FAIL' in self.tests[test]['actual']: 73 self.tests[test]['is_flaky'] = True 74 75 self.tests[test].pop('is_unexpected', None) 76 77 def mark_all_passed(self, tests, flaky=False): 78 """Marks all tests as PASS""" 79 for test in tests: 80 self.mark_passed(test, flaky) 81 82 def mark_failed(self, test, test_log=None): 83 """Sets test(s) as failed. 84 85 Params: 86 test (str): a test in format "{TestCase}/{testMethod}" 87 test_log (str): log of the specific test 88 """ 89 if not test: 90 LOGGER.warn('Empty or None test name passed to standard_json_util') 91 return 92 93 result_sink_test_result = result_sink_util.compose_test_result( 94 test, 'FAIL', False, test_log=test_log) 95 self.result_sink.post(result_sink_test_result) 96 97 if test in self.tests: 98 self.tests[test]['actual'] = self.tests[test]['actual'] + " FAIL" 99 self.tests[test]['is_unexpected'] = True 100 else: 101 self.tests[test] = self._init_test('PASS', 'FAIL', True) 102 103 def mark_all_failed(self, tests): 104 """Marks all tests as FAIL""" 105 for test in tests: 106 self.mark_failed(test) 107 108 def mark_skipped(self, test): 109 """Sets test(s) as expected SKIP. 110 111 Params: 112 test (str): a test in format "{TestCase}/{testMethod}" 113 """ 114 if not test: 115 LOGGER.warn('Empty or None test name passed to standard_json_util') 116 return 117 118 result_sink_test_result = result_sink_util.compose_test_result( 119 test, 'SKIP', True, tags=[('disabled_test', 'true')]) 120 self.result_sink.post(result_sink_test_result) 121 122 self.tests[test] = self._init_test('SKIP', 'SKIP') 123 124 def mark_all_skipped(self, tests): 125 for test in tests: 126 self.mark_skipped(test) 127 128 def mark_timeout(self, test): 129 """Sets test as TIMEOUT, which is used to indicate a test abort/timeout 130 131 Params: 132 test (str): a test in format "{TestCase}/{testMethod}" 133 """ 134 if not test: 135 LOGGER.warn('Empty or None test name passed to standard_json_util') 136 return 137 138 # Timeout tests in iOS test runner are tests that's unexpectedly not run. 139 test_log = ('The test is compiled in test target but was unexpectedly not' 140 ' run or not finished.') 141 result_sink_test_result = result_sink_util.compose_test_result( 142 test, 143 'SKIP', 144 False, 145 test_log=test_log, 146 tags=[('disabled_test', 'false')]) 147 self.result_sink.post(result_sink_test_result) 148 149 if test in self.tests: 150 self.tests[test]['actual'] = self.tests[test]['actual'] + " TIMEOUT" 151 self.tests[test]['is_unexpected'] = True 152 else: 153 self.tests[test] = self._init_test('PASS', 'TIMEOUT', True) 154