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

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

Issue 2166223002: Rename Tools/Scripts/webkitpy/bindings/main.py to bindings_tests.py (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 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
OLDNEW
(Empty)
1 # Copyright (C) 2011 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1. Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
13 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
16 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
17 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
19 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
23 #
24
25 from contextlib import contextmanager
26 import filecmp
27 import fnmatch
28 import os
29 import shutil
30 import sys
31 import tempfile
32
33 from webkitpy.common.system.executive import Executive
34
35 # Source/ path is needed both to find input IDL files, and to import other
36 # Python modules.
37 module_path = os.path.dirname(__file__)
38 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir,
39 os.pardir, os.pardir, 'Source'))
40 sys.path.append(source_path) # for Source/bindings imports
41
42 from bindings.scripts.code_generator_v8 import CodeGeneratorUnionType
43 import bindings.scripts.compute_interfaces_info_individual
44 from bindings.scripts.compute_interfaces_info_individual import InterfaceInfoCol lector
45 import bindings.scripts.compute_interfaces_info_overall
46 from bindings.scripts.compute_interfaces_info_overall import compute_interfaces_ info_overall, interfaces_info
47 from bindings.scripts.idl_compiler import IdlCompilerDictionaryImpl, IdlCompiler V8
48 from bindings.scripts.idl_reader import IdlReader
49 from bindings.scripts.utilities import ComponentInfoProviderCore
50 from bindings.scripts.utilities import ComponentInfoProviderModules
51 from bindings.scripts.utilities import idl_filename_to_component
52 from bindings.scripts.utilities import write_file
53
54
55 PASS_MESSAGE = 'All tests PASS!'
56 FAIL_MESSAGE = """Some tests FAIL!
57 To update the reference files, execute:
58 run-bindings-tests --reset-results
59
60 If the failures are not due to your changes, test results may be out of sync;
61 please rebaseline them in a separate CL, after checking that tests fail in ToT.
62 In CL, please set:
63 NOTRY=true
64 TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings)
65 """
66
67 DEPENDENCY_IDL_FILES = frozenset([
68 'TestImplements.idl',
69 'TestImplements2.idl',
70 'TestImplements3.idl',
71 'TestInterfacePartial.idl',
72 'TestInterfacePartial2.idl',
73 'TestInterfacePartial3.idl',
74 'TestInterfacePartial4.idl',
75 'TestInterface2Partial.idl',
76 'TestInterface2Partial2.idl',
77 ])
78
79 # core/inspector/InspectorInstrumentation.idl is not a valid Blink IDL.
80 NON_BLINK_IDL_FILES = frozenset([
81 'InspectorInstrumentation.idl',
82 ])
83
84 COMPONENT_DIRECTORY = frozenset(['core', 'modules'])
85
86 test_input_directory = os.path.join(source_path, 'bindings', 'tests', 'idls')
87 reference_directory = os.path.join(source_path, 'bindings', 'tests', 'results')
88
89 # component -> ComponentInfoProvider.
90 # Note that this dict contains information about testing idl files, which live
91 # in Source/bindings/tests/idls/{core,modules}, not in Source/{core,modules}.
92 component_info_providers = {}
93
94
95 @contextmanager
96 def TemporaryDirectory():
97 """Wrapper for tempfile.mkdtemp() so it's usable with 'with' statement.
98
99 Simple backport of tempfile.TemporaryDirectory from Python 3.2.
100 """
101 name = tempfile.mkdtemp()
102 try:
103 yield name
104 finally:
105 shutil.rmtree(name)
106
107
108 def generate_interface_dependencies(output_directory):
109 def idl_paths_recursive(directory):
110 # This is slow, especially on Windows, due to os.walk making
111 # excess stat() calls. Faster versions may appear in Python 3.5 or
112 # later:
113 # https://github.com/benhoyt/scandir
114 # http://bugs.python.org/issue11406
115 idl_paths = []
116 for dirpath, _, files in os.walk(directory):
117 idl_paths.extend(os.path.join(dirpath, filename)
118 for filename in fnmatch.filter(files, '*.idl'))
119 return idl_paths
120
121 def collect_blink_idl_paths():
122 """Returns IDL file paths which blink actually uses."""
123 idl_paths = []
124 for component in COMPONENT_DIRECTORY:
125 directory = os.path.join(source_path, component)
126 idl_paths.extend(idl_paths_recursive(directory))
127 return idl_paths
128
129 def collect_interfaces_info(idl_path_list):
130 info_collector = InterfaceInfoCollector()
131 for idl_path in idl_path_list:
132 if os.path.basename(idl_path) in NON_BLINK_IDL_FILES:
133 continue
134 info_collector.collect_info(idl_path)
135 info = info_collector.get_info_as_dict()
136 # TestDictionary.{h,cpp} are placed under
137 # Source/bindings/tests/idls/core. However, IdlCompiler generates
138 # TestDictionary.{h,cpp} by using relative_dir.
139 # So the files will be generated under
140 # output_dir/core/bindings/tests/idls/core.
141 # To avoid this issue, we need to clear relative_dir here.
142 for value in info['interfaces_info'].itervalues():
143 value['relative_dir'] = ''
144 component_info = info_collector.get_component_info_as_dict()
145 return info, component_info
146
147 # We compute interfaces info for *all* IDL files, not just test IDL
148 # files, as code generator output depends on inheritance (both ancestor
149 # chain and inherited extended attributes), and some real interfaces
150 # are special-cased, such as Node.
151 #
152 # For example, when testing the behavior of interfaces that inherit
153 # from Node, we also need to know that these inherit from EventTarget,
154 # since this is also special-cased and Node inherits from EventTarget,
155 # but this inheritance information requires computing dependencies for
156 # the real Node.idl file.
157 non_test_idl_paths = collect_blink_idl_paths()
158 # For bindings test IDL files, we collect interfaces info for each
159 # component so that we can generate union type containers separately.
160 test_idl_paths = {}
161 for component in COMPONENT_DIRECTORY:
162 test_idl_paths[component] = idl_paths_recursive(
163 os.path.join(test_input_directory, component))
164 # 2nd-stage computation: individual, then overall
165 #
166 # Properly should compute separately by component (currently test
167 # includes are invalid), but that's brittle (would need to update this file
168 # for each new component) and doesn't test the code generator any better
169 # than using a single component.
170 non_test_interfaces_info, non_test_component_info = collect_interfaces_info( non_test_idl_paths)
171 test_interfaces_info = {}
172 test_component_info = {}
173 for component, paths in test_idl_paths.iteritems():
174 test_interfaces_info[component], test_component_info[component] = collec t_interfaces_info(paths)
175 # In order to allow test IDL files to override the production IDL files if
176 # they have the same interface name, process the test IDL files after the
177 # non-test IDL files.
178 info_individuals = [non_test_interfaces_info] + test_interfaces_info.values( )
179 compute_interfaces_info_overall(info_individuals)
180 # Add typedefs which are specified in the actual IDL files to the testing
181 # component info.
182 test_component_info['core']['typedefs'].update(
183 non_test_component_info['typedefs'])
184 component_info_providers['core'] = ComponentInfoProviderCore(
185 interfaces_info, test_component_info['core'])
186 component_info_providers['modules'] = ComponentInfoProviderModules(
187 interfaces_info, test_component_info['core'],
188 test_component_info['modules'])
189
190
191 def bindings_tests(output_directory, verbose):
192 executive = Executive()
193
194 def list_files(directory):
195 files = []
196 for component in os.listdir(directory):
197 if component not in COMPONENT_DIRECTORY:
198 continue
199 directory_with_component = os.path.join(directory, component)
200 for filename in os.listdir(directory_with_component):
201 files.append(os.path.join(directory_with_component, filename))
202 return files
203
204 def diff(filename1, filename2):
205 # Python's difflib module is too slow, especially on long output, so
206 # run external diff(1) command
207 cmd = ['diff',
208 '-u', # unified format
209 '-N', # treat absent files as empty
210 filename1,
211 filename2]
212 # Return output and don't raise exception, even though diff(1) has
213 # non-zero exit if files differ.
214 return executive.run_command(cmd, error_handler=lambda x: None)
215
216 def is_cache_file(filename):
217 return filename.endswith('.cache')
218
219 def delete_cache_files():
220 # FIXME: Instead of deleting cache files, don't generate them.
221 cache_files = [path for path in list_files(output_directory)
222 if is_cache_file(os.path.basename(path))]
223 for cache_file in cache_files:
224 os.remove(cache_file)
225
226 def identical_file(reference_filename, output_filename):
227 reference_basename = os.path.basename(reference_filename)
228
229 if not os.path.isfile(reference_filename):
230 print 'Missing reference file!'
231 print '(if adding new test, update reference files)'
232 print reference_basename
233 print
234 return False
235
236 if not filecmp.cmp(reference_filename, output_filename):
237 # cmp is much faster than diff, and usual case is "no difference",
238 # so only run diff if cmp detects a difference
239 print 'FAIL: %s' % reference_basename
240 print diff(reference_filename, output_filename)
241 return False
242
243 if verbose:
244 print 'PASS: %s' % reference_basename
245 return True
246
247 def identical_output_files(output_files):
248 reference_files = [os.path.join(reference_directory,
249 os.path.relpath(path, output_directory))
250 for path in output_files]
251 return all([identical_file(reference_filename, output_filename)
252 for (reference_filename, output_filename) in zip(reference_f iles, output_files)])
253
254 def no_excess_files(output_files):
255 generated_files = set([os.path.relpath(path, output_directory)
256 for path in output_files])
257 # Add subversion working copy directories in core and modules.
258 for component in COMPONENT_DIRECTORY:
259 generated_files.add(os.path.join(component, '.svn'))
260
261 excess_files = []
262 for path in list_files(reference_directory):
263 relpath = os.path.relpath(path, reference_directory)
264 if relpath not in generated_files:
265 excess_files.append(relpath)
266 if excess_files:
267 print ('Excess reference files! '
268 '(probably cruft from renaming or deleting):\n' +
269 '\n'.join(excess_files))
270 return False
271 return True
272
273 def generate_union_type_containers(output_directory, component):
274 generator = CodeGeneratorUnionType(
275 component_info_providers[component], cache_dir=None,
276 output_dir=output_directory, target_component=component)
277 outputs = generator.generate_code()
278 for output_path, output_code in outputs:
279 write_file(output_code, output_path, only_if_changed=True)
280
281 try:
282 generate_interface_dependencies(output_directory)
283 for component in COMPONENT_DIRECTORY:
284 output_dir = os.path.join(output_directory, component)
285 if not os.path.exists(output_dir):
286 os.makedirs(output_dir)
287
288 generate_union_type_containers(output_dir, component)
289
290 idl_compiler = IdlCompilerV8(
291 output_dir,
292 info_provider=component_info_providers[component],
293 only_if_changed=True)
294 if component == 'core':
295 partial_interface_output_dir = os.path.join(output_directory,
296 'modules')
297 if not os.path.exists(partial_interface_output_dir):
298 os.makedirs(partial_interface_output_dir)
299 idl_partial_interface_compiler = IdlCompilerV8(
300 partial_interface_output_dir,
301 info_provider=component_info_providers['modules'],
302 only_if_changed=True,
303 target_component='modules')
304 else:
305 idl_partial_interface_compiler = None
306
307 dictionary_impl_compiler = IdlCompilerDictionaryImpl(
308 output_dir, info_provider=component_info_providers[component],
309 only_if_changed=True)
310
311 idl_filenames = []
312 input_directory = os.path.join(test_input_directory, component)
313 for filename in os.listdir(input_directory):
314 if (filename.endswith('.idl') and
315 # Dependencies aren't built
316 # (they are used by the dependent)
317 filename not in DEPENDENCY_IDL_FILES):
318 idl_filenames.append(
319 os.path.realpath(
320 os.path.join(input_directory, filename)))
321 for idl_path in idl_filenames:
322 idl_basename = os.path.basename(idl_path)
323 idl_compiler.compile_file(idl_path)
324 definition_name, _ = os.path.splitext(idl_basename)
325 if definition_name in interfaces_info:
326 interface_info = interfaces_info[definition_name]
327 if interface_info['is_dictionary']:
328 dictionary_impl_compiler.compile_file(idl_path)
329 if component == 'core' and interface_info['dependencies_othe r_component_full_paths']:
330 idl_partial_interface_compiler.compile_file(idl_path)
331 if verbose:
332 print 'Compiled: %s' % idl_path
333 finally:
334 delete_cache_files()
335
336 # Detect all changes
337 output_files = list_files(output_directory)
338 passed = identical_output_files(output_files)
339 passed &= no_excess_files(output_files)
340
341 if passed:
342 if verbose:
343 print
344 print PASS_MESSAGE
345 return 0
346 print
347 print FAIL_MESSAGE
348 return 1
349
350
351 def run_bindings_tests(reset_results, verbose):
352 # Generate output into the reference directory if resetting results, or
353 # a temp directory if not.
354 if reset_results:
355 print 'Resetting results'
356 return bindings_tests(reference_directory, verbose)
357 with TemporaryDirectory() as temp_dir:
358 return bindings_tests(temp_dir, verbose)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698