1*b7893ccfSSadaf Ebrahimi#!/usr/bin/python3 2*b7893ccfSSadaf Ebrahimi# 3*b7893ccfSSadaf Ebrahimi# Copyright (c) 2018 Google Inc. 4*b7893ccfSSadaf Ebrahimi# 5*b7893ccfSSadaf Ebrahimi# Licensed under the Apache License, Version 2.0 (the "License"); 6*b7893ccfSSadaf Ebrahimi# you may not use this file except in compliance with the License. 7*b7893ccfSSadaf Ebrahimi# You may obtain a copy of the License at 8*b7893ccfSSadaf Ebrahimi# 9*b7893ccfSSadaf Ebrahimi# http://www.apache.org/licenses/LICENSE-2.0 10*b7893ccfSSadaf Ebrahimi# 11*b7893ccfSSadaf Ebrahimi# Unless required by applicable law or agreed to in writing, software 12*b7893ccfSSadaf Ebrahimi# distributed under the License is distributed on an "AS IS" BASIS, 13*b7893ccfSSadaf Ebrahimi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*b7893ccfSSadaf Ebrahimi# See the License for the specific language governing permissions and 15*b7893ccfSSadaf Ebrahimi# limitations under the License. 16*b7893ccfSSadaf Ebrahimi# 17*b7893ccfSSadaf Ebrahimi# Author: William Henning <[email protected]> 18*b7893ccfSSadaf Ebrahimi# 19*b7893ccfSSadaf Ebrahimi# This script parses the validation layers test continuous integration ouput 20*b7893ccfSSadaf Ebrahimi# and reports the number of tests that passed, failured, ouput unexpected errors, 21*b7893ccfSSadaf Ebrahimi# or were skipped. As such, the script is only designed to parse the ouput 22*b7893ccfSSadaf Ebrahimi# generated by the existing CI implementation. 23*b7893ccfSSadaf Ebrahimi# 24*b7893ccfSSadaf Ebrahimi# usage: 25*b7893ccfSSadaf Ebrahimi# for profile in tests/device_profiles/*.json; do echo Testing with 26*b7893ccfSSadaf Ebrahimi# profile $profile; VK_LAYER_PATH=DEVSIM_AND_VALIDATION_PATHS 27*b7893ccfSSadaf Ebrahimi# VK_DEVSIM_FILE=$profile VK_ICD_FILENAMES=MOCK_ICD_PATH 28*b7893ccfSSadaf Ebrahimi# ./build/tests/vk_layer_validation_tests --devsim; done 29*b7893ccfSSadaf Ebrahimi# | python3 parse_test_results.py [--fail_on_skip] [--fail_on_unexpected] 30*b7893ccfSSadaf Ebrahimi# 31*b7893ccfSSadaf Ebrahimi# --fail_on_skip causes the script to exit with a non-zero exit code if a test 32*b7893ccfSSadaf Ebrahimi# didn't run on any device profile 33*b7893ccfSSadaf Ebrahimi# 34*b7893ccfSSadaf Ebrahimi# --fail_on_unexpected causes the script to exit with a non-zero exit code if 35*b7893ccfSSadaf Ebrahimi# a test printed unexpected errors 36*b7893ccfSSadaf Ebrahimi# 37*b7893ccfSSadaf Ebrahimi 38*b7893ccfSSadaf Ebrahimiimport argparse 39*b7893ccfSSadaf Ebrahimiimport re 40*b7893ccfSSadaf Ebrahimiimport sys 41*b7893ccfSSadaf Ebrahimifrom collections import defaultdict 42*b7893ccfSSadaf Ebrahimi 43*b7893ccfSSadaf Ebrahimiclass OutputStats(object): 44*b7893ccfSSadaf Ebrahimi def __init__(self): 45*b7893ccfSSadaf Ebrahimi self.current_profile = "" 46*b7893ccfSSadaf Ebrahimi self.current_test = "" 47*b7893ccfSSadaf Ebrahimi self.current_test_output = "" 48*b7893ccfSSadaf Ebrahimi self.test_results = defaultdict(defaultdict) 49*b7893ccfSSadaf Ebrahimi self.unexpected_errors = defaultdict(defaultdict) 50*b7893ccfSSadaf Ebrahimi 51*b7893ccfSSadaf Ebrahimi def match(self, line): 52*b7893ccfSSadaf Ebrahimi self.new_profile_match(line) 53*b7893ccfSSadaf Ebrahimi self.test_suite_end_match(line) 54*b7893ccfSSadaf Ebrahimi self.start_test_match(line) 55*b7893ccfSSadaf Ebrahimi if self.current_test != "": 56*b7893ccfSSadaf Ebrahimi self.current_test_output += line 57*b7893ccfSSadaf Ebrahimi self.skip_test_match(line) 58*b7893ccfSSadaf Ebrahimi self.pass_test_match(line) 59*b7893ccfSSadaf Ebrahimi self.fail_test_match(line) 60*b7893ccfSSadaf Ebrahimi self.unexpected_error_match(line) 61*b7893ccfSSadaf Ebrahimi 62*b7893ccfSSadaf Ebrahimi def print_summary(self, skip_is_failure, unexpected_is_failure): 63*b7893ccfSSadaf Ebrahimi if self.current_test != "": 64*b7893ccfSSadaf Ebrahimi self.test_died() 65*b7893ccfSSadaf Ebrahimi 66*b7893ccfSSadaf Ebrahimi passed_tests = 0 67*b7893ccfSSadaf Ebrahimi skipped_tests = 0 68*b7893ccfSSadaf Ebrahimi failed_tests = 0 69*b7893ccfSSadaf Ebrahimi unexpected_error_tests = 0 70*b7893ccfSSadaf Ebrahimi did_fail = False 71*b7893ccfSSadaf Ebrahimi 72*b7893ccfSSadaf Ebrahimi for test_name, results in self.test_results.items(): 73*b7893ccfSSadaf Ebrahimi skipped_profiles = 0 74*b7893ccfSSadaf Ebrahimi passed_profiles = 0 75*b7893ccfSSadaf Ebrahimi failed_profiles = 0 76*b7893ccfSSadaf Ebrahimi aborted_profiles = 0 77*b7893ccfSSadaf Ebrahimi unexpected_error_profiles = 0 78*b7893ccfSSadaf Ebrahimi for profile, result in results.items(): 79*b7893ccfSSadaf Ebrahimi if result == "pass": 80*b7893ccfSSadaf Ebrahimi passed_profiles += 1 81*b7893ccfSSadaf Ebrahimi if result == "fail": 82*b7893ccfSSadaf Ebrahimi failed_profiles += 1 83*b7893ccfSSadaf Ebrahimi if result == "skip": 84*b7893ccfSSadaf Ebrahimi skipped_profiles += 1 85*b7893ccfSSadaf Ebrahimi if self.unexpected_errors.get(test_name, {}).get(profile, "") == "true": 86*b7893ccfSSadaf Ebrahimi unexpected_error_profiles += 1 87*b7893ccfSSadaf Ebrahimi if failed_profiles != 0: 88*b7893ccfSSadaf Ebrahimi print("TEST FAILED:", test_name) 89*b7893ccfSSadaf Ebrahimi failed_tests += 1 90*b7893ccfSSadaf Ebrahimi elif skipped_profiles == len(results): 91*b7893ccfSSadaf Ebrahimi print("TEST SKIPPED ALL DEVICES:", test_name) 92*b7893ccfSSadaf Ebrahimi skipped_tests += 1 93*b7893ccfSSadaf Ebrahimi else: 94*b7893ccfSSadaf Ebrahimi passed_tests += 1 95*b7893ccfSSadaf Ebrahimi if unexpected_error_profiles != 0: 96*b7893ccfSSadaf Ebrahimi print("UNEXPECTED ERRORS:", test_name) 97*b7893ccfSSadaf Ebrahimi unexpected_error_tests += 1 98*b7893ccfSSadaf Ebrahimi num_tests = len(self.test_results) 99*b7893ccfSSadaf Ebrahimi print("PASSED: ", passed_tests, "/", num_tests, " tests") 100*b7893ccfSSadaf Ebrahimi if skipped_tests != 0: 101*b7893ccfSSadaf Ebrahimi did_fail |= skip_is_failure 102*b7893ccfSSadaf Ebrahimi print("NEVER RAN: ", skipped_tests, "/", num_tests, " tests") 103*b7893ccfSSadaf Ebrahimi if failed_tests != 0: 104*b7893ccfSSadaf Ebrahimi did_fail = True 105*b7893ccfSSadaf Ebrahimi print("FAILED: ", failed_tests, "/", num_tests, "tests") 106*b7893ccfSSadaf Ebrahimi if unexpected_error_tests != 0: 107*b7893ccfSSadaf Ebrahimi did_fail |= unexpected_is_failure 108*b7893ccfSSadaf Ebrahimi print("UNEXPECTED OUPUT: ", unexpected_error_tests, "/", num_tests, "tests") 109*b7893ccfSSadaf Ebrahimi return did_fail 110*b7893ccfSSadaf Ebrahimi 111*b7893ccfSSadaf Ebrahimi def new_profile_match(self, line): 112*b7893ccfSSadaf Ebrahimi if re.search(r'Testing with profile .*/(.*)', line) is not None: 113*b7893ccfSSadaf Ebrahimi self.current_profile = re.search(r'Testing with profile .*/(.*)', line).group(1) 114*b7893ccfSSadaf Ebrahimi 115*b7893ccfSSadaf Ebrahimi def test_suite_end_match(self, line): 116*b7893ccfSSadaf Ebrahimi if re.search(r'\[-*\]', line) is not None: 117*b7893ccfSSadaf Ebrahimi if self.current_test != "": 118*b7893ccfSSadaf Ebrahimi # Here we see a message that starts [----------] before another test 119*b7893ccfSSadaf Ebrahimi # finished running. This should mean that that other test died. 120*b7893ccfSSadaf Ebrahimi self.test_died() 121*b7893ccfSSadaf Ebrahimi 122*b7893ccfSSadaf Ebrahimi def start_test_match(self, line): 123*b7893ccfSSadaf Ebrahimi if re.search(r'\[ RUN\s*\]', line) is not None: 124*b7893ccfSSadaf Ebrahimi # This parser doesn't handle the case where one test's start comes between another 125*b7893ccfSSadaf Ebrahimi # test's start and result. 126*b7893ccfSSadaf Ebrahimi assert self.current_test == "" 127*b7893ccfSSadaf Ebrahimi self.current_test = re.search(r'] (.*)', line).group(1) 128*b7893ccfSSadaf Ebrahimi self.current_test_output = "" 129*b7893ccfSSadaf Ebrahimi 130*b7893ccfSSadaf Ebrahimi def skip_test_match(self, line): 131*b7893ccfSSadaf Ebrahimi if re.search(r'TEST SKIPPED', line) is not None: 132*b7893ccfSSadaf Ebrahimi self.test_results[self.current_test][self.current_profile] = "skip" 133*b7893ccfSSadaf Ebrahimi 134*b7893ccfSSadaf Ebrahimi def pass_test_match(self, line): 135*b7893ccfSSadaf Ebrahimi if re.search(r'\[\s*OK \]', line) is not None: 136*b7893ccfSSadaf Ebrahimi # If gtest says the test passed, check if it was skipped before marking it passed 137*b7893ccfSSadaf Ebrahimi if self.test_results.get(self.current_test, {}).get(self.current_profile, "") != "skip": 138*b7893ccfSSadaf Ebrahimi self.test_results[self.current_test][self.current_profile] = "pass" 139*b7893ccfSSadaf Ebrahimi self.current_test = "" 140*b7893ccfSSadaf Ebrahimi 141*b7893ccfSSadaf Ebrahimi def fail_test_match(self, line): 142*b7893ccfSSadaf Ebrahimi if re.search(r'\[\s*FAILED\s*\]', line) is not None and self.current_test != "": 143*b7893ccfSSadaf Ebrahimi self.test_results[self.current_test][self.current_profile] = "fail" 144*b7893ccfSSadaf Ebrahimi self.current_test = "" 145*b7893ccfSSadaf Ebrahimi 146*b7893ccfSSadaf Ebrahimi def unexpected_error_match(self, line): 147*b7893ccfSSadaf Ebrahimi if re.search(r'^Unexpected: ', line) is not None: 148*b7893ccfSSadaf Ebrahimi self.unexpected_errors[self.current_test][self.current_profile] = "true" 149*b7893ccfSSadaf Ebrahimi 150*b7893ccfSSadaf Ebrahimi def test_died(self): 151*b7893ccfSSadaf Ebrahimi print("A test likely crashed. Testing is being aborted.") 152*b7893ccfSSadaf Ebrahimi print("Final test output: ") 153*b7893ccfSSadaf Ebrahimi print(self.current_test_output) 154*b7893ccfSSadaf Ebrahimi sys.exit(1) 155*b7893ccfSSadaf Ebrahimi 156*b7893ccfSSadaf Ebrahimidef main(): 157*b7893ccfSSadaf Ebrahimi parser = argparse.ArgumentParser(description='Parse the output from validation layer tests.') 158*b7893ccfSSadaf Ebrahimi parser.add_argument('--fail_on_skip', action='store_true', help="Makes the script exit with a " 159*b7893ccfSSadaf Ebrahimi "non-zero exit code if a test didn't run on any device profile.") 160*b7893ccfSSadaf Ebrahimi parser.add_argument('--fail_on_unexpected', action='store_true', help="Makes the script exit " 161*b7893ccfSSadaf Ebrahimi "with a non-zero exit code if a test causes unexpected errors.") 162*b7893ccfSSadaf Ebrahimi args = parser.parse_args() 163*b7893ccfSSadaf Ebrahimi 164*b7893ccfSSadaf Ebrahimi stats = OutputStats() 165*b7893ccfSSadaf Ebrahimi for line in sys.stdin: 166*b7893ccfSSadaf Ebrahimi stats.match(line) 167*b7893ccfSSadaf Ebrahimi failed = stats.print_summary(args.fail_on_skip, args.fail_on_unexpected) 168*b7893ccfSSadaf Ebrahimi if failed == True: 169*b7893ccfSSadaf Ebrahimi print("\nFAILED CI") 170*b7893ccfSSadaf Ebrahimi sys.exit(1) 171*b7893ccfSSadaf Ebrahimi 172*b7893ccfSSadaf Ebrahimiif __name__ == '__main__': 173*b7893ccfSSadaf Ebrahimi main() 174