Upstream version 7.36.149.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 from contextlib import contextmanager
26 import filecmp
27 import fnmatch
28 import os
29 import shutil
30 import sys
31 import tempfile
32
33 from webkitpy.common.system.executive import Executive
34
35 # Source/ path is needed both to find input IDL files, and to import other
36 # Python modules.
37 module_path = os.path.dirname(__file__)
38 source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir,
39                                             os.pardir, os.pardir, 'Source'))
40 sys.path.append(source_path)  # for Source/bindings imports
41
42 from bindings.scripts.compute_interfaces_info import compute_interfaces_info, interfaces_info
43 from bindings.scripts.idl_compiler import IdlCompilerV8
44
45
46 PASS_MESSAGE = 'All tests PASS!'
47 FAIL_MESSAGE = """Some tests FAIL!
48 To update the reference files, execute:
49     run-bindings-tests --reset-results
50
51 If the failures are not due to your changes, test results may be out of sync;
52 please rebaseline them in a separate CL, after checking that tests fail in ToT.
53 In CL, please set:
54 NOTRY=true
55 TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings)
56 """
57
58 DEPENDENCY_IDL_FILES = frozenset([
59     'TestImplements.idl',
60     'TestImplements2.idl',
61     'TestImplements3.idl',
62     'TestPartialInterface.idl',
63     'TestPartialInterface2.idl',
64 ])
65
66
67 test_input_directory = os.path.join(source_path, 'bindings', 'tests', 'idls')
68 reference_directory = os.path.join(source_path, 'bindings', 'tests', 'results')
69
70
71 @contextmanager
72 def TemporaryDirectory():
73     """Wrapper for tempfile.mkdtemp() so it's usable with 'with' statement.
74
75     Simple backport of tempfile.TemporaryDirectory from Python 3.2.
76     """
77     name = tempfile.mkdtemp()
78     try:
79         yield name
80     finally:
81         shutil.rmtree(name)
82
83
84 def generate_interface_dependencies():
85     def idl_paths_recursive(directory):
86         # This is slow, especially on Windows, due to os.walk making
87         # excess stat() calls. Faster versions may appear in Python 3.5 or
88         # later:
89         # https://github.com/benhoyt/scandir
90         # http://bugs.python.org/issue11406
91         idl_paths = []
92         for dirpath, _, files in os.walk(directory):
93             idl_paths.extend(os.path.join(dirpath, filename)
94                              for filename in fnmatch.filter(files, '*.idl'))
95         return idl_paths
96
97     # We compute interfaces info for *all* IDL files, not just test IDL
98     # files, as code generator output depends on inheritance (both ancestor
99     # chain and inherited extended attributes), and some real interfaces
100     # are special-cased, such as Node.
101     #
102     # For example, when testing the behavior of interfaces that inherit
103     # from Node, we also need to know that these inherit from EventTarget,
104     # since this is also special-cased and Node inherits from EventTarget,
105     # but this inheritance information requires computing dependencies for
106     # the real Node.idl file.
107     compute_interfaces_info(idl_paths_recursive(source_path))
108
109
110 def bindings_tests(output_directory, verbose):
111     executive = Executive()
112
113     def diff(filename1, filename2):
114         # Python's difflib module is too slow, especially on long output, so
115         # run external diff(1) command
116         cmd = ['diff',
117                '-u',  # unified format
118                '-N',  # treat absent files as empty
119                filename1,
120                filename2]
121         # Return output and don't raise exception, even though diff(1) has
122         # non-zero exit if files differ.
123         return executive.run_command(cmd, error_handler=lambda x: None)
124
125     def delete_cache_files():
126         # FIXME: Instead of deleting cache files, don't generate them.
127         cache_files = [os.path.join(output_directory, output_file)
128                        for output_file in os.listdir(output_directory)
129                        if (output_file in ('lextab.py',  # PLY lex
130                                            'lextab.pyc',
131                                            'parsetab.pickle') or  # PLY yacc
132                            output_file.endswith('.cache'))]  # Jinja
133         for cache_file in cache_files:
134             os.remove(cache_file)
135
136     def identical_file(reference_filename, output_filename):
137         reference_basename = os.path.basename(reference_filename)
138
139         if not os.path.isfile(reference_filename):
140             print 'Missing reference file!'
141             print '(if adding new test, update reference files)'
142             print reference_basename
143             print
144             return False
145
146         if not filecmp.cmp(reference_filename, output_filename):
147             # cmp is much faster than diff, and usual case is "no differance",
148             # so only run diff if cmp detects a difference
149             print 'FAIL: %s' % reference_basename
150             print diff(reference_filename, output_filename)
151             return False
152
153         if verbose:
154             print 'PASS: %s' % reference_basename
155         return True
156
157     def identical_output_files():
158         file_pairs = [(os.path.join(reference_directory, output_file),
159                        os.path.join(output_directory, output_file))
160                       for output_file in os.listdir(output_directory)]
161         return all([identical_file(reference_filename, output_filename)
162                     for (reference_filename, output_filename) in file_pairs])
163
164     def no_excess_files():
165         generated_files = set(os.listdir(output_directory))
166         generated_files.add('.svn')  # Subversion working copy directory
167         excess_files = [output_file
168                         for output_file in os.listdir(reference_directory)
169                         if output_file not in generated_files]
170         if excess_files:
171             print ('Excess reference files! '
172                   '(probably cruft from renaming or deleting):\n' +
173                   '\n'.join(excess_files))
174             return False
175         return True
176
177     try:
178         generate_interface_dependencies()
179         idl_compiler = IdlCompilerV8(output_directory,
180                                      interfaces_info=interfaces_info,
181                                      only_if_changed=True)
182
183         idl_basenames = [filename
184                          for filename in os.listdir(test_input_directory)
185                          if (filename.endswith('.idl') and
186                              # Dependencies aren't built
187                              # (they are used by the dependent)
188                              filename not in DEPENDENCY_IDL_FILES)]
189         for idl_basename in idl_basenames:
190             idl_path = os.path.realpath(
191                 os.path.join(test_input_directory, idl_basename))
192             idl_compiler.compile_file(idl_path)
193             if verbose:
194                 print 'Compiled: %s' % filename
195     finally:
196         delete_cache_files()
197
198     # Detect all changes
199     passed = identical_output_files()
200     passed &= no_excess_files()
201
202     if passed:
203         if verbose:
204             print
205             print PASS_MESSAGE
206         return 0
207     print
208     print FAIL_MESSAGE
209     return 1
210
211
212 def run_bindings_tests(reset_results, verbose):
213     # Generate output into the reference directory if resetting results, or
214     # a temp directory if not.
215     if reset_results:
216         print 'Resetting results'
217         return bindings_tests(reference_directory, verbose)
218     with TemporaryDirectory() as temp_dir:
219         return bindings_tests(temp_dir, verbose)