# Copyright 2018 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. """Common file shared by test_push of autotest and skylab. autotest: site_utils/test_push.py skylab: venv/skylab_staging/test_push.py """ import collections import re # Dictionary of test results keyed by test name regular expression. EXPECTED_TEST_RESULTS = {'^SERVER_JOB$': ['GOOD'], # This is related to dummy_Fail/control.dependency. 'dummy_Fail.dependency$': ['TEST_NA'], 'login_LoginSuccess.*': ['GOOD'], 'provision_AutoUpdate.double': ['GOOD'], 'dummy_Pass.*': ['GOOD'], 'dummy_Fail.Fail$': ['FAIL'], 'dummy_Fail.Error$': ['ERROR'], 'dummy_Fail.Warn$': ['WARN'], 'dummy_Fail.NAError$': ['TEST_NA'], 'dummy_Fail.Crash$': ['GOOD'], 'autotest_SyncCount$': ['GOOD'], } EXPECTED_TEST_RESULTS_DUMMY = {'^SERVER_JOB$': ['GOOD'], 'dummy_Pass.*': ['GOOD'], 'dummy_Fail.Fail': ['FAIL'], 'dummy_Fail.Warn': ['WARN'], 'dummy_Fail.Crash': ['GOOD'], 'dummy_Fail.Error': ['ERROR'], 'dummy_Fail.NAError': ['TEST_NA'], } EXPECTED_TEST_RESULTS_POWERWASH = {'platform_Powerwash': ['GOOD'], 'SERVER_JOB': ['GOOD'], } _TestPushErrors = collections.namedtuple( '_TestPushErrors', [ 'mismatch_errors', 'unknown_tests', 'missing_tests', ] ) def summarize_push(test_views, expected_results, ignored_tests=[]): """Summarize the test push errors.""" test_push_errors = _match_test_results(test_views, expected_results, ignored_tests) return _generate_push_summary(test_push_errors) def _match_test_results(test_views, expected_results, ignored_tests): """Match test results with expected results. @param test_views: A defaultdict where keys are test names and values are lists of test statuses, e.g., {'dummy_Fail.Error': ['ERROR', 'ERROR], 'dummy_Fail.NAError': ['TEST_NA'], 'dummy_Fail.RetrySuccess': ['ERROR', 'GOOD'], } @param expected_results: A dictionary of test name to expected test result. Has the same format as test_views. @param ignored_tests: A list of test name patterns. Any mismatch between test results and expected test results that matches one these patterns is ignored. @return: A _TestPushErrors tuple. """ mismatch_errors = [] unknown_tests = [] found_keys = set() for test_name, test_status_list in test_views.iteritems(): test_found = False for test_name_pattern, expected_result in expected_results.items(): if re.search(test_name_pattern, test_name): test_found = True found_keys.add(test_name_pattern) if (sorted(expected_result) != sorted(test_status_list) and _is_significant(test_name, ignored_tests)): error = ('%s Expected: %s, Actual: %s' % (test_name, expected_result, test_status_list)) mismatch_errors.append(error) if not test_found and _is_significant(test_name, ignored_tests): unknown_tests.append(test_name) missing_tests = set(expected_results.keys()) - found_keys missing_tests = [t for t in missing_tests if _is_significant(t, ignored_tests)] return _TestPushErrors(mismatch_errors=mismatch_errors, unknown_tests=unknown_tests, missing_tests=missing_tests) def _is_significant(test, ignored_tests_patterns): return all([test not in m for m in ignored_tests_patterns]) def _generate_push_summary(test_push_errors): """Generate a list of summary based on the test_push results.""" summary = [] if test_push_errors.mismatch_errors: summary.append(('Results of %d test(s) do not match expected ' 'values:') % len(test_push_errors.mismatch_errors)) summary.extend(test_push_errors.mismatch_errors) summary.append('\n') if test_push_errors.unknown_tests: summary.append('%d test(s) are not expected to be run:' % len(test_push_errors.unknown_tests)) summary.extend(test_push_errors.unknown_tests) summary.append('\n') if test_push_errors.missing_tests: summary.append('%d test(s) are missing from the results:' % len(test_push_errors.missing_tests)) summary.extend(test_push_errors.missing_tests) summary.append('\n') return summary