Implement a new edit command to change the enclosing list type
[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         return changes_found
131
132     def test_matches_patterns(self, test):
133         if not self.patterns:
134             return True
135         for pattern in self.patterns:
136             if fnmatch.fnmatch(test, pattern):
137                 return True
138         return False
139
140     def run_tests(self, generator, input_directory, reference_directory, supplemental_dependency_file):
141         work_directory = reference_directory
142
143         passed = True
144         for input_file in os.listdir(input_directory):
145             (name, extension) = os.path.splitext(input_file)
146             if extension != '.idl':
147                 continue
148
149             if not self.test_matches_patterns(input_file):
150                 continue
151
152             # Generate output into the work directory (either the given one or a
153             # temp one if not reset_results is performed)
154             if not self.reset_results:
155                 work_directory = tempfile.mkdtemp()
156
157             if self.generate_from_idl(generator,
158                                       os.path.join(input_directory, input_file),
159                                       work_directory,
160                                       supplemental_dependency_file):
161                 passed = False
162
163             if self.reset_results:
164                 print("Reset results: (%s) %s" % (generator, input_file))
165                 continue
166
167             # Detect changes
168             if self.detect_changes(generator, work_directory, reference_directory):
169                 passed = False
170             shutil.rmtree(work_directory)
171
172         return passed
173
174     def close_and_remove(self, temporary_file):
175         os.close(temporary_file[0])
176         os.remove(temporary_file[1])
177
178     def main(self):
179         current_scm = detect_scm_system(os.curdir)
180         os.chdir(os.path.join(current_scm.checkout_root, 'Source'))
181
182         all_tests_passed = True
183
184         input_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test')
185         supplemental_dependency_file = tempfile.mkstemp()
186         window_constructors_file = tempfile.mkstemp()
187         workerglobalscope_constructors_file = tempfile.mkstemp()
188         dedicatedworkerglobalscope_constructors_file = tempfile.mkstemp()
189         serviceworkerglobalscope_constructors_file = tempfile.mkstemp()
190         workletglobalscope_constructors_file = tempfile.mkstemp()
191         paintworkletglobalscope_constructors_file = tempfile.mkstemp()
192         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]):
193             print('Failed to generate a supplemental dependency file.')
194             self.close_and_remove(supplemental_dependency_file)
195             self.close_and_remove(window_constructors_file)
196             self.close_and_remove(workerglobalscope_constructors_file)
197             self.close_and_remove(dedicatedworkerglobalscope_constructors_file)
198             self.close_and_remove(serviceworkerglobalscope_constructors_file)
199             self.close_and_remove(workletglobalscope_constructors_file)
200             self.close_and_remove(paintworkletglobalscope_constructors_file)
201             return -1
202
203         for generator in self.generators:
204             input_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test')
205             reference_directory = os.path.join('WebCore', 'bindings', 'scripts', 'test', generator)
206             if not self.run_tests(generator, input_directory, reference_directory, supplemental_dependency_file[1]):
207                 all_tests_passed = False
208
209         self.close_and_remove(supplemental_dependency_file)
210         self.close_and_remove(window_constructors_file)
211         self.close_and_remove(workerglobalscope_constructors_file)
212         self.close_and_remove(dedicatedworkerglobalscope_constructors_file)
213         self.close_and_remove(serviceworkerglobalscope_constructors_file)
214         self.close_and_remove(workletglobalscope_constructors_file)
215         self.close_and_remove(paintworkletglobalscope_constructors_file)
216
217         if self.json_file_name:
218             json_data = {
219                 'failures': self.failures,
220             }
221
222             with open(self.json_file_name, 'w') as json_file:
223                 json.dump(json_data, json_file)
224
225         print('')
226         if all_tests_passed:
227             print('All tests PASS!')
228             return 0
229         else:
230             print('Some tests FAIL! (To update the reference files, execute "run-bindings-tests --reset-results")')
231             return -1