Commit 021b62a0 authored by nbarth@chromium.org's avatar nbarth@chromium.org

Cleanup run-bindings-tests and use filecmp (23% faster r-b-t)

The use of filecmp:
* speeds up r-b-t by 23% (if no diffs): (885 ms vs. 1.150 sec)
* uses only 1 process (if no diffs): 30 => 1
* if diffs, same speed
(can't measure difference, because filecmp is basically instantaneous on my machine)

Also some cleanup:
* remove dead code
* simplify temp file handling (caused build bot failures previously)
* don't catch exceptions, so easier to debug errors in CG
(because it's just one process now)

Currently r-b-t launches a separate diff(1) process for each file it compares.
Python has a built-in diff facility, which is extremely fast and avoids other processes.
This should make a big difference on Windows, meaning faster uploads for
Windows developers and less load on build bots.

Also a lot of the code is no longer necessary, after Terry's simplifications.
We can eliminate all the redundant temp file handling, and just need a simple
temp dir class.

R=haraken
BUG=341748

Review URL: https://codereview.chromium.org/216753002

git-svn-id: svn://svn.chromium.org/blink/trunk@170315 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 0976deaf
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
import filecmp
import fnmatch import fnmatch
import os import os
import shutil import shutil
...@@ -30,7 +31,6 @@ import tempfile ...@@ -30,7 +31,6 @@ import tempfile
from webkitpy.common.checkout.scm.detection import detect_scm_system from webkitpy.common.checkout.scm.detection import detect_scm_system
from webkitpy.common.system import executive from webkitpy.common.system import executive
from webkitpy.common.system.executive import ScriptError
# Add Source path to PYTHONPATH to support function calls to bindings/scripts # Add Source path to PYTHONPATH to support function calls to bindings/scripts
# for compute_interfaces_info and idl_compiler # for compute_interfaces_info and idl_compiler
...@@ -71,73 +71,45 @@ test_input_directory = os.path.join('bindings', 'tests', 'idls') ...@@ -71,73 +71,45 @@ test_input_directory = os.path.join('bindings', 'tests', 'idls')
reference_directory = os.path.join('bindings', 'tests', 'results') reference_directory = os.path.join('bindings', 'tests', 'results')
class ScopedTempFileProvider(object): class ScopedTempDir(object):
"""Wrapper for tempfile.mkdtemp() so it's usable with 'with' statement."""
def __init__(self): def __init__(self):
self.file_handles = [] self.dir_path = tempfile.mkdtemp()
self.file_paths = []
self.dir_paths = []
def __enter__(self): def __enter__(self):
return self return self.dir_path
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
for file_handle in self.file_handles: # The temporary directory is used as an output directory, so it
os.close(file_handle) # contains unknown files (it isn't empty), hence use rmtree
for file_path in self.file_paths: shutil.rmtree(self.dir_path)
os.remove(file_path)
for dir_path in self.dir_paths:
# Temporary directories are used as output directories, so they
# contains unknown files (they aren't empty), hence use rmtree
shutil.rmtree(dir_path)
def new_temp_file(self):
file_handle, file_path = tempfile.mkstemp()
self.file_handles.append(file_handle)
self.file_paths.append(file_path)
return file_handle, file_path
def new_temp_dir(self):
dir_path = tempfile.mkdtemp()
self.dir_paths.append(dir_path)
return dir_path
class BindingsTests(object): class BindingsTests(object):
def __init__(self, reset_results, verbose, provider): def __init__(self, output_directory, verbose):
self.reset_results = reset_results
self.verbose = verbose self.verbose = verbose
self.executive = executive.Executive() self.executive = executive.Executive()
self.provider = provider
self.idl_compiler = None self.idl_compiler = None
# Generate output into the reference directory if resetting results, or self.output_directory = output_directory
# a temp directory if not.
if reset_results: def diff(self, filename1, filename2):
self.output_directory = reference_directory # Python's difflib module is too slow, especially on long output, so
else: # run external diff(1) command
self.output_directory = provider.new_temp_dir() cmd = ['diff',
'-u',
def run_command(self, cmd): '-N',
output = self.executive.run_command(cmd) filename1,
if output: filename2]
print output # Return output and don't raise exception, even though diff(1) has
# non-zero exit if files differ.
return self.executive.run_command(cmd, error_handler=lambda x: None)
def generate_from_idl(self, idl_file): def generate_from_idl(self, idl_file):
idl_file_fullpath = os.path.realpath(idl_file) idl_file_fullpath = os.path.realpath(idl_file)
try: self.idl_compiler.compile_file(idl_file_fullpath)
self.idl_compiler.compile_file(idl_file_fullpath)
except Exception as err:
print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file)
print err
return 1
return 0
def generate_interface_dependencies(self): def generate_interface_dependencies(self):
def idl_paths(directory):
return [os.path.join(directory, input_file)
for input_file in os.listdir(directory)
if input_file.endswith('.idl')]
def idl_paths_recursive(directory): def idl_paths_recursive(directory):
idl_paths = [] idl_paths = []
for dirpath, _, files in os.walk(directory): for dirpath, _, files in os.walk(directory):
...@@ -145,13 +117,6 @@ class BindingsTests(object): ...@@ -145,13 +117,6 @@ class BindingsTests(object):
for filename in fnmatch.filter(files, '*.idl')) for filename in fnmatch.filter(files, '*.idl'))
return idl_paths return idl_paths
def write_list_file(idl_paths):
list_file, list_filename = self.provider.new_temp_file()
list_contents = ''.join(idl_path + '\n'
for idl_path in idl_paths)
os.write(list_file, list_contents)
return list_filename
# We compute interfaces info for *all* IDL files, not just test IDL # We compute interfaces info for *all* IDL files, not just test IDL
# files, as code generator output depends on inheritance (both ancestor # files, as code generator output depends on inheritance (both ancestor
# chain and inherited extended attributes), and some real interfaces # chain and inherited extended attributes), and some real interfaces
...@@ -162,14 +127,7 @@ class BindingsTests(object): ...@@ -162,14 +127,7 @@ class BindingsTests(object):
# since this is also special-cased and Node inherits from EventTarget, # since this is also special-cased and Node inherits from EventTarget,
# but this inheritance information requires computing dependencies for # but this inheritance information requires computing dependencies for
# the real Node.idl file. # the real Node.idl file.
try: compute_interfaces_info(idl_paths_recursive(all_input_directory))
compute_interfaces_info(idl_paths_recursive(all_input_directory))
except Exception as err:
print 'ERROR: compute_interfaces_info.py'
print err
return 1
return 0
def delete_cache_files(self): def delete_cache_files(self):
# FIXME: Instead of deleting cache files, don't generate them. # FIXME: Instead of deleting cache files, don't generate them.
...@@ -184,17 +142,10 @@ class BindingsTests(object): ...@@ -184,17 +142,10 @@ class BindingsTests(object):
def identical_file(self, reference_filename, output_filename): def identical_file(self, reference_filename, output_filename):
reference_basename = os.path.basename(reference_filename) reference_basename = os.path.basename(reference_filename)
cmd = ['diff',
'-u', if not filecmp.cmp(reference_filename, output_filename):
'-N',
reference_filename,
output_filename]
try:
self.run_command(cmd)
except ScriptError as err:
# run_command throws an exception on diff (b/c non-zero exit code)
print 'FAIL: %s' % reference_basename print 'FAIL: %s' % reference_basename
print err.output print self.diff(reference_filename, output_filename)
return False return False
if self.verbose: if self.verbose:
...@@ -222,10 +173,7 @@ class BindingsTests(object): ...@@ -222,10 +173,7 @@ class BindingsTests(object):
return True return True
def run_tests(self): def run_tests(self):
# Generate output, immediately dying on failure self.generate_interface_dependencies()
if self.generate_interface_dependencies():
return False
self.idl_compiler = IdlCompilerV8(self.output_directory, self.idl_compiler = IdlCompilerV8(self.output_directory,
EXTENDED_ATTRIBUTES_FILE, EXTENDED_ATTRIBUTES_FILE,
interfaces_info=interfaces_info, interfaces_info=interfaces_info,
...@@ -241,10 +189,9 @@ class BindingsTests(object): ...@@ -241,10 +189,9 @@ class BindingsTests(object):
continue continue
idl_path = os.path.join(test_input_directory, input_filename) idl_path = os.path.join(test_input_directory, input_filename)
if self.generate_from_idl(idl_path): self.generate_from_idl(idl_path)
return False if self.verbose:
if self.reset_results and self.verbose: print 'Compiled: %s' % input_filename
print 'Reset results: %s' % input_filename
self.delete_cache_files() self.delete_cache_files()
...@@ -269,5 +216,10 @@ class BindingsTests(object): ...@@ -269,5 +216,10 @@ class BindingsTests(object):
def run_bindings_tests(reset_results, verbose): def run_bindings_tests(reset_results, verbose):
with ScopedTempFileProvider() as provider: # Generate output into the reference directory if resetting results, or
return BindingsTests(reset_results, verbose, provider).main() # a temp directory if not.
if reset_results:
print 'Resetting results'
return BindingsTests(reference_directory, verbose).main()
with ScopedTempDir() as temp_dir:
return BindingsTests(temp_dir, verbose).main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment