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

Side by Side Diff: Tools/Scripts/webkitpy/bindings/main.py

Issue 216753002: Cleanup run-bindings-tests and use filecmp (23% faster r-b-t) (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: More cruft Created 6 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | 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 # Copyright (C) 2011 Google Inc. All rights reserved. 1 # Copyright (C) 2011 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions 4 # modification, are permitted provided that the following conditions
5 # are met: 5 # are met:
6 # 1. Redistributions of source code must retain the above copyright 6 # 1. Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer. 7 # notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright 8 # 2. Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the 9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution. 10 # documentation and/or other materials provided with the distribution.
11 # 11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 12 # THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 14 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
16 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
17 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
19 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 19 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 21 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 # 23 #
24 24
25 import filecmp
25 import fnmatch 26 import fnmatch
26 import os 27 import os
27 import shutil 28 import shutil
28 import sys 29 import sys
29 import tempfile 30 import tempfile
30 31
31 from webkitpy.common.checkout.scm.detection import detect_scm_system 32 from webkitpy.common.checkout.scm.detection import detect_scm_system
32 from webkitpy.common.system import executive 33 from webkitpy.common.system import executive
33 from webkitpy.common.system.executive import ScriptError
34 34
35 # Add Source path to PYTHONPATH to support function calls to bindings/scripts 35 # Add Source path to PYTHONPATH to support function calls to bindings/scripts
36 # for compute_interfaces_info and idl_compiler 36 # for compute_interfaces_info and idl_compiler
37 module_path = os.path.dirname(__file__) 37 module_path = os.path.dirname(__file__)
38 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir, 38 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir,
39 os.pardir, os.pardir, 'Source')) 39 os.pardir, os.pardir, 'Source'))
40 sys.path.append(source_path) 40 sys.path.append(source_path)
41 41
42 from bindings.scripts.compute_interfaces_info import compute_interfaces_info, in terfaces_info 42 from bindings.scripts.compute_interfaces_info import compute_interfaces_info, in terfaces_info
43 from bindings.scripts.idl_compiler import IdlCompilerV8 43 from bindings.scripts.idl_compiler import IdlCompilerV8
(...skipping 20 matching lines...) Expand all
64 ]) 64 ])
65 65
66 66
67 EXTENDED_ATTRIBUTES_FILE = 'bindings/IDLExtendedAttributes.txt' 67 EXTENDED_ATTRIBUTES_FILE = 'bindings/IDLExtendedAttributes.txt'
68 68
69 all_input_directory = '.' # Relative to Source/ 69 all_input_directory = '.' # Relative to Source/
70 test_input_directory = os.path.join('bindings', 'tests', 'idls') 70 test_input_directory = os.path.join('bindings', 'tests', 'idls')
71 reference_directory = os.path.join('bindings', 'tests', 'results') 71 reference_directory = os.path.join('bindings', 'tests', 'results')
72 72
73 73
74 class ScopedTempFileProvider(object): 74 class ScopedTempDir(object):
75 """Wrapper for tempfile.mkdtemp() so it's usable with 'with' statement."""
75 def __init__(self): 76 def __init__(self):
76 self.file_handles = [] 77 self.dir_path = tempfile.mkdtemp()
77 self.file_paths = []
78 self.dir_paths = []
79 78
80 def __enter__(self): 79 def __enter__(self):
81 return self 80 return self.dir_path
82 81
83 def __exit__(self, exc_type, exc_value, traceback): 82 def __exit__(self, exc_type, exc_value, traceback):
84 for file_handle in self.file_handles: 83 # The temporary directory is used as an output directory, so it
85 os.close(file_handle) 84 # contains unknown files (it isn't aren't empty), hence use rmtree
haraken 2014/03/28 10:33:18 isn't aren't => isn't
Nils Barth (inactive) 2014/03/28 10:47:25 Done.
86 for file_path in self.file_paths: 85 shutil.rmtree(self.dir_path)
87 os.remove(file_path)
88 for dir_path in self.dir_paths:
89 # Temporary directories are used as output directories, so they
90 # contains unknown files (they aren't empty), hence use rmtree
91 shutil.rmtree(dir_path)
92 86
93 def new_temp_file(self):
94 file_handle, file_path = tempfile.mkstemp()
95 self.file_handles.append(file_handle)
96 self.file_paths.append(file_path)
97 return file_handle, file_path
98
99 def new_temp_dir(self):
100 dir_path = tempfile.mkdtemp()
101 self.dir_paths.append(dir_path)
102 return dir_path
103 87
104 88
105 class BindingsTests(object): 89 class BindingsTests(object):
106 def __init__(self, reset_results, verbose, provider): 90 def __init__(self, output_directory, verbose):
107 self.reset_results = reset_results
108 self.verbose = verbose 91 self.verbose = verbose
109 self.executive = executive.Executive() 92 self.executive = executive.Executive()
110 self.provider = provider
111 self.idl_compiler = None 93 self.idl_compiler = None
112 # Generate output into the reference directory if resetting results, or 94 self.output_directory = output_directory
113 # a temp directory if not.
114 if reset_results:
115 self.output_directory = reference_directory
116 else:
117 self.output_directory = provider.new_temp_dir()
118 95
119 def run_command(self, cmd): 96 def diff(self, filename1, filename2):
120 output = self.executive.run_command(cmd) 97 # difflib is too slow, especially on long output, so run diff
haraken 2014/03/28 10:33:18 'difflib' and 'diff' are confusing. Shall we use '
Nils Barth (inactive) 2014/03/28 10:47:25 Good point. I've clarified the comment (as "Python
121 if output: 98 cmd = ['diff',
122 print output 99 '-u',
100 '-N',
101 filename1,
102 filename2]
103 # Return output and don't raise exception, even though diff has
104 # non-zero exit if files differ.
105 return self.executive.run_command(cmd, error_handler=lambda x: None)
haraken 2014/03/28 10:33:18 Does this invoke Python's built-in diff?
Nils Barth (inactive) 2014/03/28 10:47:25 No, it invokes an external command. Python's built
123 106
124 def generate_from_idl(self, idl_file): 107 def generate_from_idl(self, idl_file):
125 idl_file_fullpath = os.path.realpath(idl_file) 108 idl_file_fullpath = os.path.realpath(idl_file)
126 try: 109 self.idl_compiler.compile_file(idl_file_fullpath)
127 self.idl_compiler.compile_file(idl_file_fullpath)
128 except Exception as err:
129 print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file)
130 print err
131 return 1
132
133 return 0
134 110
135 def generate_interface_dependencies(self): 111 def generate_interface_dependencies(self):
136 def idl_paths(directory): 112 def idl_paths(directory):
137 return [os.path.join(directory, input_file) 113 return [os.path.join(directory, input_file)
138 for input_file in os.listdir(directory) 114 for input_file in os.listdir(directory)
139 if input_file.endswith('.idl')] 115 if input_file.endswith('.idl')]
140 116
141 def idl_paths_recursive(directory): 117 def idl_paths_recursive(directory):
142 idl_paths = [] 118 idl_paths = []
143 for dirpath, _, files in os.walk(directory): 119 for dirpath, _, files in os.walk(directory):
144 idl_paths.extend(os.path.join(dirpath, filename) 120 idl_paths.extend(os.path.join(dirpath, filename)
145 for filename in fnmatch.filter(files, '*.idl')) 121 for filename in fnmatch.filter(files, '*.idl'))
146 return idl_paths 122 return idl_paths
147 123
148 def write_list_file(idl_paths):
149 list_file, list_filename = self.provider.new_temp_file()
150 list_contents = ''.join(idl_path + '\n'
151 for idl_path in idl_paths)
152 os.write(list_file, list_contents)
153 return list_filename
154
155 # We compute interfaces info for *all* IDL files, not just test IDL 124 # We compute interfaces info for *all* IDL files, not just test IDL
156 # files, as code generator output depends on inheritance (both ancestor 125 # files, as code generator output depends on inheritance (both ancestor
157 # chain and inherited extended attributes), and some real interfaces 126 # chain and inherited extended attributes), and some real interfaces
158 # are special-cased, such as Node. 127 # are special-cased, such as Node.
159 # 128 #
160 # For example, when testing the behavior of interfaces that inherit 129 # For example, when testing the behavior of interfaces that inherit
161 # from Node, we also need to know that these inherit from EventTarget, 130 # from Node, we also need to know that these inherit from EventTarget,
162 # since this is also special-cased and Node inherits from EventTarget, 131 # since this is also special-cased and Node inherits from EventTarget,
163 # but this inheritance information requires computing dependencies for 132 # but this inheritance information requires computing dependencies for
164 # the real Node.idl file. 133 # the real Node.idl file.
165 try: 134 compute_interfaces_info(idl_paths_recursive(all_input_directory))
166 compute_interfaces_info(idl_paths_recursive(all_input_directory))
167 except Exception as err:
168 print 'ERROR: compute_interfaces_info.py'
169 print err
170 return 1
171
172 return 0
173 135
174 def delete_cache_files(self): 136 def delete_cache_files(self):
175 # FIXME: Instead of deleting cache files, don't generate them. 137 # FIXME: Instead of deleting cache files, don't generate them.
176 cache_files = [os.path.join(self.output_directory, output_file) 138 cache_files = [os.path.join(self.output_directory, output_file)
177 for output_file in os.listdir(self.output_directory) 139 for output_file in os.listdir(self.output_directory)
178 if (output_file in ('lextab.py', # PLY lex 140 if (output_file in ('lextab.py', # PLY lex
179 'lextab.pyc', 141 'lextab.pyc',
180 'parsetab.pickle') or # PLY yacc 142 'parsetab.pickle') or # PLY yacc
181 output_file.endswith('.cache'))] # Jinja 143 output_file.endswith('.cache'))] # Jinja
182 for cache_file in cache_files: 144 for cache_file in cache_files:
183 os.remove(cache_file) 145 os.remove(cache_file)
184 146
185 def identical_file(self, reference_filename, output_filename): 147 def identical_file(self, reference_filename, output_filename):
186 reference_basename = os.path.basename(reference_filename) 148 reference_basename = os.path.basename(reference_filename)
187 cmd = ['diff', 149
188 '-u', 150 if not filecmp.cmp(reference_filename, output_filename):
189 '-N',
190 reference_filename,
191 output_filename]
192 try:
193 self.run_command(cmd)
194 except ScriptError as err:
195 # run_command throws an exception on diff (b/c non-zero exit code)
196 print 'FAIL: %s' % reference_basename 151 print 'FAIL: %s' % reference_basename
197 print err.output 152 print self.diff(reference_filename, output_filename)
198 return False 153 return False
199 154
200 if self.verbose: 155 if self.verbose:
201 print 'PASS: %s' % reference_basename 156 print 'PASS: %s' % reference_basename
202 return True 157 return True
203 158
204 def identical_output_files(self): 159 def identical_output_files(self):
205 file_pairs = [(os.path.join(reference_directory, output_file), 160 file_pairs = [(os.path.join(reference_directory, output_file),
206 os.path.join(self.output_directory, output_file)) 161 os.path.join(self.output_directory, output_file))
207 for output_file in os.listdir(self.output_directory)] 162 for output_file in os.listdir(self.output_directory)]
208 return all([self.identical_file(reference_filename, output_filename) 163 return all([self.identical_file(reference_filename, output_filename)
209 for (reference_filename, output_filename) in file_pairs]) 164 for (reference_filename, output_filename) in file_pairs])
210 165
211 def no_excess_files(self): 166 def no_excess_files(self):
212 generated_files = set(os.listdir(self.output_directory)) 167 generated_files = set(os.listdir(self.output_directory))
213 generated_files.add('.svn') # Subversion working copy directory 168 generated_files.add('.svn') # Subversion working copy directory
214 excess_files = [output_file 169 excess_files = [output_file
215 for output_file in os.listdir(reference_directory) 170 for output_file in os.listdir(reference_directory)
216 if output_file not in generated_files] 171 if output_file not in generated_files]
217 if excess_files: 172 if excess_files:
218 print ('Excess reference files! ' 173 print ('Excess reference files! '
219 '(probably cruft from renaming or deleting):\n' + 174 '(probably cruft from renaming or deleting):\n' +
220 '\n'.join(excess_files)) 175 '\n'.join(excess_files))
221 return False 176 return False
222 return True 177 return True
223 178
224 def run_tests(self): 179 def run_tests(self):
225 # Generate output, immediately dying on failure 180 self.generate_interface_dependencies()
226 if self.generate_interface_dependencies():
227 return False
228
229 self.idl_compiler = IdlCompilerV8(self.output_directory, 181 self.idl_compiler = IdlCompilerV8(self.output_directory,
230 EXTENDED_ATTRIBUTES_FILE, 182 EXTENDED_ATTRIBUTES_FILE,
231 interfaces_info=interfaces_info, 183 interfaces_info=interfaces_info,
232 only_if_changed=True) 184 only_if_changed=True)
233 185
234 for input_filename in os.listdir(test_input_directory): 186 for input_filename in os.listdir(test_input_directory):
235 if not input_filename.endswith('.idl'): 187 if not input_filename.endswith('.idl'):
236 continue 188 continue
237 if input_filename in DEPENDENCY_IDL_FILES: 189 if input_filename in DEPENDENCY_IDL_FILES:
238 # Dependencies aren't built (they are used by the dependent) 190 # Dependencies aren't built (they are used by the dependent)
239 if self.verbose: 191 if self.verbose:
240 print 'DEPENDENCY: %s' % input_filename 192 print 'DEPENDENCY: %s' % input_filename
241 continue 193 continue
242 194
243 idl_path = os.path.join(test_input_directory, input_filename) 195 idl_path = os.path.join(test_input_directory, input_filename)
244 if self.generate_from_idl(idl_path): 196 self.generate_from_idl(idl_path)
245 return False 197 if self.verbose:
246 if self.reset_results and self.verbose: 198 print 'Compiled: %s' % input_filename
247 print 'Reset results: %s' % input_filename
248 199
249 self.delete_cache_files() 200 self.delete_cache_files()
250 201
251 # Detect all changes 202 # Detect all changes
252 passed = self.identical_output_files() 203 passed = self.identical_output_files()
253 passed &= self.no_excess_files() 204 passed &= self.no_excess_files()
254 return passed 205 return passed
255 206
256 def main(self): 207 def main(self):
257 current_scm = detect_scm_system(os.curdir) 208 current_scm = detect_scm_system(os.curdir)
258 os.chdir(os.path.join(current_scm.checkout_root, 'Source')) 209 os.chdir(os.path.join(current_scm.checkout_root, 'Source'))
259 210
260 all_tests_passed = self.run_tests() 211 all_tests_passed = self.run_tests()
261 if all_tests_passed: 212 if all_tests_passed:
262 if self.verbose: 213 if self.verbose:
263 print 214 print
264 print PASS_MESSAGE 215 print PASS_MESSAGE
265 return 0 216 return 0
266 print 217 print
267 print FAIL_MESSAGE 218 print FAIL_MESSAGE
268 return 1 219 return 1
269 220
270 221
271 def run_bindings_tests(reset_results, verbose): 222 def run_bindings_tests(reset_results, verbose):
272 with ScopedTempFileProvider() as provider: 223 # Generate output into the reference directory if resetting results, or
273 return BindingsTests(reset_results, verbose, provider).main() 224 # a temp directory if not.
225 if reset_results:
226 print 'Resetting results'
227 return BindingsTests(reference_directory, verbose).main()
228 with ScopedTempDir() as temp_dir:
229 return BindingsTests(temp_dir, verbose).main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698