Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Tools / Scripts / webkitpy / bindings / main.py
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 import fnmatch
26 import os
27 import cPickle as pickle
28 from shutil import rmtree
29 import tempfile
30 from webkitpy.common.checkout.scm.detection import detect_scm_system
31 from webkitpy.common.system.executive import ScriptError
32
33 PASS_MESSAGE = 'All tests PASS!'
34 FAIL_MESSAGE = """Some tests FAIL!
35 To update the reference files, execute:
36     run-bindings-tests --reset-results
37
38 If the failures are not due to your changes, test results may be out of sync;
39 please rebaseline them in a separate CL, after checking that tests fail in ToT.
40 In CL, please set:
41 NOTRY=true
42 TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings)
43 """
44
45 DEPENDENCY_IDL_FILES = set([
46     'SupportTestPartialInterface.idl',
47     'TestImplements.idl',
48     'TestImplements2.idl',
49     'TestImplements3.idl',
50     'TestPartialInterface.idl',
51     'TestPartialInterfacePython.idl',
52     'TestPartialInterfacePython2.idl',
53 ])
54
55 SKIP_PYTHON = 'TestSVG.idl'  # Not implementing SVG-specific hacks in Python
56
57 all_input_directory = '.'  # Relative to Source/
58 test_input_directory = os.path.join('bindings', 'tests', 'idls')
59 reference_directory = os.path.join('bindings', 'tests', 'results')
60 reference_event_names_filename = os.path.join(reference_directory, 'EventInterfaces.in')
61
62
63 class ScopedTempFileProvider(object):
64     def __init__(self):
65         self.files = []
66         self.directories = []
67
68     def __del__(self):
69         for filename in self.files:
70             os.remove(filename)
71         for directory in self.directories:
72             rmtree(directory)
73
74     def newtempfile(self):
75         file_handle, path = tempfile.mkstemp()
76         self.files.append(path)
77         return file_handle, path
78
79     def newtempdir(self):
80         path = tempfile.mkdtemp()
81         self.directories.append(path)
82         return path
83
84 provider = ScopedTempFileProvider()
85
86
87 class BindingsTests(object):
88     def __init__(self, reset_results, test_python, verbose, executive):
89         self.reset_results = reset_results
90         self.test_python = test_python
91         self.verbose = verbose
92         self.executive = executive
93         _, self.interface_dependencies_filename = provider.newtempfile()
94         _, self.interfaces_info_filename = provider.newtempfile()
95         # Generate output into the reference directory if resetting results, or
96         # a temp directory if not.
97         if reset_results:
98             self.output_directory = reference_directory
99         else:
100             self.output_directory = provider.newtempdir()
101         self.output_directory_py = provider.newtempdir()
102         self.event_names_filename = os.path.join(self.output_directory, 'EventInterfaces.in')
103
104     def run_command(self, cmd):
105         output = self.executive.run_command(cmd)
106         if output:
107             print output
108
109     def generate_from_idl_pl(self, idl_file):
110         cmd = ['perl', '-w',
111                '-Ibindings/scripts',
112                '-Ibuild/scripts',
113                '-Icore/scripts',
114                '-I../../JSON/out/lib/perl5',
115                'bindings/scripts/generate_bindings.pl',
116                # idl include directories (path relative to generate-bindings.pl)
117                '--include', '.',
118                '--outputDir', self.output_directory,
119                '--interfaceDependenciesFile', self.interface_dependencies_filename,
120                '--idlAttributesFile', 'bindings/IDLExtendedAttributes.txt',
121                idl_file]
122         try:
123             self.run_command(cmd)
124         except ScriptError, e:
125             print 'ERROR: generate_bindings.pl: ' + os.path.basename(idl_file)
126             print e.output
127             return e.exit_code
128         return 0
129
130     def generate_from_idl_py(self, idl_file):
131         cmd = ['python',
132                'bindings/scripts/unstable/idl_compiler.py',
133                '--output-dir', self.output_directory_py,
134                '--idl-attributes-file', 'bindings/IDLExtendedAttributes.txt',
135                '--interfaces-info-file', self.interfaces_info_filename,
136                idl_file]
137         try:
138             self.run_command(cmd)
139         except ScriptError, e:
140             print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file)
141             print e.output
142             return e.exit_code
143         return 0
144
145     def generate_interface_dependencies(self):
146         def idl_paths(directory):
147             return [os.path.join(directory, input_file)
148                     for input_file in os.listdir(directory)
149                     if input_file.endswith('.idl')]
150
151         def idl_paths_recursive(directory):
152             idl_paths = []
153             for dirpath, _, files in os.walk(directory):
154                 idl_paths.extend(os.path.join(dirpath, filename)
155                                  for filename in fnmatch.filter(files, '*.idl'))
156             return idl_paths
157
158         def write_list_file(idl_paths):
159             list_file, list_filename = provider.newtempfile()
160             list_contents = ''.join(idl_path + '\n'
161                                     for idl_path in idl_paths)
162             os.write(list_file, list_contents)
163             return list_filename
164
165         def compute_dependencies(idl_files_list_filename,
166                                  event_names_filename):
167             # Dummy files, required by compute_dependencies but not checked
168             _, window_constructors_file = provider.newtempfile()
169             _, workerglobalscope_constructors_file = provider.newtempfile()
170             _, sharedworkerglobalscope_constructors_file = provider.newtempfile()
171             _, dedicatedworkerglobalscope_constructors_file = provider.newtempfile()
172             _, serviceworkersglobalscope_constructors_file = provider.newtempfile()
173             cmd = ['python',
174                    'bindings/scripts/compute_dependencies.py',
175                    '--idl-files-list', idl_files_list_filename,
176                    '--interface-dependencies-file', self.interface_dependencies_filename,
177                    '--interfaces-info-file', self.interfaces_info_filename,
178                    '--window-constructors-file', window_constructors_file,
179                    '--workerglobalscope-constructors-file', workerglobalscope_constructors_file,
180                    '--sharedworkerglobalscope-constructors-file', sharedworkerglobalscope_constructors_file,
181                    '--dedicatedworkerglobalscope-constructors-file', dedicatedworkerglobalscope_constructors_file,
182                    '--serviceworkerglobalscope-constructors-file', serviceworkersglobalscope_constructors_file,
183                    '--event-names-file', event_names_filename,
184                    '--write-file-only-if-changed', '0']
185             self.run_command(cmd)
186
187         test_idl_files_list_filename = write_list_file(idl_paths(test_input_directory))
188         all_idl_files_list_filename = write_list_file(idl_paths_recursive(all_input_directory))
189
190         if self.reset_results and self.verbose:
191             print 'Reset results: EventInterfaces.in'
192         try:
193             # We first compute dependencies for testing files only,
194             # so we can compare EventInterfaces.in.
195             compute_dependencies(test_idl_files_list_filename,
196                                  self.event_names_filename)
197
198             # We then compute dependencies for all IDL files, as code generator
199             # output depends on inheritance (both ancestor chain and inherited
200             # extended attributes), and some real interfaces are special-cased,
201             # such as Node.
202             # For example, when testing the behavior of interfaces that inherit
203             # from Node, we also need to know that these inherit from
204             # EventTarget, since this is also special-cased and Node inherits
205             # from EventTarget, but this inheritance information requires
206             # computing dependencies for the real Node.idl file.
207             #
208             # Don't overwrite the event names file generated for testing IDLs
209             _, dummy_event_names_filename = provider.newtempfile()
210             compute_dependencies(all_idl_files_list_filename,
211                                  dummy_event_names_filename)
212         except ScriptError, e:
213             print 'ERROR: compute_dependencies.py'
214             print e.output
215             return e.exit_code
216         return 0
217
218     def identical_file(self, reference_filename, output_filename):
219         reference_basename = os.path.basename(reference_filename)
220         cmd = ['diff',
221                '-u',
222                '-N',
223                reference_filename,
224                output_filename]
225         try:
226             self.run_command(cmd)
227         except ScriptError, e:
228             # run_command throws an exception on diff (b/c non-zero exit code)
229             print 'FAIL: %s' % reference_basename
230             print e.output
231             return False
232
233         if self.verbose:
234             print 'PASS: %s' % reference_basename
235         return True
236
237     def identical_output_files(self, output_directory):
238         file_pairs = [(os.path.join(reference_directory, output_file),
239                        os.path.join(output_directory, output_file))
240                       for output_file in os.listdir(output_directory)
241                       # FIXME: add option to compiler to not generate tables
242                       if output_file != 'parsetab.py']
243         return all([self.identical_file(reference_filename, output_filename)
244                     for (reference_filename, output_filename) in file_pairs])
245
246     def no_excess_files(self):
247         generated_files = set(os.listdir(self.output_directory))
248         generated_files.add('.svn')  # Subversion working copy directory
249         excess_files = [output_file
250                         for output_file in os.listdir(reference_directory)
251                         if output_file not in generated_files]
252         if excess_files:
253             print ('Excess reference files! '
254                   '(probably cruft from renaming or deleting):\n' +
255                   '\n'.join(excess_files))
256             return False
257         return True
258
259     def run_tests(self):
260         # Generate output, immediately dying on failure
261         if self.generate_interface_dependencies():
262             return False
263
264         for input_filename in os.listdir(test_input_directory):
265             if not input_filename.endswith('.idl'):
266                 continue
267             if input_filename in DEPENDENCY_IDL_FILES:
268                 # Dependencies aren't built (they are used by the dependent)
269                 if self.verbose:
270                     print 'DEPENDENCY: %s' % input_filename
271                 continue
272
273             idl_path = os.path.join(test_input_directory, input_filename)
274             if self.generate_from_idl_pl(idl_path):
275                 return False
276             if self.reset_results and self.verbose:
277                 print 'Reset results: %s' % input_filename
278             if not self.test_python:
279                 continue
280             if input_filename == SKIP_PYTHON:
281                 if self.verbose:
282                     print 'SKIP: %s' % input_filename
283                 continue
284             if self.generate_from_idl_py(idl_path):
285                 return False
286
287         # Detect all changes
288         passed = self.identical_file(reference_event_names_filename,
289                                      self.event_names_filename)
290         passed &= self.identical_output_files(self.output_directory)
291         if self.test_python:
292             if self.verbose:
293                 print
294                 print 'Python:'
295             passed &= self.identical_output_files(self.output_directory_py)
296         passed &= self.no_excess_files()
297         return passed
298
299     def main(self):
300         current_scm = detect_scm_system(os.curdir)
301         os.chdir(os.path.join(current_scm.checkout_root, 'Source'))
302
303         all_tests_passed = self.run_tests()
304         if all_tests_passed:
305             if self.verbose:
306                 print
307                 print PASS_MESSAGE
308             return 0
309         print
310         print FAIL_MESSAGE
311         return -1