OLD | NEW |
| (Empty) |
1 # Copyright (C) 2014 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 shutil | |
28 import sys | |
29 import tempfile | |
30 | |
31 from webkitpy.common.checkout.scm.detection import detect_scm_system | |
32 from webkitpy.common.system import executive | |
33 from webkitpy.common.system.executive import ScriptError | |
34 | |
35 # Add Source path to PYTHONPATH to support function calls to bindings/scripts | |
36 # for compute_dependencies and idl_compiler | |
37 module_path = os.path.dirname(__file__) | |
38 source_path = os.path.normpath(os.path.join(module_path, os.pardir, | |
39 os.pardir, os.pardir, os.pardir, | |
40 'Source')) | |
41 sys.path.append(source_path) | |
42 | |
43 from bindings.scripts.compute_interfaces_info import compute_interfaces_info, in
terfaces_info | |
44 from bindings.scripts.idl_compiler import IdlCompilerV8 | |
45 | |
46 | |
47 PASS_MESSAGE = 'All tests PASS!' | |
48 FAIL_MESSAGE = """Some tests FAIL! | |
49 To update the reference files, execute: | |
50 run-bindings-tests --reset-results | |
51 | |
52 If the failures are not due to your changes, test results may be out of sync; | |
53 please rebaseline them in a separate CL, after checking that tests fail in ToT. | |
54 In CL, please set: | |
55 NOTRY=true | |
56 TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings) | |
57 """ | |
58 | |
59 DEPENDENCY_IDL_FILES = set([ | |
60 'SupportTestPartialInterface.idl', | |
61 'TestImplements.idl', | |
62 'TestImplements2.idl', | |
63 'TestImplements3.idl', | |
64 'TestPartialInterface.idl', | |
65 'TestPartialInterfacePython.idl', | |
66 'TestPartialInterfacePython2.idl', | |
67 ]) | |
68 | |
69 | |
70 EXTENDED_ATTRIBUTES_FILE = 'bindings/IDLExtendedAttributes.txt' | |
71 | |
72 all_input_directory = '.' # Relative to Source/ | |
73 test_input_directory = os.path.join('bindings', 'tests', 'idls') | |
74 reference_directory = os.path.join('bindings', 'tests', 'results') | |
75 | |
76 | |
77 class ScopedTempFileProvider(object): | |
78 def __init__(self): | |
79 self.file_handles = [] | |
80 self.file_paths = [] | |
81 self.dir_paths = [] | |
82 | |
83 def __enter__(self): | |
84 return self | |
85 | |
86 def __exit__(self, exc_type, exc_value, traceback): | |
87 for file_handle in self.file_handles: | |
88 os.close(file_handle) | |
89 for file_path in self.file_paths: | |
90 os.remove(file_path) | |
91 for dir_path in self.dir_paths: | |
92 # Temporary directories are used as output directories, so they | |
93 # contains unknown files (they aren't empty), hence use rmtree | |
94 shutil.rmtree(dir_path) | |
95 | |
96 def new_temp_file(self): | |
97 file_handle, file_path = tempfile.mkstemp() | |
98 self.file_handles.append(file_handle) | |
99 self.file_paths.append(file_path) | |
100 return file_handle, file_path | |
101 | |
102 def new_temp_dir(self): | |
103 dir_path = tempfile.mkdtemp() | |
104 self.dir_paths.append(dir_path) | |
105 return dir_path | |
106 | |
107 | |
108 class DartTests(object): | |
109 def __init__(self, reset_results, verbose, provider): | |
110 self.reset_results = reset_results | |
111 self.verbose = verbose | |
112 self.executive = executive.Executive() | |
113 self.provider = provider | |
114 self.idl_compiler = None | |
115 _, self.interfaces_info_filename = provider.new_temp_file() | |
116 # Generate output into the reference directory if resetting results, or | |
117 # a temp directory if not. | |
118 if reset_results: | |
119 self.output_directory = reference_directory | |
120 else: | |
121 self.output_directory = provider.new_temp_dir() | |
122 | |
123 def run_command(self, cmd): | |
124 output = self.executive.run_command(cmd) | |
125 if output: | |
126 print output | |
127 | |
128 def generate_from_idl(self, idl_file): | |
129 try: | |
130 idl_file_fullpath = os.path.realpath(idl_file) | |
131 self.idl_compiler.compile_file(idl_file_fullpath) | |
132 except ScriptError, e: | |
133 print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file) | |
134 print e.output | |
135 return e.exit_code | |
136 | |
137 return 0 | |
138 | |
139 def generate_interface_dependencies(self): | |
140 def idl_paths(directory): | |
141 return [os.path.join(directory, input_file) | |
142 for input_file in os.listdir(directory) | |
143 if input_file.endswith('.idl')] | |
144 | |
145 def idl_paths_recursive(directory): | |
146 idl_paths = [] | |
147 for dirpath, _, files in os.walk(directory): | |
148 idl_paths.extend(os.path.join(dirpath, filename) | |
149 for filename in fnmatch.filter(files, '*.idl')) | |
150 return idl_paths | |
151 | |
152 def write_list_file(idl_paths): | |
153 list_file, list_filename = self.provider.new_temp_file() | |
154 list_contents = ''.join(idl_path + '\n' | |
155 for idl_path in idl_paths) | |
156 os.write(list_file, list_contents) | |
157 return list_filename | |
158 | |
159 # We compute interfaces info for *all* IDL files, not just test IDL | |
160 # files, as code generator output depends on inheritance (both ancestor | |
161 # chain and inherited extended attributes), and some real interfaces | |
162 # are special-cased, such as Node. | |
163 # | |
164 # For example, when testing the behavior of interfaces that inherit | |
165 # from Node, we also need to know that these inherit from EventTarget, | |
166 # since this is also special-cased and Node inherits from EventTarget, | |
167 # but this inheritance information requires computing dependencies for | |
168 # the real Node.idl file. | |
169 try: | |
170 compute_interfaces_info(idl_paths_recursive(all_input_directory)) | |
171 | |
172 except ScriptError, e: | |
173 print 'ERROR: compute_interfaces_info.py' | |
174 print e.output | |
175 return e.exit_code | |
176 | |
177 return 0 | |
178 | |
179 def delete_cache_files(self): | |
180 # FIXME: Instead of deleting cache files, don't generate them. | |
181 cache_files = [os.path.join(self.output_directory, output_file) | |
182 for output_file in os.listdir(self.output_directory) | |
183 if (output_file in ('lextab.py', # PLY lex | |
184 'lextab.pyc', | |
185 'parsetab.pickle') or # PLY yacc | |
186 output_file.endswith('.cache'))] # Jinja | |
187 for cache_file in cache_files: | |
188 os.remove(cache_file) | |
189 | |
190 def identical_file(self, reference_filename, output_filename): | |
191 reference_basename = os.path.basename(reference_filename) | |
192 cmd = ['diff', | |
193 '-u', | |
194 '-N', | |
195 reference_filename, | |
196 output_filename] | |
197 try: | |
198 self.run_command(cmd) | |
199 except ScriptError, e: | |
200 # run_command throws an exception on diff (b/c non-zero exit code) | |
201 print 'FAIL: %s' % reference_basename | |
202 print e.output | |
203 return False | |
204 | |
205 if self.verbose: | |
206 print 'PASS: %s' % reference_basename | |
207 return True | |
208 | |
209 def identical_output_files(self): | |
210 file_pairs = [(os.path.join(reference_directory, output_file), | |
211 os.path.join(self.output_directory, output_file)) | |
212 for output_file in os.listdir(self.output_directory)] | |
213 return all([self.identical_file(reference_filename, output_filename) | |
214 for (reference_filename, output_filename) in file_pairs]) | |
215 | |
216 def no_excess_files(self): | |
217 generated_files = set(os.listdir(self.output_directory)) | |
218 generated_files.add('.svn') # Subversion working copy directory | |
219 excess_files = [output_file | |
220 for output_file in os.listdir(reference_directory) | |
221 if output_file not in generated_files] | |
222 if excess_files: | |
223 print ('Excess reference files! ' | |
224 '(probably cruft from renaming or deleting):\n' + | |
225 '\n'.join(excess_files)) | |
226 return False | |
227 return True | |
228 | |
229 def run_tests(self): | |
230 # Generate output, immediately dying on failure | |
231 if self.generate_interface_dependencies(): | |
232 return False | |
233 | |
234 self.idl_compiler = IdlCompilerV8(self.output_directory, | |
235 EXTENDED_ATTRIBUTES_FILE, | |
236 interfaces_info=interfaces_info, | |
237 only_if_changed=True) | |
238 | |
239 for input_filename in os.listdir(test_input_directory): | |
240 if not input_filename.endswith('.idl'): | |
241 continue | |
242 if input_filename in DEPENDENCY_IDL_FILES: | |
243 # Dependencies aren't built (they are used by the dependent) | |
244 if self.verbose: | |
245 print 'DEPENDENCY: %s' % input_filename | |
246 continue | |
247 | |
248 idl_path = os.path.join(test_input_directory, input_filename) | |
249 if self.generate_from_idl(idl_path): | |
250 return False | |
251 if self.reset_results and self.verbose: | |
252 print 'Reset results: %s' % input_filename | |
253 | |
254 self.delete_cache_files() | |
255 | |
256 # Detect all changes | |
257 passed = self.identical_output_files() | |
258 passed &= self.no_excess_files() | |
259 return passed | |
260 | |
261 def main(self): | |
262 current_scm = detect_scm_system(os.curdir) | |
263 os.chdir(os.path.join(current_scm.checkout_root, 'Source')) | |
264 | |
265 all_tests_passed = self.run_tests() | |
266 if all_tests_passed: | |
267 if self.verbose: | |
268 print | |
269 print PASS_MESSAGE | |
270 return 0 | |
271 print | |
272 print FAIL_MESSAGE | |
273 return -1 | |
274 | |
275 | |
276 def run_dart_tests(reset_results, verbose): | |
277 with ScopedTempFileProvider() as provider: | |
278 return DartTests(reset_results, verbose, provider).main() | |
OLD | NEW |