mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
234 lines
9.3 KiB
Python
234 lines
9.3 KiB
Python
# Copyright (C) 2011 Google Inc. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
|
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
from contextlib import contextmanager
|
|
import filecmp
|
|
import fnmatch
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
|
|
from webkitpy.common.system.executive import Executive
|
|
|
|
# engine/ path is needed both to find input IDL files, and to import other
|
|
# Python modules.
|
|
module_path = os.path.dirname(__file__)
|
|
source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir,
|
|
os.pardir, os.pardir, 'engine'))
|
|
sys.path.append(source_path) # for engine/bindings imports
|
|
|
|
import bindings.scripts.compute_interfaces_info_individual
|
|
from bindings.scripts.compute_interfaces_info_individual import compute_info_individual, info_individual
|
|
import bindings.scripts.compute_interfaces_info_overall
|
|
from bindings.scripts.compute_interfaces_info_overall import compute_interfaces_info_overall, interfaces_info
|
|
from bindings.scripts.idl_compiler import IdlCompilerV8
|
|
|
|
|
|
PASS_MESSAGE = 'All tests PASS!'
|
|
FAIL_MESSAGE = """Some tests FAIL!
|
|
To update the reference files, execute:
|
|
run-bindings-tests --reset-results
|
|
|
|
If the failures are not due to your changes, test results may be out of sync;
|
|
please rebaseline them in a separate CL, after checking that tests fail in ToT.
|
|
In CL, please set:
|
|
NOTRY=true
|
|
TBR=(someone in engine/bindings/OWNERS or WATCHLISTS:bindings)
|
|
"""
|
|
|
|
DEPENDENCY_IDL_FILES = frozenset([
|
|
'TestImplements.idl',
|
|
'TestImplements2.idl',
|
|
'TestImplements3.idl',
|
|
'TestPartialInterface.idl',
|
|
'TestPartialInterface2.idl',
|
|
])
|
|
|
|
|
|
test_input_directory = os.path.join(source_path, 'bindings', 'tests', 'idls')
|
|
reference_directory = os.path.join(source_path, 'bindings', 'tests', 'results')
|
|
|
|
|
|
@contextmanager
|
|
def TemporaryDirectory():
|
|
"""Wrapper for tempfile.mkdtemp() so it's usable with 'with' statement.
|
|
|
|
Simple backport of tempfile.TemporaryDirectory from Python 3.2.
|
|
"""
|
|
name = tempfile.mkdtemp()
|
|
try:
|
|
yield name
|
|
finally:
|
|
shutil.rmtree(name)
|
|
|
|
|
|
def generate_interface_dependencies():
|
|
def idl_paths_recursive(directory):
|
|
# This is slow, especially on Windows, due to os.walk making
|
|
# excess stat() calls. Faster versions may appear in Python 3.5 or
|
|
# later:
|
|
# https://github.com/benhoyt/scandir
|
|
# http://bugs.python.org/issue11406
|
|
idl_paths = []
|
|
for dirpath, _, files in os.walk(directory):
|
|
idl_paths.extend(os.path.join(dirpath, filename)
|
|
for filename in fnmatch.filter(files, '*.idl'))
|
|
return idl_paths
|
|
|
|
# We compute interfaces info for *all* IDL files, not just test IDL
|
|
# files, as code generator output depends on inheritance (both ancestor
|
|
# chain and inherited extended attributes), and some real interfaces
|
|
# are special-cased, such as Node.
|
|
#
|
|
# For example, when testing the behavior of interfaces that inherit
|
|
# from Node, we also need to know that these inherit from EventTarget,
|
|
# since this is also special-cased and Node inherits from EventTarget,
|
|
# but this inheritance information requires computing dependencies for
|
|
# the real Node.idl file.
|
|
|
|
# 2-stage computation: individual, then overall
|
|
#
|
|
# Properly should compute separately by component (currently test
|
|
# includes are invalid), but that's brittle (would need to update this file
|
|
# for each new component) and doesn't test the code generator any better
|
|
# than using a single component.
|
|
for idl_filename in idl_paths_recursive(source_path):
|
|
compute_info_individual(idl_filename, 'tests')
|
|
info_individuals = [info_individual()]
|
|
compute_interfaces_info_overall(info_individuals)
|
|
|
|
|
|
def bindings_tests(output_directory, verbose):
|
|
executive = Executive()
|
|
|
|
def diff(filename1, filename2):
|
|
# Python's difflib module is too slow, especially on long output, so
|
|
# run external diff(1) command
|
|
cmd = ['diff',
|
|
'-u', # unified format
|
|
'-N', # treat absent files as empty
|
|
filename1,
|
|
filename2]
|
|
# Return output and don't raise exception, even though diff(1) has
|
|
# non-zero exit if files differ.
|
|
return executive.run_command(cmd, error_handler=lambda x: None)
|
|
|
|
def delete_cache_files():
|
|
# FIXME: Instead of deleting cache files, don't generate them.
|
|
cache_files = [os.path.join(output_directory, output_file)
|
|
for output_file in os.listdir(output_directory)
|
|
if (output_file in ('lextab.py', # PLY lex
|
|
'lextab.pyc',
|
|
'parsetab.pickle') or # PLY yacc
|
|
output_file.endswith('.cache'))] # Jinja
|
|
for cache_file in cache_files:
|
|
os.remove(cache_file)
|
|
|
|
def identical_file(reference_filename, output_filename):
|
|
reference_basename = os.path.basename(reference_filename)
|
|
|
|
if not os.path.isfile(reference_filename):
|
|
print 'Missing reference file!'
|
|
print '(if adding new test, update reference files)'
|
|
print reference_basename
|
|
print
|
|
return False
|
|
|
|
if not filecmp.cmp(reference_filename, output_filename):
|
|
# cmp is much faster than diff, and usual case is "no differance",
|
|
# so only run diff if cmp detects a difference
|
|
print 'FAIL: %s' % reference_basename
|
|
print diff(reference_filename, output_filename)
|
|
return False
|
|
|
|
if verbose:
|
|
print 'PASS: %s' % reference_basename
|
|
return True
|
|
|
|
def identical_output_files():
|
|
file_pairs = [(os.path.join(reference_directory, output_file),
|
|
os.path.join(output_directory, output_file))
|
|
for output_file in os.listdir(output_directory)]
|
|
return all([identical_file(reference_filename, output_filename)
|
|
for (reference_filename, output_filename) in file_pairs])
|
|
|
|
def no_excess_files():
|
|
generated_files = set(os.listdir(output_directory))
|
|
generated_files.add('.svn') # Subversion working copy directory
|
|
excess_files = [output_file
|
|
for output_file in os.listdir(reference_directory)
|
|
if output_file not in generated_files]
|
|
if excess_files:
|
|
print ('Excess reference files! '
|
|
'(probably cruft from renaming or deleting):\n' +
|
|
'\n'.join(excess_files))
|
|
return False
|
|
return True
|
|
|
|
try:
|
|
generate_interface_dependencies()
|
|
idl_compiler = IdlCompilerV8(output_directory,
|
|
interfaces_info=interfaces_info,
|
|
only_if_changed=True)
|
|
|
|
idl_basenames = [filename
|
|
for filename in os.listdir(test_input_directory)
|
|
if (filename.endswith('.idl') and
|
|
# Dependencies aren't built
|
|
# (they are used by the dependent)
|
|
filename not in DEPENDENCY_IDL_FILES)]
|
|
for idl_basename in idl_basenames:
|
|
idl_path = os.path.realpath(
|
|
os.path.join(test_input_directory, idl_basename))
|
|
idl_compiler.compile_file(idl_path)
|
|
definition_name, _ = os.path.splitext(idl_basename)
|
|
if verbose:
|
|
print 'Compiled: %s' % filename
|
|
finally:
|
|
delete_cache_files()
|
|
|
|
# Detect all changes
|
|
passed = identical_output_files()
|
|
passed &= no_excess_files()
|
|
|
|
if passed:
|
|
if verbose:
|
|
print
|
|
print PASS_MESSAGE
|
|
return 0
|
|
print
|
|
print FAIL_MESSAGE
|
|
return 1
|
|
|
|
|
|
def run_bindings_tests(reset_results, verbose):
|
|
# Generate output into the reference directory if resetting results, or
|
|
# a temp directory if not.
|
|
if reset_results:
|
|
print 'Resetting results'
|
|
return bindings_tests(reference_directory, verbose)
|
|
with TemporaryDirectory() as temp_dir:
|
|
return bindings_tests(temp_dir, verbose)
|