1#!/usr/bin/env python3 2# Copyright 2016 The Chromium Authors 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# https://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16"""Helper script to update the test error expectations based on actual results. 17 18This is useful for regenerating test expectations after making changes to the 19error format. 20 21To use this run the affected tests, and then pass the input to this script 22(either via stdin, or as the first argument). For instance: 23 24 $ ./build/pki_test --gtest_filter="*VerifyCertificateChain*" | \ 25 pki/testdata/verify_certificate_chain_unittest/rebase-errors.py 26 27The script works by scanning the stdout looking for gtest failures having a 28particular format. The C++ test side should have been instrumented to dump out 29the test file's path on mismatch. 30 31This script will then update the corresponding test/error file that contains the 32error expectation. 33""" 34 35import os 36import sys 37import re 38 39# Regular expression to find the failed errors in test stdout. 40# * Group 1 of the match is file path (relative to //src) where the 41# expected errors were read from. 42# * Group 2 of the match is the actual error text 43failed_test_regex = re.compile(r""" 44Cert path errors don't match expectations \((.+?)\) 45 46EXPECTED: 47 48(?:.|\n)*? 49ACTUAL: 50 51((?:.|\n)*?) 52===> Use pki/testdata/verify_certificate_chain_unittest/rebase-errors.py to rebaseline. 53""", re.MULTILINE) 54 55 56def read_file_to_string(path): 57 """Reads a file entirely to a string""" 58 with open(path, 'r') as f: 59 return f.read() 60 61 62def write_string_to_file(data, path): 63 """Writes a string to a file""" 64 print("Writing file %s ..." % (path)) 65 with open(path, "w") as f: 66 f.write(data) 67 68 69def get_src_root(): 70 """Returns the path to BoringSSL source tree. This assumes the 71 current script is inside the source tree.""" 72 cur_dir = os.path.dirname(os.path.realpath(__file__)) 73 74 while True: 75 # Check if it looks like the BoringSSL root. 76 if os.path.isdir(os.path.join(cur_dir, "crypto")) and \ 77 os.path.isdir(os.path.join(cur_dir, "pki")) and \ 78 os.path.isdir(os.path.join(cur_dir, "ssl")): 79 return cur_dir 80 parent_dir, _ = os.path.split(cur_dir) 81 if not parent_dir or parent_dir == cur_dir: 82 break 83 cur_dir = parent_dir 84 85 print("Couldn't find src dir") 86 sys.exit(1) 87 88 89def get_abs_path(rel_path): 90 """Converts |rel_path| (relative to src) to a full path""" 91 return os.path.join(get_src_root(), rel_path) 92 93 94def fixup_errors_for_file(actual_errors, test_file_path): 95 """Updates the errors in |test_file_path| to match |actual_errors|""" 96 contents = read_file_to_string(test_file_path) 97 98 header = "\nexpected_errors:\n" 99 index = contents.find(header) 100 if index < 0: 101 print("Couldn't find expected_errors") 102 sys.exit(1) 103 104 # The rest of the file contains the errors (overwrite). 105 contents = contents[0:index] + header + actual_errors 106 107 write_string_to_file(contents, test_file_path) 108 109 110def main(): 111 if len(sys.argv) > 2: 112 print('Usage: %s [path-to-unittest-stdout]' % (sys.argv[0])) 113 sys.exit(1) 114 115 # Read the input either from a file, or from stdin. 116 test_stdout = None 117 if len(sys.argv) == 2: 118 test_stdout = read_file_to_string(sys.argv[1]) 119 else: 120 print('Reading input from stdin...') 121 test_stdout = sys.stdin.read() 122 123 for m in failed_test_regex.finditer(test_stdout): 124 src_relative_errors_path = "pki/" + m.group(1) 125 errors_path = get_abs_path(src_relative_errors_path) 126 actual_errors = m.group(2) 127 128 if errors_path.endswith(".test"): 129 fixup_errors_for_file(actual_errors, errors_path) 130 elif errors_path.endswith(".txt"): 131 write_string_to_file(actual_errors, errors_path) 132 else: 133 print('Unknown file extension') 134 sys.exit(1) 135 136 137 138if __name__ == "__main__": 139 main() 140