| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2016 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Helper script to update the test error expectations based on actual results. | 6 """Helper script to update the test error expectations based on actual results. |
| 7 | 7 |
| 8 This is useful for regenerating test expectations after making changes to the | 8 This is useful for regenerating test expectations after making changes to the |
| 9 error format. | 9 error format. |
| 10 | 10 |
| 11 To use this run the affected tests, and then pass the input to this script | 11 To use this run the affected tests, and then pass the input to this script |
| 12 (either via stdin, or as the first argument). For instance: | 12 (either via stdin, or as the first argument). For instance: |
| 13 | 13 |
| 14 $ ./out/Release/net_unittests --gtest_filter="*VerifyCertificateChain*" | \ | 14 $ ./out/Release/net_unittests --gtest_filter="*ParseCertificate*" | \ |
| 15 net/data/verify_certificate_chain_unittest/rebase-errors.py | 15 net/data/parse_certificate_unittest/rebase-errors.py |
| 16 | 16 |
| 17 The script works by scanning the stdout looking for gtest failures when | 17 The script works by scanning the stdout looking for gtest failures when |
| 18 comparing "errors.ToDebugString(chain)". The C++ test side should have been | 18 comparing "errors.ToDebugString()". The C++ test side should have been |
| 19 instrumented to dump out the test file's path on mismatch. | 19 instrumented to dump out the test file's path on mismatch. |
| 20 | 20 |
| 21 This script will then update the corresponding file(s) -- a .pem file, and | 21 This script will then update the corresponding .pem file |
| 22 possibly an accompanying .py file. | |
| 23 """ | 22 """ |
| 24 | 23 |
| 24 import sys |
| 25 sys.path += ['../verify_certificate_chain_unittest'] |
| 26 |
| 25 import common | 27 import common |
| 28 |
| 26 import os | 29 import os |
| 27 import sys | 30 import sys |
| 28 import re | 31 import re |
| 29 | 32 |
| 30 | 33 |
| 31 # Regular expression to find the failed errors in test stdout. | 34 # Regular expression to find the failed errors in test stdout. |
| 32 # * Group 1 of the match is the actual error text (backslash-escaped) | 35 # * Group 1 of the match is the actual error text (backslash-escaped) |
| 33 # * Group 2 of the match is file path (relative to //src) where the expected | 36 # * Group 2 of the match is file path (relative to //src) where the expected |
| 34 # errors were read from. | 37 # errors were read from. |
| 35 failed_test_regex = re.compile(r""" | 38 failed_test_regex = re.compile(r""" |
| 36 Value of: errors.ToDebugString\((?:test.chain)?\) | 39 Value of: errors.ToDebugString\(\) |
| 37 Actual: "(.*)" | 40 Actual: "(.*)" |
| 38 (?:.|\n)+? | 41 (?:.|\n)+? |
| 39 Test file: (.*) | 42 Test file: (.*[.]pem) |
| 40 """, re.MULTILINE) | 43 """, re.MULTILINE) |
| 41 | 44 |
| 42 | 45 |
| 43 # Regular expression to find the ERRORS block (and any text above it) in a PEM | 46 # Regular expression to find the ERRORS block (and any text above it) in a PEM |
| 44 # file. The assumption is that ERRORS is not the very first block in the file | 47 # file. The assumption is that ERRORS is not the very first block in the file |
| 45 # (since it looks for an -----END to precede it). | 48 # (since it looks for an -----END to precede it). |
| 46 # * Group 1 of the match is the ERRORS block content and any comments | 49 # * Group 1 of the match is the ERRORS block content and any comments |
| 47 # immediately above it. | 50 # immediately above it. |
| 48 errors_block_regex = re.compile(r""".* | 51 errors_block_regex = re.compile(r""".* |
| 49 -----END .*?----- | 52 -----END .*?----- |
| (...skipping 11 matching lines...) Expand all Loading... |
| 61 return f.read() | 64 return f.read() |
| 62 | 65 |
| 63 | 66 |
| 64 def write_string_to_file(data, path): | 67 def write_string_to_file(data, path): |
| 65 """Writes a string to a file""" | 68 """Writes a string to a file""" |
| 66 print "Writing file %s ..." % (path) | 69 print "Writing file %s ..." % (path) |
| 67 with open(path, "w") as f: | 70 with open(path, "w") as f: |
| 68 f.write(data) | 71 f.write(data) |
| 69 | 72 |
| 70 | 73 |
| 71 def get_py_path(pem_path): | |
| 72 """Returns the .py filepath used to generate the given .pem path, which may | |
| 73 or may not exist. | |
| 74 | |
| 75 Some test files (notably those in verify_certificate_chain_unittest/ have a | |
| 76 "generate-XXX.py" script that builds the "XXX.pem" file. Build the path to | |
| 77 the corresponding "generate-XXX.py" (which may or may not exist).""" | |
| 78 file_name = os.path.basename(pem_path) | |
| 79 file_name_no_extension = os.path.splitext(file_name)[0] | |
| 80 py_file_name = 'generate-' + file_name_no_extension + '.py' | |
| 81 return os.path.join(os.path.dirname(pem_path), py_file_name) | |
| 82 | |
| 83 | |
| 84 def replace_string(original, start, end, replacement): | 74 def replace_string(original, start, end, replacement): |
| 85 """Replaces the specified range of |original| with |replacement|""" | 75 """Replaces the specified range of |original| with |replacement|""" |
| 86 return original[0:start] + replacement + original[end:] | 76 return original[0:start] + replacement + original[end:] |
| 87 | 77 |
| 88 | 78 |
| 89 def fixup_pem_file(path, actual_errors): | 79 def fixup_pem_file(path, actual_errors): |
| 90 """Updates the ERRORS block in the test .pem file""" | 80 """Updates the ERRORS block in the test .pem file""" |
| 91 contents = read_file_to_string(path) | 81 contents = read_file_to_string(path) |
| 92 | 82 |
| 93 m = errors_block_regex.search(contents) | 83 m = errors_block_regex.search(contents) |
| 94 | 84 |
| 95 if not m: | 85 if not m: |
| 96 contents += '\n' + common.text_data_to_pem('ERRORS', actual_errors) | 86 contents += '\n' + common.text_data_to_pem('ERRORS', actual_errors) |
| 97 else: | 87 else: |
| 98 contents = replace_string(contents, m.start(1), m.end(1), | 88 contents = replace_string(contents, m.start(1), m.end(1), |
| 99 common.text_data_to_pem('ERRORS', actual_errors)) | 89 common.text_data_to_pem('ERRORS', actual_errors)) |
| 100 | 90 |
| 101 # Update the file. | 91 # Update the file. |
| 102 write_string_to_file(contents, path) | 92 write_string_to_file(contents, path) |
| 103 | 93 |
| 104 | 94 |
| 105 def fixup_py_file(path, actual_errors): | |
| 106 """Replaces the 'errors = XXX' section of the test's python script""" | |
| 107 contents = read_file_to_string(path) | |
| 108 | |
| 109 # This assumes that the errors variable uses triple quotes. | |
| 110 prog = re.compile(r'^errors = (""".*?"""|None)', re.MULTILINE | re.DOTALL) | |
| 111 result = prog.search(contents) | |
| 112 | |
| 113 # Replace the stuff in between the triple quotes with the actual errors. | |
| 114 contents = replace_string(contents, result.start(1), result.end(1), | |
| 115 '"""' + actual_errors + '"""') | |
| 116 | |
| 117 # Update the file. | |
| 118 write_string_to_file(contents, path) | |
| 119 | |
| 120 | |
| 121 def get_src_root(): | 95 def get_src_root(): |
| 122 """Returns the path to the enclosing //src directory. This assumes the | 96 """Returns the path to the enclosing //src directory. This assumes the |
| 123 current script is inside the source tree.""" | 97 current script is inside the source tree.""" |
| 124 cur_dir = os.path.dirname(os.path.realpath(__file__)) | 98 cur_dir = os.path.dirname(os.path.realpath(__file__)) |
| 125 | 99 |
| 126 while True: | 100 while True: |
| 127 parent_dir, dirname = os.path.split(cur_dir) | 101 parent_dir, dirname = os.path.split(cur_dir) |
| 128 # Check if it looks like the src/ root. | 102 # Check if it looks like the src/ root. |
| 129 if dirname == "src" and os.path.isdir(os.path.join(cur_dir, "net")): | 103 if dirname == "src" and os.path.isdir(os.path.join(cur_dir, "net")): |
| 130 return cur_dir | 104 return cur_dir |
| 131 if not parent_dir or parent_dir == cur_dir: | 105 if not parent_dir or parent_dir == cur_dir: |
| 132 break | 106 break |
| 133 cur_dir = parent_dir | 107 cur_dir = parent_dir |
| 134 | 108 |
| 135 print "Couldn't find src dir" | 109 print "Couldn't find src dir" |
| 136 sys.exit(1) | 110 sys.exit(1) |
| 137 | 111 |
| 138 | 112 |
| 139 def get_abs_path(rel_path): | 113 def get_abs_path(rel_path): |
| 140 """Converts |rel_path| (relative to src) to a full path""" | 114 """Converts |rel_path| (relative to src) to a full path""" |
| 141 return os.path.join(get_src_root(), rel_path) | 115 return os.path.join(get_src_root(), rel_path) |
| 142 | 116 |
| 143 | 117 |
| 144 def fixup_errors_for_file(actual_errors, pem_path): | |
| 145 """Updates the errors in |test_file_path| (.pem file) to match | |
| 146 |actual_errors|""" | |
| 147 | |
| 148 fixup_pem_file(pem_path, actual_errors) | |
| 149 | |
| 150 # If the test has a generator script update it too. | |
| 151 py_path = get_py_path(pem_path) | |
| 152 if os.path.isfile(py_path): | |
| 153 fixup_py_file(py_path, actual_errors) | |
| 154 | |
| 155 | |
| 156 def main(): | 118 def main(): |
| 157 if len(sys.argv) > 2: | 119 if len(sys.argv) > 2: |
| 158 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0]) | 120 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0]) |
| 159 sys.exit(1) | 121 sys.exit(1) |
| 160 | 122 |
| 161 # Read the input either from a file, or from stdin. | 123 # Read the input either from a file, or from stdin. |
| 162 test_stdout = None | 124 test_stdout = None |
| 163 if len(sys.argv) == 2: | 125 if len(sys.argv) == 2: |
| 164 test_stdout = read_file_to_string(sys.argv[1]) | 126 test_stdout = read_file_to_string(sys.argv[1]) |
| 165 else: | 127 else: |
| 166 print 'Reading input from stdin...' | 128 print 'Reading input from stdin...' |
| 167 test_stdout = sys.stdin.read() | 129 test_stdout = sys.stdin.read() |
| 168 | 130 |
| 169 for m in failed_test_regex.finditer(test_stdout): | 131 for m in failed_test_regex.finditer(test_stdout): |
| 170 actual_errors = m.group(1) | 132 actual_errors = m.group(1) |
| 171 actual_errors = actual_errors.decode('string-escape') | 133 actual_errors = actual_errors.decode('string-escape') |
| 172 relative_test_path = m.group(2) | 134 relative_test_path = m.group(2) |
| 173 fixup_errors_for_file(actual_errors, get_abs_path(relative_test_path)) | 135 fixup_pem_file(get_abs_path(relative_test_path), actual_errors) |
| 174 | 136 |
| 175 | 137 |
| 176 if __name__ == "__main__": | 138 if __name__ == "__main__": |
| 177 main() | 139 main() |
| OLD | NEW |