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

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: update var name 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 will search the unit-test stdout and looking for a failed value of:
mattm 2016/09/15 00:42:05 awkward wording
eroman 2016/09/16 01:14:29 Done.
18 for failure lines and the corresponding actual error string. 18 errors.ToDebugString()
19 19
20 It will then go and update the corresponding .pem and .py file. 20 It will then go and update the corresponding .pem and/or .py files.
21
22 This script works for tests described in .pem files which use a block
23 called "ERRORS", and in the C++ side check "errors.ToDebugString()"
21 """ 24 """
22 25
23 import common 26 import common
24 import glob
25 import os 27 import os
26 import sys 28 import sys
27 import re 29 import re
28 30
29 31
32 kFailedTestPattern = r"""
mattm 2016/09/15 00:42:05 do the re.compile for these patterns here rather t
eroman 2016/09/16 01:14:29 Done. Agreed that is better! I also added an expla
33 Value of: errors.ToDebugString\(\)
34 Actual: "(.*?)"
mattm 2016/09/15 00:42:05 probably should be greedy (safe since this pattern
eroman 2016/09/16 01:14:29 Done.
35 (?:.|\n)+?
36 Test file: (.*?)
mattm 2016/09/15 00:42:05 here too
eroman 2016/09/16 01:14:29 Done.
37 """
38
39
40 kErrorsBlockPattern = r"""
41 -----END .*-----
mattm 2016/09/15 00:42:05 non-greedy?
eroman 2016/09/16 01:14:29 Done. (Thanks for the help over IM on this).
42
43 (.*?
44 -----BEGIN ERRORS-----
45 .*?
46 -----END ERRORS-----
47 )"""
48
49
30 def read_file_to_string(path): 50 def read_file_to_string(path):
31 """Reads a file entirely to a string""" 51 """Reads a file entirely to a string"""
32 with open(path, 'r') as f: 52 with open(path, 'r') as f:
33 return f.read() 53 return f.read()
34 54
35 55
36 def write_string_to_file(data, path): 56 def write_string_to_file(data, path):
37 """Writes a string to a file""" 57 """Writes a string to a file"""
38 print "Writing file %s ..." % (path) 58 print "Writing file %s ..." % (path)
39 with open(path, "w") as f: 59 with open(path, "w") as f:
40 f.write(data) 60 f.write(data)
41 61
42 62
43 def get_file_paths_for_test(test_name): 63 def get_py_path(pem_path):
44 """Returns the file paths (as a tuple) that define a particular unit test. 64 """Returns the .py filepath used to generate the given .pem path if there is
45 For instance given test name 'IntermediateLacksBasicConstraints' it would 65 one. Otherwise returns None."""
46 return the paths to:
47 66
48 * intermediate-lacks-basic-constraints.pem, 67 base_dir = os.path.dirname(pem_path)
49 * generate-intermediate-lacks-basic-constraints.py 68 if not base_dir.endswith('/verify_certificate_chain_unittest'):
mattm 2016/09/15 00:42:05 reasoning for hard coding this? (Instead of say, b
mattm 2016/09/15 00:42:05 os.path.split(base_dir)[1]
eroman 2016/09/16 01:14:29 Done.
50 """ 69 return None
51 # The directory that this python script is stored in.
52 base_dir = os.path.dirname(os.path.realpath(__file__))
53 70
54 # The C++ test name is just a camel case verson of the file name. Rather than 71 file_name = os.path.basename(pem_path)
55 # converting directly from camel case to a file name, it is simpler to just 72 file_name_no_extension = os.path.splitext(file_name)[0]
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 73
59 for pem_path in paths: 74 py_file_name = 'generate-' + file_name_no_extension + '.py'
60 file_name = os.path.basename(pem_path) 75 return os.path.join(base_dir, py_file_name)
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 76
74 77
75 def replace_string(original, start, end, replacement): 78 def replace_string(original, start, end, replacement):
76 """Replaces the specified range of |original| with |replacement|""" 79 """Replaces the specified range of |original| with |replacement|"""
77 return original[0:start] + replacement + original[end:] 80 return original[0:start] + replacement + original[end:]
78 81
79 82
80 def fixup_pem_file(path, actual_errors): 83 def fixup_pem_file(path, actual_errors):
81 """Updates the ERRORS block in the test .pem file""" 84 """Updates the ERRORS block in the test .pem file"""
82 contents = read_file_to_string(path) 85 contents = read_file_to_string(path)
83 86
84 # This assumes that ERRORS is the last thing in file, and comes after the 87 prog = re.compile(kErrorsBlockPattern, re.MULTILINE | re.DOTALL)
85 # VERIFY_RESULT block. 88 m = prog.search(contents)
86 kEndVerifyResult = '-----END VERIFY_RESULT-----' 89
87 contents = contents[0:contents.index(kEndVerifyResult)] 90 if not m:
88 contents += kEndVerifyResult 91 print "Couldn't find ERRORS block in %s" % (path)
89 contents += '\n' 92 return
90 contents += '\n' 93
91 contents += common.text_data_to_pem('ERRORS', actual_errors) 94 contents = replace_string(contents, m.start(1), m.end(1),
95 common.text_data_to_pem('ERRORS', actual_errors))
92 96
93 # Update the file. 97 # Update the file.
94 write_string_to_file(contents, path) 98 write_string_to_file(contents, path)
95 99
96 100
97 def fixup_py_file(path, actual_errors): 101 def fixup_py_file(path, actual_errors):
98 """Replaces the 'errors = XXX' section of the test's python script""" 102 """Replaces the 'errors = XXX' section of the test's python script"""
99 contents = read_file_to_string(path) 103 contents = read_file_to_string(path)
100 104
101 # This assumes that the errors variable uses triple quotes. 105 # This assumes that the errors variable uses triple quotes.
102 prog = re.compile(r'^errors = """(.*)"""', re.MULTILINE | re.DOTALL) 106 prog = re.compile(r'^errors = """(.*)"""', re.MULTILINE | re.DOTALL)
mattm 2016/09/15 00:42:05 guess this should be non-greedy too
eroman 2016/09/16 01:14:29 Done.
103 result = prog.search(contents) 107 result = prog.search(contents)
104 108
105 # Replace the stuff in between the triple quotes with the actual errors. 109 # Replace the stuff in between the triple quotes with the actual errors.
106 contents = replace_string(contents, result.start(1), result.end(1), 110 contents = replace_string(contents, result.start(1), result.end(1),
107 actual_errors) 111 actual_errors)
108 112
109 # Update the file. 113 # Update the file.
110 write_string_to_file(contents, path) 114 write_string_to_file(contents, path)
111 115
112 116
113 def fixup_test(test_name, actual_errors): 117 def get_abs_path(rel_path):
114 """Updates the test files used by |test_name|, setting the expected error to 118 """Converts |rel_path| (relative to src) to a full path"""
119 # Assume the current script lives in a well known location.
120 kScriptDir = "net/data/verify_certificate_chain_unittest"
121 script_dir = os.path.dirname(os.path.realpath(__file__))
122 if not script_dir.endswith(kScriptDir):
123 print "Script is not in expected location: " % (kScriptDir)
mattm 2016/09/15 00:42:05 format string doesn't contain %s
eroman 2016/09/16 01:14:29 Done
124 exit(1)
mattm 2016/09/15 00:42:05 sys.exit
eroman 2016/09/16 01:14:29 ... clearly I didn't test this failure case.
125
126 src_dir = script_dir[0:-len(kScriptDir)]
mattm 2016/09/15 00:42:05 instead of hardcoding all that, how about somethin
eroman 2016/09/16 01:14:29 Done.
127 return src_dir + rel_path
mattm 2016/09/15 00:42:05 os.path.join
eroman 2016/09/16 01:14:29 Done.
128
129
130 def fixup_errors_for_file(actual_errors, pem_path):
131 """Updates the errors in |test_file_path| (.pem file) to match
115 |actual_errors|""" 132 |actual_errors|"""
116 133
117 # Determine the paths for the corresponding *.pem file and generate-*.py 134 fixup_pem_file(pem_path, actual_errors)
118 pem_path, py_path = get_file_paths_for_test(test_name)
119 135
120 fixup_pem_file(pem_path, actual_errors) 136 # Tests in verify_certificate_chain_unittests additionally have a .py
121 fixup_py_file(py_path, actual_errors) 137 # generator file to update.
122 138 py_path = get_py_path(pem_path)
123 139 if py_path:
124 kTestNamePattern = (r'^\[ RUN \] VerifyCertificateChain/' 140 fixup_py_file(py_path, actual_errors)
125 'VerifyCertificateChainSingleRootTest/0\.(.*)$')
126 kValueOfLine = 'Value of: errors.ToDebugString()'
127 kActualPattern = '^ Actual: "(.*)"$'
128 141
129 142
130 def main(): 143 def main():
131 if len(sys.argv) > 2: 144 if len(sys.argv) > 2:
132 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0]) 145 print 'Usage: %s [path-to-unittest-stdout]' % (sys.argv[0])
133 sys.exit(1) 146 sys.exit(1)
134 147
135 # Read the input either from a file, or from stdin. 148 # Read the input either from a file, or from stdin.
136 test_stdout = None 149 test_stdout = None
137 if len(sys.argv) == 2: 150 if len(sys.argv) == 2:
138 test_stdout = read_file_to_string(sys.argv[1]) 151 test_stdout = read_file_to_string(sys.argv[1])
139 else: 152 else:
140 print 'Reading input from stdin...' 153 print 'Reading input from stdin...'
141 test_stdout = sys.stdin.read() 154 test_stdout = sys.stdin.read()
142 155
143 lines = test_stdout.split('\n') 156 prog = re.compile(kFailedTestPattern, re.MULTILINE)
144 157
145 # Iterate over each line of the unit test stdout. 158 for m in prog.finditer(test_stdout):
146 for i in range(len(lines) - 3): 159 actual_errors = m.group(1)
147 # Figure out the name of the test. 160 actual_errors = actual_errors.decode('string-escape')
148 m = re.search(kTestNamePattern, lines[i]) 161 relative_test_path = m.group(2)
149 if not m: 162 fixup_errors_for_file(actual_errors, get_abs_path(relative_test_path))
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 163
166 164
167 if __name__ == "__main__": 165 if __name__ == "__main__":
168 main() 166 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