65a588ebe458020c9f53d52898b15d18958ff540
[WebKit-https.git] / 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 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 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 json
27 import os
28 import os.path
29 import shutil
30 import subprocess
31 import sys
32 import tempfile
33 from webkitpy.common.checkout.scm.detection import detect_scm_system
34 from webkitpy.common.system.executive import ScriptError
35
36
37 class BindingsTests:
38
39     def __init__(self, reset_results, generators, executive, verbose, patterns, json_file_name):
40         self.reset_results = reset_results
41         self.generators = generators
42         self.executive = executive
43         self.verbose = verbose
44         self.patterns = patterns
45         self.json_file_name = json_file_name
46
47         if self.json_file_name:
48             self.failures = []
49
50     def generate_from_idl(self, generator, idl_file, output_directory, supplemental_dependency_file):
51         cmd = ['perl', '-w',
52                '-IWebCore/bindings/scripts',
53                'WebCore/bindings/scripts/generate-bindings.pl',
54                # idl include directories (path relative to generate-bindings.pl)
55                '--include', '.',
56                '--defines', 'TESTING_%s' % generator,
57                '--generator', generator,
58                '--outputDir', output_directory,
59                '--supplementalDependencyFile', supplemental_dependency_file,
60                '--idlAttributesFile', 'WebCore/bindings/scripts/IDLAttributes.json',
61                idl_file]
62
63         exit_code = 0
64         try:
65             output = self.executive.run_command(cmd)
66             if output:
67                 print(output)
68         except ScriptError as e:
69             print(e.output)
70             exit_code = e.exit_code
71         return exit_code
72
73     def generate_supplemental_dependency(self, input_directory, supplemental_dependency_file, window_constructors_file, workerglobalscope_constructors_file, dedicatedworkerglobalscope_constructors_file, serviceworkerglobalscope_constructors_file, workletglobalscope_constructors_file, paintworkletglobalscope_constructors_file):
74         idl_files_list = tempfile.mkstemp()
75         for input_file in os.listdir(input_directory):
76             (name, extension) = os.path.splitext(input_file)
77             if extension != '.idl':
78                 continue
79             os.write(idl_files_list[0], os.path.join(input_directory, input_file) + "\n")
80         os.close(idl_files_list[0])
81
82         cmd = ['perl', '-w',
83                '-IWebCore/bindings/scripts',
84                'WebCore/bindings/scripts/preprocess-idls.pl',
85                '--idlFilesList', idl_files_list[1],
86                '--defines', '',
87                '--supplementalDependencyFile', supplemental_dependency_file,
88                '--windowConstructorsFile', window_constructors_file,
89                '--workerGlobalScopeConstructorsFile', workerglobalscope_constructors_file,
90                '--dedicatedWorkerGlobalScopeConstructorsFile', dedicatedworkerglobalscope_constructors_file,
91                '--serviceWorkerGlobalScopeConstructorsFile', serviceworkerglobalscope_constructors_file,
92                '--workletGlobalScopeConstructorsFile', workletglobalscope_constructors_file,
93                '--paintWorkletGlobalScopeConstructorsFile', paintworkletglobalscope_constructors_file]
94
95         exit_code = 0
96         try:
97             output = self.executive.run_command(cmd)
98             if output:
99                 print(output)
100         except ScriptError as e:
101             print(e.output)
102             exit_code = e.exit_code
103         os.remove(idl_files_list[1])
104         return exit_code
105
106     def detect_changes(self, generator, work_directory, reference_directory):
107         changes_found = False
108         for output_file in os.listdir(work_directory):
109             cmd = ['diff',
110                    '-u',
111                    '-N',
112                    os.path.join(reference_directory, output_file),
113                    os.path.join(work_directory, output_file)]
114
115             exit_code = 0
116             try:
117                 output = self.executive.run_command(cmd)
118             except ScriptError as e:
119                 output = e.output
120                 exit_code = e.exit_code
121
122             if exit_code or output:
123                 print('FAIL: (%s) %s' % (generator, output_file))
124                 print(output)
125                 changes_found = True
126                 if self.json_file_name:
127                     self.failures.append("(%s) %s" % (generator, output_file))
128             elif self.verbose:
129                 print('PASS: (%s) %s' % (generator, output_file))
130             sys.stdout.flush()
131         return changes_found
132
133     def test_matches_patterns(self, test):
134         if not self.patterns:
135             return True
136         for pattern in self.patterns:
137             if fnmatch.fnmatch(test, pattern):
138                 return True
139         return False
140
141     def run_tests(self, generator, input_directory, reference_directory, supplemental_dependency_file):
142         work_directory = reference_directory
143
144         passed = True
145         for input_file in os.listdir(input_directory):
146             (name, extension) = os.path.splitext(input_file)
147             if extension != '.idl':
148                 continue
149
150             if not self.test_matches_patterns(input_file):
151                 continue
152
153             # Generate output into the work directory (either the given one or a
154             # temp one if not reset_results is performed)
155             if not self.reset_results:
156                 work_directory = tempfile.mkdtemp()
157
158             if self.generate_from_idl(generator,
159                                       os.path.join(input_directory, input_file),
160                                       work_directory,
161                                       supplemental_dependency_file):
162                 passed = False
163
164             if self.reset_results:
165                 print("Reset results: (%s) %s" % (generator, input_file))
166                 continue
167
168             # Detect changes
169             if self.detect_changes(generator, work_directory, reference_directory):
170                 passed = False
171             shutil.rmtree(work_directory)
172
173         return passed
174
175     def close_and_remove(self, temporary_file):
176         os.close(temporary_file[0])
177         os.remove(temporary_file[1])
178
179     def main(self):
180         current_scm = detect_scm_system(os.curdir)
181         os.chdir(os.path.join(current_scm.checkout_root, 'Source'))
182
183         all_tests_passed = True
184
185         input_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test')
186         supplemental_dependency_file = tempfile.mkstemp()
187         window_constructors_file = tempfile.mkstemp()
188         workerglobalscope_constructors_file = tempfile.mkstemp()
189         dedicatedworkerglobalscope_constructors_file = tempfile.mkstemp()
190         serviceworkerglobalscope_constructors_file = tempfile.mkstemp()
191         workletglobalscope_constructors_file = tempfile.mkstemp()
192         paintworkletglobalscope_constructors_file = tempfile.mkstemp()
193         if self.generate_supplemental_dependency(input_directory, supplemental_dependency_file[1], window_constructors_file[1], workerglobalscope_constructors_file[1], dedicatedworkerglobalscope_constructors_file[1], serviceworkerglobalscope_constructors_file[1], workletglobalscope_constructors_file[1], paintworkletglobalscope_constructors_file[1]):
194             print('Failed to generate a supplemental dependency file.')
195             self.close_and_remove(supplemental_dependency_file)
196             self.close_and_remove(window_constructors_file)
197             self.close_and_remove(workerglobalscope_constructors_file)
198             self.close_and_remove(dedicatedworkerglobalscope_constructors_file)
199             self.close_and_remove(serviceworkerglobalscope_constructors_file)
200             self.close_and_remove(workletglobalscope_constructors_file)
201             self.close_and_remove(paintworkletglobalscope_constructors_file)
202             return -1
203
204         for generator in self.generators:
205             input_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test')
206             reference_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test', generator)
207             if not self.run_tests(generator, input_directory, reference_directory, supplemental_dependency_file[1]):
208                 all_tests_passed = False
209
210         self.close_and_remove(supplemental_dependency_file)
211         self.close_and_remove(window_constructors_file)
212         self.close_and_remove(workerglobalscope_constructors_file)
213         self.close_and_remove(dedicatedworkerglobalscope_constructors_file)
214         self.close_and_remove(serviceworkerglobalscope_constructors_file)
215         self.close_and_remove(workletglobalscope_constructors_file)
216         self.close_and_remove(paintworkletglobalscope_constructors_file)
217
218         if self.json_file_name:
219             json_data = {
220                 'failures': self.failures,
221             }
222
223             with open(self.json_file_name, 'w') as json_file:
224                 json.dump(json_data, json_file)
225
226         print('')
227         if all_tests_passed:
228             print('All tests PASS!')
229             return 0
230         else:
231             print('Some tests FAIL! (To update the reference files, execute "run-bindings-tests --reset-results")')
232             return -1