blob: 4a165715f403d4f36a15c2dd9e3546651bb8a4da [file] [log] [blame]
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
from collections import OrderedDict
import os
import result_sink_util
LOGGER = logging.getLogger(__name__)
class StdJson():
def __init__(self, **kwargs):
"""Module for storing the results in standard JSON format.
https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md
"""
self.tests = OrderedDict()
self.result_sink = result_sink_util.ResultSinkClient()
self._shard_index = os.getenv('GTEST_SHARD_INDEX', 0)
if 'passed' in kwargs:
self.mark_all_passed(kwargs['passed'])
if 'failed' in kwargs:
self.mark_all_failed(kwargs['failed'])
if 'flaked' in kwargs:
self.mark_all_passed(kwargs['flaked'], flaky=True)
def _init_test(self, expected, actual, is_unexpected=False):
"""Returns a dict of test result info used as values in self.tests dict."""
test = {
'expected': expected,
'actual': actual,
'shard': self._shard_index,
}
if is_unexpected:
test['is_unexpected'] = True
return test
def finalize(self):
"""Teardown and finalizing tasks needed after all results are reported."""
LOGGER.info('Finalizing in standard json util.')
self.result_sink.close()
def mark_passed(self, test, flaky=False):
"""Sets test as passed
Params:
test (str): a test in format "{TestCase}/{testMethod}"
If flaky=True, or if 'FAIL' already set in 'actual',
apply is_flaky=True for all test(s).
"""
if not test:
LOGGER.warn('Empty or None test name passed to standard_json_util')
return
self.result_sink.post(test, 'PASS', True)
if test in self.tests:
self.tests[test]['actual'] = self.tests[test]['actual'] + " PASS"
else:
self.tests[test] = self._init_test('PASS', 'PASS')
if flaky or 'FAIL' in self.tests[test]['actual']:
self.tests[test]['is_flaky'] = True
self.tests[test].pop('is_unexpected', None)
def mark_all_passed(self, tests, flaky=False):
"""Marks all tests as PASS"""
for test in tests:
self.mark_passed(test, flaky)
def mark_failed(self, test, test_log=None):
"""Sets test(s) as failed.
Params:
test (str): a test in format "{TestCase}/{testMethod}"
test_log (str): log of the specific test
"""
if not test:
LOGGER.warn('Empty or None test name passed to standard_json_util')
return
self.result_sink.post(test, 'FAIL', False, test_log=test_log)
if test in self.tests:
self.tests[test]['actual'] = self.tests[test]['actual'] + " FAIL"
self.tests[test]['is_unexpected'] = True
else:
self.tests[test] = self._init_test('PASS', 'FAIL', True)
def mark_all_failed(self, tests):
"""Marks all tests as FAIL"""
for test in tests:
self.mark_failed(test)
def mark_disabled(self, test):
"""Sets test(s) as expected SKIP with disabled test label.
Params:
test (str): a test in format "{TestCase}/{testMethod}"
"""
if not test:
LOGGER.warn('Empty or None test name passed to standard_json_util')
return
self.result_sink.post(test, 'SKIP', True, tags=[('disabled_test', 'true')])
self.tests[test] = self._init_test('SKIP', 'SKIP')
def mark_all_disabled(self, tests):
for test in tests:
self.mark_disabled(test)
def mark_timeout(self, test):
"""Sets test as TIMEOUT, which is used to indicate a test abort/timeout
Params:
test (str): a test in format "{TestCase}/{testMethod}"
"""
if not test:
LOGGER.warn('Empty or None test name passed to standard_json_util')
return
# Timeout tests in iOS test runner are tests that's unexpectedly not run.
test_log = ('The test is compiled in test target but was unexpectedly not'
' run or not finished.')
self.result_sink.post(
test,
'SKIP',
False,
test_log=test_log,
tags=[('disabled_test', 'false')])
if test in self.tests:
self.tests[test]['actual'] = self.tests[test]['actual'] + " TIMEOUT"
self.tests[test]['is_unexpected'] = True
else:
self.tests[test] = self._init_test('PASS', 'TIMEOUT', True)