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 |