Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: net/data/verify_certificate_chain_unittest/rebase-errors.py

Issue 2346663002: Generalize rebase-errors.py so it works for other files. (Closed)
Patch Set: moar comment wording Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/cert/internal/verify_certificate_chain_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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="*VerifyCertificateChain*" | \
15 net/data/verify_certificate_chain_unittest/rebase-errors.py 15 net/data/verify_certificate_chain_unittest/rebase-errors.py
16 16
17 The script will search the unit-test (results.txt in above example) and look 17 The script works by scanning the stdout looking for gtest failures when
18 for failure lines and the corresponding actual error string. 18 comparing "errors.ToDebugString()". The C++ test side should have been
19 instrumented to dump out the test file's path on mismatch.
19 20
20 It will then go and update the corresponding .pem and .py file. 21 This script will then update the corresponding file(s) -- a .pem file, and
22 possibly an accompanying .py file.
21 """ 23 """
22 24
23 import common 25 import common
24 import glob
25 import os 26 import os
26 import sys 27 import sys
27 import re 28 import re
28 29
29 30
31 # Regular expression to find the failed errors in test stdout.
32 # * 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
34 # errors were read from.
35 failed_test_regex = re.compile(r"""
36 Value of: errors.ToDebugString\(\)
37 Actual: "(.*)"
38 (?:.|\n)+?
39 Test file: (.*)
40 """, re.MULTILINE)
41
42
43 # 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
45 # (since it looks for an -----END to precede it).
46 # * Group 1 of the match is the ERRORS block content and any comments
47 # immediately above it.
48 errors_block_regex = re.compile(r""".*
49 -----END .*?-----
50
51 (.*?
52 -----BEGIN ERRORS-----
53 .*?
54 -----END ERRORS-----
55 )""", re.MULTILINE | re.DOTALL)
56
57
30 def read_file_to_string(path): 58 def read_file_to_string(path):
31 """Reads a file entirely to a string""" 59 """Reads a file entirely to a string"""
32 with open(path, 'r') as f: 60 with open(path, 'r') as f:
33 return f.read() 61 return f.read()
34 62
35 63
36 def write_string_to_file(data, path): 64 def write_string_to_file(data, path):
37 """Writes a string to a file""" 65 """Writes a string to a file"""
38 print "Writing file %s ..." % (path) 66 print "Writing file %s ..." % (path)
39 with open(path, "w") as f: 67 with open(path, "w") as f:
40 f.write(data) 68 f.write(data)
41 69
42 70
43 def get_file_paths_for_test(test_name): 71 def get_py_path(pem_path):
44 """Returns the file paths (as a tuple) that define a particular unit test. 72 """Returns the .py filepath used to generate the given .pem path, which may
45 For instance given test name 'IntermediateLacksBasicConstraints' it would 73 or may not exist.
46 return the paths to:
47 74
48 * intermediate-lacks-basic-constraints.pem, 75 Some test files (notably those in verify_certificate_chain_unittest/ have a
49 * generate-intermediate-lacks-basic-constraints.py 76 "generate-XXX.py" script that builds the "XXX.pem" file. Build the path to
50 """ 77 the corresponding "generate-XXX.py" (which may or may not exist)."""
51 # The directory that this python script is stored in. 78 file_name = os.path.basename(pem_path)
52 base_dir = os.path.dirname(os.path.realpath(__file__)) 79 file_name_no_extension = os.path.splitext(file_name)[0]
53 80 py_file_name = 'generate-' + file_name_no_extension + '.py'
54 # The C++ test name is just a camel case verson of the file name. Rather than 81 return os.path.join(os.path.dirname(pem_path), py_file_name)
55 # converting directly from camel case to a file name, it is simpler to just
56 # scan the file list and see which matches. (Not efficient but good enough).
57 paths = glob.glob(os.path.join(base_dir, '*.pem'))
58
59 for pem_path in paths:
60 file_name = os.path.basename(pem_path)
61 file_name_no_extension = os.path.splitext(file_name)[0]
62
63 # Strip the hyphens in file name to bring it closer to the camel case.
64 transformed = file_name_no_extension.replace('-', '')
65
66 # Now all that differs is the case.
67 if transformed.lower() == test_name.lower():
68 py_file_name = 'generate-' + file_name_no_extension + '.py'
69 py_path = os.path.join(base_dir, py_file_name)
70 return (pem_path, py_path)
71
72 return None
73 82
74 83
75 def replace_string(original, start, end, replacement): 84 def replace_string(original, start, end, replacement):
76 """Replaces the specified range of |original| with |replacement|""" 85 """Replaces the specified range of |original| with |replacement|"""
77 return original[0:start] + replacement + original[end:] 86 return original[0:start] + replacement + original[end:]
78 87
79 88
80 def fixup_pem_file(path, actual_errors): 89 def fixup_pem_file(path, actual_errors):
81 """Updates the ERRORS block in the test .pem file""" 90 """Updates the ERRORS block in the test .pem file"""
82 contents = read_file_to_string(path) 91 contents = read_file_to_string(path)
83 92
84 # This assumes that ERRORS is the last thing in file, and comes after the 93 m = errors_block_regex.search(contents)
85 # VERIFY_RESULT block. 94
86 kEndVerifyResult = '-----END VERIFY_RESULT-----' 95 if not m:
87 contents = contents[0:contents.index(kEndVerifyResult)] 96 print "Couldn't find ERRORS block in %s" % (path)
88 contents += kEndVerifyResult 97 return
89 contents += '\n' 98
90 contents += '\n' 99 contents = replace_string(contents, m.start(1), m.end(1),
91 contents += common.text_data_to_pem('ERRORS', actual_errors) 100 common.text_data_to_pem('ERRORS', actual_errors))
92 101
93 # Update the file. 102 # Update the file.
94 write_string_to_file(contents, path) 103 write_string_to_file(contents, path)
95 104
96 105
97 def fixup_py_file(path, actual_errors): 106 def fixup_py_file(path, actual_errors):
98 """Replaces the 'errors = XXX' section of the test's python script""" 107 """Replaces the 'errors = XXX' section of the test's python script"""
99 contents = read_file_to_string(path) 108 contents = read_file_to_string(path)
100 109
101 # This assumes that the errors variable uses triple quotes. 110 # This assumes that the errors variable uses triple quotes.
102 prog = re.compile(r'^errors = """(.*)"""', re.MULTILINE | re.DOTALL) 111 prog = re.compile(r'^errors = """(.*?)"""', re.MULTILINE | re.DOTALL)
103 result = prog.search(contents) 112 result = prog.search(contents)
104 113
105 # Replace the stuff in between the triple quotes with the actual errors. 114 # Replace the stuff in between the triple quotes with the actual errors.
106 contents = replace_string(contents, result.start(1), result.end(1), 115 contents = replace_string(contents, result.start(1), result.end(1),
107 actual_errors) 116 actual_errors)
108 117
109 # Update the file. 118 # Update the file.
110 write_string_to_file(contents, path) 119 write_string_to_file(contents, path)
111 120
112 121
113 def fixup_test(test_name, actual_errors): 122 def get_src_root():
114 """Updates the test files used by |test_name|, setting the expected error to 123 """Returns the path to the enclosing //src directory. This assumes the
124 current script is inside the source tree."""
125 cur_dir = os.path.dirname(os.path.realpath(__file__))
126
127 while True:
128 parent_dir, dirname = os.path.split(cur_dir)
129 # Check if it looks like the src/ root.
130 if dirname == "src" and os.path.isdir(os.path.join(cur_dir, "net")):
131 return cur_dir
132 if not parent_dir or parent_dir == cur_dir:
133 break
134 cur_dir = parent_dir
135
136 print "Couldn't find src dir"
137 sys.exit(1)
138
139
140 def get_abs_path(rel_path):
141 """Converts |rel_path| (relative to src) to a full path"""
142 return os.path.join(get_src_root(), rel_path)
143
144
145 def fixup_errors_for_file(actual_errors, pem_path):
146 """Updates the errors in |test_file_path| (.pem file) to match
115 |actual_errors|""" 147 |actual_errors|"""
116 148
117 # Determine the paths for the corresponding *.pem file and generate-*.py 149 fixup_pem_file(pem_path, actual_errors)
118 pem_path, py_path = get_file_paths_for_test(test_name)
119 150
120 fixup_pem_file(pem_path, actual_errors) 151 # If the test has a generator script update it too.
121 fixup_py_file(py_path, actual_errors) 152 py_path = get_py_path(pem_path)
122 153 if os.path.isfile(py_path):
123 154 fixup_py_file(py_path, actual_errors)
124 kTestNamePattern = (r'^\[ RUN \] VerifyCertificateChain/'
125 'VerifyCertificateChainSingleRootTest/0\.(.*)$')
126 kValueOfLine = 'Value of: errors.ToDebugString()'
127 kActualPattern = '^ Actual: "(.*)"$'
128 155
129 156
130 def main(): 157 def main():
131 if len(sys.argv) > 2: 158 if len(sys.argv) > 2:
132 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0]) 159 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0])
133 sys.exit(1) 160 sys.exit(1)
134 161
135 # Read the input either from a file, or from stdin. 162 # Read the input either from a file, or from stdin.
136 test_stdout = None 163 test_stdout = None
137 if len(sys.argv) == 2: 164 if len(sys.argv) == 2:
138 test_stdout = read_file_to_string(sys.argv[1]) 165 test_stdout = read_file_to_string(sys.argv[1])
139 else: 166 else:
140 print 'Reading input from stdin...' 167 print 'Reading input from stdin...'
141 test_stdout = sys.stdin.read() 168 test_stdout = sys.stdin.read()
142 169
143 lines = test_stdout.split('\n') 170 for m in failed_test_regex.finditer(test_stdout):
144 171 actual_errors = m.group(1)
145 # Iterate over each line of the unit test stdout. 172 actual_errors = actual_errors.decode('string-escape')
146 for i in range(len(lines) - 3): 173 relative_test_path = m.group(2)
147 # Figure out the name of the test. 174 fixup_errors_for_file(actual_errors, get_abs_path(relative_test_path))
148 m = re.search(kTestNamePattern, lines[i])
149 if not m:
150 continue
151 test_name = m.group(1)
152
153 # Confirm that it is a failure having to do with the errors.
154 if lines[i + 2] != kValueOfLine:
155 continue
156
157 # Get the actual error text (which in gtest output is escaped).
158 m = re.search(kActualPattern, lines[i + 3])
159 if not m:
160 continue
161 actual = m.group(1)
162 actual = actual.decode('string-escape')
163
164 fixup_test(test_name, actual)
165 175
166 176
167 if __name__ == "__main__": 177 if __name__ == "__main__":
168 main() 178 main()
OLDNEW
« no previous file with comments | « net/cert/internal/verify_certificate_chain_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698