diff options
Diffstat (limited to 'chromium/third_party/WebKit/Tools/Scripts/webkitpy')
54 files changed, 1521 insertions, 1053 deletions
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py index 120c088737b..b1409d27ada 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py @@ -23,190 +23,237 @@ # import os -import os.path import shutil -import subprocess -import sys import tempfile from webkitpy.common.checkout.scm.detection import detect_scm_system from webkitpy.common.system.executive import ScriptError - -class ScopedTempFileProvider: +# Python compiler is incomplete; skip IDLs with unimplemented features +SKIP_PYTHON = set([ + 'TestActiveDOMObject.idl', + 'TestCallback.idl', + 'TestCustomAccessors.idl', + 'TestEvent.idl', + 'TestEventConstructor.idl', + 'TestEventTarget.idl', + 'TestException.idl', + 'TestExtendedEvent.idl', + 'TestImplements.idl', + 'TestInterface.idl', + 'TestInterfaceImplementedAs.idl', + 'TestMediaQueryListListener.idl', + 'TestNamedConstructor.idl', + 'TestNode.idl', + 'TestObject.idl', + 'TestOverloadedConstructors.idl', + 'TestPartialInterface.idl', + 'TestSerializedScriptValueInterface.idl', + 'TestTypedArray.idl', + 'TestTypedefs.idl', +]) + +input_directory = os.path.join('bindings', 'tests', 'idls') +reference_directory = os.path.join('bindings', 'tests', 'results') +reference_event_names_filename = os.path.join(reference_directory, 'EventInterfaces.in') + + +class ScopedTempFileProvider(object): def __init__(self): self.files = [] + self.directories = [] def __del__(self): - for f in self.files: - os.remove(f) + for filename in self.files: + os.remove(filename) + for directory in self.directories: + shutil.rmtree(directory) def newtempfile(self): - path = tempfile.mkstemp()[1] + file_handle, path = tempfile.mkstemp() self.files.append(path) + return file_handle, path + + def newtempdir(self): + path = tempfile.mkdtemp() + self.directories.append(path) return path +provider = ScopedTempFileProvider() -class BindingsTests: - def __init__(self, reset_results, executive): +class BindingsTests(object): + def __init__(self, reset_results, test_python, executive): self.reset_results = reset_results + self.test_python = test_python self.executive = executive + _, self.interface_dependencies_filename = provider.newtempfile() + if reset_results: + self.event_names_filename = os.path.join(reference_directory, 'EventInterfaces.in') + else: + _, self.event_names_filename = provider.newtempfile() - def generate_from_idl(self, idl_file, output_directory, interface_dependencies_file): + def run_command(self, cmd): + return self.executive.run_command(cmd) + + def generate_from_idl_pl(self, idl_file, output_directory): cmd = ['perl', '-w', '-Ibindings/scripts', '-Icore/scripts', '-I../../JSON/out/lib/perl5', - 'bindings/scripts/deprecated_generate_bindings.pl', + 'bindings/scripts/generate_bindings.pl', # idl include directories (path relative to generate-bindings.pl) '--include', '.', '--outputDir', output_directory, - '--interfaceDependenciesFile', interface_dependencies_file, + '--interfaceDependenciesFile', self.interface_dependencies_filename, '--idlAttributesFile', 'bindings/scripts/IDLAttributes.txt', idl_file] - - exit_code = 0 try: - output = self.executive.run_command(cmd) - if output: - print output + output = self.run_command(cmd) except ScriptError, e: print e.output - exit_code = e.exit_code - return exit_code - - def generate_interface_dependencies(self, input_directory, interface_dependencies_file, window_constructors_file, workerglobalscope_constructors_file, sharedworkerglobalscope_constructors_file, dedicatedworkerglobalscope_constructors_file, event_names_file): - idl_files_list = tempfile.mkstemp() - for input_file in os.listdir(input_directory): - (name, extension) = os.path.splitext(input_file) - if extension != '.idl': - continue - os.write(idl_files_list[0], os.path.join(input_directory, input_file) + "\n") - os.close(idl_files_list[0]) + return e.exit_code + if output: + print output + return 0 + def generate_from_idl_py(self, idl_file, output_directory): + cmd = ['python', + 'bindings/scripts/unstable/idl_compiler.py', + '--output-dir', output_directory, + '--idl-attributes-file', 'bindings/scripts/IDLAttributes.txt', + '--include', '.', + '--interface-dependencies-file', + self.interface_dependencies_filename, + idl_file] + try: + output = self.run_command(cmd) + except ScriptError, e: + print e.output + return e.exit_code + if output: + print output + return 0 + + def generate_interface_dependencies(self): + idl_files_list_file, idl_files_list_filename = provider.newtempfile() + idl_paths = [os.path.join(input_directory, input_file) + for input_file in os.listdir(input_directory) + if input_file.endswith('.idl')] + idl_files_list_contents = ''.join(idl_path + '\n' + for idl_path in idl_paths) + os.write(idl_files_list_file, idl_files_list_contents) + + # Dummy files, required by compute_dependencies but not checked + _, window_constructors_file = provider.newtempfile() + _, workerglobalscope_constructors_file = provider.newtempfile() + _, sharedworkerglobalscope_constructors_file = provider.newtempfile() + _, dedicatedworkerglobalscope_constructors_file = provider.newtempfile() cmd = ['python', 'bindings/scripts/compute_dependencies.py', - '--idl-files-list', idl_files_list[1], - '--interface-dependencies-file', interface_dependencies_file, + '--idl-files-list', idl_files_list_filename, + '--interface-dependencies-file', self.interface_dependencies_filename, '--window-constructors-file', window_constructors_file, '--workerglobalscope-constructors-file', workerglobalscope_constructors_file, '--sharedworkerglobalscope-constructors-file', sharedworkerglobalscope_constructors_file, '--dedicatedworkerglobalscope-constructors-file', dedicatedworkerglobalscope_constructors_file, - '--event-names-file', event_names_file, + '--event-names-file', self.event_names_filename, '--write-file-only-if-changed', '0'] if self.reset_results: - print "Reset results: EventInterfaces.in" - - exit_code = 0 + print 'Reset results: EventInterfaces.in' try: - output = self.executive.run_command(cmd) - if output: - print output + output = self.run_command(cmd) except ScriptError, e: print e.output - exit_code = e.exit_code - os.remove(idl_files_list[1]) - return exit_code + return e.exit_code + if output: + print output + return 0 - def detect_changes_in_file(self, work_file, reference_file): + def identical_file(self, reference_filename, work_filename): cmd = ['diff', '-u', '-N', - os.path.join(reference_file), - os.path.join(work_file)] - - changes_found = False - exit_code = 0 + reference_filename, + work_filename] try: - output = self.executive.run_command(cmd) + output = self.run_command(cmd) except ScriptError, e: - output = e.output - exit_code = e.exit_code + print e.output + return False - if exit_code or output: - print 'FAIL: %s' % (os.path.basename(reference_file)) + reference_basename = os.path.basename(reference_filename) + if output: + print 'FAIL: %s' % reference_basename print output - changes_found = True - else: - print 'PASS: %s' % (os.path.basename(reference_file)) - - return changes_found - - - def detect_changes(self, work_directory, reference_directory): - changes_found = False - for output_file in os.listdir(work_directory): - if self.detect_changes_in_file(os.path.join(reference_directory, output_file), os.path.join(work_directory, output_file)): - changes_found = True - return changes_found - - def run_tests(self, input_directory, reference_directory, interface_dependencies_file, event_names_file): - work_directory = reference_directory - - passed = True - - if not self.reset_results and self.detect_changes_in_file(event_names_file, os.path.join(reference_directory, 'EventInterfaces.in')): - passed = False - - for input_file in os.listdir(input_directory): - (name, extension) = os.path.splitext(input_file) - if extension != '.idl': - continue - # Generate output into the work directory (either the given one or a - # temp one if not reset_results is performed) - if not self.reset_results: - work_directory = tempfile.mkdtemp() - - if self.generate_from_idl(os.path.join(input_directory, input_file), - work_directory, - interface_dependencies_file): - passed = False - + return False + print 'PASS: %s' % reference_basename + return True + + def identical_output_directory(self, work_directory): + file_pairs = [(os.path.join(reference_directory, output_file), + os.path.join(work_directory, output_file)) + for output_file in os.listdir(work_directory) + # FIXME: add option to compiler to not generate tables + if output_file != 'parsetab.py'] + return all([self.identical_file(reference_filename, work_filename) + for (reference_filename, work_filename) in file_pairs]) + + def run_tests(self): + def generate_and_check_output_pl(idl_filename): + # Generate output into the reference directory if resetting + # results, or a temp directory if not. if self.reset_results: - print "Reset results: %s" % (input_file) - continue - + work_directory = reference_directory + else: + work_directory = provider.newtempdir() + idl_path = os.path.join(input_directory, idl_filename) + if self.generate_from_idl_pl(idl_path, work_directory): + return False + if self.reset_results: + print 'Reset results: %s' % input_file + return True + return self.identical_output_directory(work_directory) + + def generate_and_check_output_py(idl_filename): + if idl_filename in SKIP_PYTHON: + print 'SKIP: %s' % idl_filename + return True + work_directory = provider.newtempdir() + idl_path = os.path.join(input_directory, idl_filename) + if self.generate_from_idl_py(idl_path, work_directory): + return False # Detect changes - if self.detect_changes(work_directory, reference_directory): - passed = False - shutil.rmtree(work_directory) + return self.identical_output_directory(work_directory) + if self.reset_results: + passed = True + else: + passed = self.identical_file(reference_event_names_filename, + self.event_names_filename) + passed &= all([generate_and_check_output_pl(input_file) + for input_file in os.listdir(input_directory) + if input_file.endswith('.idl')]) + print + if self.test_python: + print 'Python:' + passed &= all([generate_and_check_output_py(input_file) + for input_file in os.listdir(input_directory) + if input_file.endswith('.idl')]) return passed def main(self): current_scm = detect_scm_system(os.curdir) os.chdir(os.path.join(current_scm.checkout_root, 'Source')) - all_tests_passed = True - - provider = ScopedTempFileProvider() - - input_directory = os.path.join('bindings', 'tests', 'idls') - reference_directory = os.path.join('bindings', 'tests', 'results') - - interface_dependencies_file = provider.newtempfile() - window_constructors_file = provider.newtempfile() - workerglobalscope_constructors_file = provider.newtempfile() - sharedworkerglobalscope_constructors_file = provider.newtempfile() - dedicatedworkerglobalscope_constructors_file = provider.newtempfile() - - if self.reset_results: - event_names_file = os.path.join(reference_directory, 'EventInterfaces.in') - else: - event_names_file = provider.newtempfile() - - if self.generate_interface_dependencies(input_directory, interface_dependencies_file, window_constructors_file, workerglobalscope_constructors_file, sharedworkerglobalscope_constructors_file, dedicatedworkerglobalscope_constructors_file, event_names_file): + if self.generate_interface_dependencies(): print 'Failed to generate interface dependencies file.' return -1 - if not self.run_tests(input_directory, reference_directory, interface_dependencies_file, event_names_file): - all_tests_passed = False - - print '' + all_tests_passed = self.run_tests() + print if all_tests_passed: print 'All tests PASS!' return 0 - else: - print 'Some tests FAIL! (To update the reference files, execute "run-bindings-tests --reset-results")' - return -1 + print 'Some tests FAIL! (To update the reference files, execute "run-bindings-tests --reset-results")' + return -1 diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/urls.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/urls.py index 31575c1df2b..331bb06422e 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/urls.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/urls.py @@ -37,8 +37,21 @@ def view_revision_url(revision_number): return "http://trac.webkit.org/changeset/%s" % revision_number +def chromium_results_url_base(): + return 'https://storage.googleapis.com/chromium-layout-test-archives' + + +def chromium_results_url_base_for_builder(builder_name): + return '%s/%s' % (chromium_results_url_base(), re.sub('[ .()]', '_', builder_name)) + + def chromium_results_zip_url(builder_name): - return 'http://build.chromium.org/f/chromium/layout_test_results/%s/layout-test-results.zip' % builder_name + return chromium_results_url_base_for_builder(builder_name) + '/results/layout-test-results.zip' + + +def chromium_accumulated_results_url_base_for_builder(builder_name): + return chromium_results_url_base_for_builder(builder_name) + "/results/layout-test-results" + chromium_lkgr_url = "http://chromium-status.appspot.com/lkgr" contribution_guidelines = "http://webkit.org/coding/contributing.html" diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot.py index 5030bba4802..ee710110a81 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot.py @@ -36,10 +36,10 @@ class ChromiumBuilder(Builder): # The build.chromium.org builders store their results in a different # location than the build.webkit.org builders. def results_url(self): - return "http://build.chromium.org/f/chromium/layout_test_results/%s" % builder_path_from_name(self._name) + return config_urls.chromium_results_url_base_for_builder(self._name) def accumulated_results_url(self): - return self.results_url() + "/results/layout-test-results" + return config_urls.chromium_accumulated_results_url_base_for_builder(self._name) class ChromiumBuildBot(BuildBot): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot_unittest.py new file mode 100644 index 00000000000..723386dd0e4 --- /dev/null +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot_unittest.py @@ -0,0 +1,43 @@ +# Copyright (C) 2013 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: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. + +import webkitpy.thirdparty.unittest2 as unittest + +from webkitpy.common.net.buildbot.chromiumbuildbot import ChromiumBuildBot + + +class ChromiumBuilderTest(unittest.TestCase): + def test_results_url(self): + builder = ChromiumBuildBot().builder_with_name('WebKit Mac10.8 (dbg)') + self.assertEqual(builder.results_url(), + 'https://storage.googleapis.com/chromium-layout-test-archives/WebKit_Mac10_8__dbg_') + + def test_accumulated_results_url(self): + builder = ChromiumBuildBot().builder_with_name('WebKit Mac10.8 (dbg)') + self.assertEqual(builder.accumulated_results_url(), + 'https://storage.googleapis.com/chromium-layout-test-archives/WebKit_Mac10_8__dbg_/results/layout-test-results') diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py index b3c4367c0b5..2597e0a1401 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py @@ -103,6 +103,9 @@ class LayoutTestResults(object): def run_was_interrupted(self): return self._results["interrupted"] + def builder_name(self): + return self._results["builder_name"] + def blink_revision(self): return int(self._results["blink_revision"]) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py index 94121da6293..79f5b96c637 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py @@ -397,7 +397,7 @@ class Executive(object): error_handler=None, return_exit_code=False, return_stderr=True, - decode_output=True): + decode_output=True, debug_logging=True): """Popen wrapper for convenience and to work around python bugs.""" assert(isinstance(args, list) or isinstance(args, tuple)) start_time = time.time() @@ -422,7 +422,8 @@ class Executive(object): # http://bugs.python.org/issue1731717 exit_code = process.wait() - _log.debug('"%s" took %.2fs' % (self.command_for_printing(args), time.time() - start_time)) + if debug_logging: + _log.debug('"%s" took %.2fs' % (self.command_for_printing(args), time.time() - start_time)) if return_exit_code: return exit_code diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py index 6a3d018a531..a0f08b736fc 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py @@ -98,7 +98,8 @@ class MockExecutive(object): return_exit_code=False, return_stderr=True, decode_output=False, - env=None): + env=None, + debug_logging=False): self.calls.append(args) @@ -147,6 +148,8 @@ class MockExecutive(object): _log.info('Mock call: %s' % args) def run_in_parallel(self, commands): + assert len(commands) + num_previous_calls = len(self.calls) command_outputs = [] for cmd_line, cwd in commands: @@ -177,7 +180,8 @@ class MockExecutive2(MockExecutive): return_exit_code=False, return_stderr=True, decode_output=False, - env=None): + env=None, + debug_logging=False): self.calls.append(args) assert(isinstance(args, list) or isinstance(args, tuple)) if self._exception: diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py index 582e1996f98..b2c602bed35 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py @@ -72,6 +72,13 @@ class PlatformInfo(object): def is_freebsd(self): return self.os_name == 'freebsd' + def is_highdpi(self): + if self.is_mac(): + output = self._executive.run_command(['system_profiler', 'SPDisplaysDataType'], error_handler=self._executive.ignore_error) + if output and 'Retina: Yes' in output: + return True + return False + def display_name(self): # platform.platform() returns Darwin information for Mac, which is just confusing. if self.is_mac(): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py index bc72810cfc6..1ba00190b9f 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py @@ -28,9 +28,10 @@ class MockPlatformInfo(object): - def __init__(self, os_name='mac', os_version='snowleopard'): + def __init__(self, os_name='mac', os_version='snowleopard', is_highdpi=False): self.os_name = os_name self.os_version = os_version + self._is_highdpi = is_highdpi def is_mac(self): return self.os_name == 'mac' @@ -41,6 +42,9 @@ class MockPlatformInfo(object): def is_win(self): return self.os_name == 'win' + def is_highdpi(self): + return self._is_highdpi + def is_cygwin(self): return self.os_name == 'cygwin' diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost.py index 3b4439ee478..52b88d6fcc1 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost.py @@ -43,3 +43,12 @@ class SystemHost(object): def copy_current_environment(self): return environment.Environment(os.environ.copy()) + + def print_(self, *args, **kwargs): + sep = kwargs.get('sep', ' ') + end = kwargs.get('end', '\n') + file = kwargs.get('file', None) + stderr = kwargs.get('stderr', False) + + file = file or (sys.stderr if stderr else sys.stdout) + file.write(sep.join([str(arg) for arg in args]) + end) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost_mock.py index 4667b08b9ab..b39b4ea0b76 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost_mock.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost_mock.py @@ -1,30 +1,31 @@ - # 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: - # - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * 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. - # * Neither the name of Google Inc. nor the names of its - # contributors may be used to endorse or promote products derived from - # this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # "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 THE COPYRIGHT - # OWNER 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. +# 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: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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 StringIO import StringIO from webkitpy.common.system.environment import Environment from webkitpy.common.system.executive_mock import MockExecutive @@ -48,5 +49,17 @@ class MockSystemHost(object): # FIXME: Should this take pointers to the filesystem and the executive? self.workspace = MockWorkspace() + self.stdout = StringIO() + self.stderr = StringIO() + def copy_current_environment(self): return Environment({"MOCK_ENVIRON_COPY": '1'}) + + def print_(self, *args, **kwargs): + sep = kwargs.get('sep', ' ') + end = kwargs.get('end', '\n') + file = kwargs.get('file', None) + stderr = kwargs.get('stderr', False) + + file = file or (self.stderr if stderr else self.stdout) + file.write(sep.join([str(arg) for arg in args]) + end) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py index 78c8d2e477a..d94bf6d4118 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py @@ -89,7 +89,7 @@ class WebKitFinder(object): def _check_upward_for_depot_tools(self): fs = self._filesystem prev_dir = '' - current_dir = fs.dirname(self._webkit_base()) + current_dir = fs.dirname(self._webkit_base) while current_dir != prev_dir: if fs.exists(fs.join(current_dir, 'depot_tools', 'pylint.py')): return fs.join(current_dir, 'depot_tools') diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py index dbacc161724..d4a269a2846 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py @@ -120,7 +120,7 @@ class LayoutTestRunner(object): except KeyboardInterrupt: self._printer.flush() self._printer.writeln('Interrupted, exiting ...') - raise + run_results.keyboard_interrupted = True except Exception, e: _log.debug('%s("%s") raised, exiting' % (e.__class__.__name__, str(e))) raise @@ -252,7 +252,13 @@ class Worker(object): start = time.time() self._caller.post('started_test', test_input, test_timeout_sec) - result = self._run_test_with_timeout(test_input, test_timeout_sec, stop_when_done) + if self._driver and self._driver.has_crashed(): + self._kill_driver() + if not self._driver: + self._driver = self._port.create_driver(self._worker_number) + result = single_test_runner.run_single_test(self._port, self._options, self._results_directory, + self._name, self._driver, test_input, stop_when_done) + result.shard_name = shard_name result.worker_name = self._name result.total_run_time = time.time() - start @@ -275,13 +281,9 @@ class Worker(object): # # Note that we need to convert the test timeout from a # string value in milliseconds to a float for Python. - driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0 - if not self._options.run_singly: - return driver_timeout_sec - thread_padding_sec = 1.0 - thread_timeout_sec = driver_timeout_sec + thread_padding_sec - return thread_timeout_sec + # FIXME: Can we just return the test_input.timeout now? + driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0 def _kill_driver(self): # Be careful about how and when we kill the driver; if driver.stop() @@ -292,10 +294,6 @@ class Worker(object): _log.debug("%s killing driver" % self._name) driver.stop() - def _run_test_with_timeout(self, test_input, timeout, stop_when_done): - if self._options.run_singly: - return self._run_test_in_another_thread(test_input, timeout, stop_when_done) - return self._run_test_in_this_thread(test_input, stop_when_done) def _clean_up_after_test(self, test_input, result): test_name = test_input.test_name @@ -316,72 +314,6 @@ class Worker(object): else: _log.debug("%s %s passed" % (self._name, test_name)) - def _run_test_in_another_thread(self, test_input, thread_timeout_sec, stop_when_done): - """Run a test in a separate thread, enforcing a hard time limit. - - Since we can only detect the termination of a thread, not any internal - state or progress, we can only run per-test timeouts when running test - files singly. - - Args: - test_input: Object containing the test filename and timeout - thread_timeout_sec: time to wait before killing the driver process. - Returns: - A TestResult - """ - worker = self - - driver = self._port.create_driver(self._worker_number) - - class SingleTestThread(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.result = None - - def run(self): - self.result = worker._run_single_test(driver, test_input, stop_when_done) - - thread = SingleTestThread() - thread.start() - thread.join(thread_timeout_sec) - result = thread.result - failures = [] - if thread.isAlive(): - # If join() returned with the thread still running, the - # driver is completely hung and there's nothing - # more we can do with it. We have to kill all the - # drivers to free it up. If we're running more than - # one driver thread, we'll end up killing the other - # drivers too, introducing spurious crashes. We accept - # that tradeoff in order to avoid losing the rest of this - # thread's results. - _log.error('Test thread hung: killing all drivers') - failures = [test_failures.FailureTimeout()] - - driver.stop() - - if not result: - result = test_results.TestResult(test_input.test_name, failures=failures, test_run_time=0) - return result - - def _run_test_in_this_thread(self, test_input, stop_when_done): - """Run a single test file using a shared driver process. - - Args: - test_input: Object containing the test filename, uri and timeout - - Returns: a TestResult object. - """ - if self._driver and self._driver.has_crashed(): - self._kill_driver() - if not self._driver: - self._driver = self._port.create_driver(self._worker_number) - return self._run_single_test(self._driver, test_input, stop_when_done) - - def _run_single_test(self, driver, test_input, stop_when_done): - return single_test_runner.run_single_test(self._port, self._options, self._results_directory, - self._name, driver, test_input, stop_when_done) - class TestShard(object): """A test shard is a named list of TestInputs.""" diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py index 5fef70088e0..4cd1f5b4fd7 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py @@ -50,6 +50,7 @@ from webkitpy.layout_tests.models import test_expectations from webkitpy.layout_tests.models import test_failures from webkitpy.layout_tests.models import test_run_results from webkitpy.layout_tests.models.test_input import TestInput +from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS _log = logging.getLogger(__name__) @@ -150,7 +151,7 @@ class Manager(object): def _set_up_run(self, test_names): self._printer.write_update("Checking build ...") - if not self._port.check_build(self.needs_servers(test_names)): + if self._options.build and not self._port.check_build(self.needs_servers(test_names), self._printer): _log.error("Build check failed") return False @@ -213,8 +214,11 @@ class Manager(object): initial_results = self._run_tests(tests_to_run, tests_to_skip, self._options.repeat_each, self._options.iterations, int(self._options.child_processes), retrying=False) + # Don't retry failures when interrupted by user or failures limit exception. + should_retry_failures = should_retry_failures and not (initial_results.interrupted or initial_results.keyboard_interrupted) + tests_to_retry = self._tests_to_retry(initial_results) - if should_retry_failures and tests_to_retry and not initial_results.interrupted: + if should_retry_failures and tests_to_retry: enabled_pixel_tests_in_retry = self._force_pixel_tests_if_needed() _log.info('') @@ -249,10 +253,12 @@ class Manager(object): results_path = self._filesystem.join(self._results_directory, "results.html") self._copy_results_html_file(results_path) - if self._options.show_results and (exit_code or (self._options.full_results_html and initial_results.total_failures)): - self._port.show_results_html_file(results_path) - - self._printer.print_results(time.time() - start_time, initial_results, summarized_failing_results) + if initial_results.keyboard_interrupted: + exit_code = INTERRUPTED_EXIT_STATUS + else: + if self._options.show_results and (exit_code or (self._options.full_results_html and initial_results.total_failures)): + self._port.show_results_html_file(results_path) + self._printer.print_results(time.time() - start_time, initial_results, summarized_failing_results) return test_run_results.RunDetails(exit_code, summarized_full_results, summarized_failing_results, initial_results, retry_results, enabled_pixel_tests_in_retry) def _run_tests(self, tests_to_run, tests_to_skip, repeat_each, iterations, num_workers, retrying): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py index d87e5964df9..115428ecf9b 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py @@ -283,6 +283,12 @@ class SingleTestRunner(object): reference_output = None test_result = None + # If the test crashed, or timed out, there's no point in running the reference at all. + # This can save a lot of execution time if we have a lot of crashes or timeouts. + if test_output.crash or test_output.timeout: + expected_driver_output = DriverOutput(text=None, image=None, image_hash=None, audio=None) + return self._compare_output(expected_driver_output, test_output) + # A reftest can have multiple match references and multiple mismatch references; # the test fails if any mismatch matches and all of the matches don't match. # To minimize the number of references we have to check, we run all of the mismatches first, @@ -303,7 +309,10 @@ class SingleTestRunner(object): assert(reference_output) test_result_writer.write_test_result(self._filesystem, self._port, self._results_directory, self._test_name, test_output, reference_output, test_result.failures) - reftest_type = set([reference_file[0] for reference_file in self._reference_files]) + + # FIXME: We don't really deal with a mix of reftest types properly. We pass in a set() to reftest_type + # and only really handle the first of the references in the result. + reftest_type = list(set([reference_file[0] for reference_file in self._reference_files])) return TestResult(self._test_name, test_result.failures, total_test_time + test_result.test_run_time, test_result.has_stderr, reftest_type=reftest_type, pid=test_result.pid, references=reference_test_names) def _compare_output_with_reference(self, reference_driver_output, actual_driver_output, reference_filename, mismatch): @@ -325,6 +334,8 @@ class SingleTestRunner(object): diff, err_str = self._port.diff_image(reference_driver_output.image, actual_driver_output.image) if not diff: failures.append(test_failures.FailureReftestMismatchDidNotOccur(reference_filename)) + elif err_str: + _log.error(err_str) else: _log.warning(" %s -> ref test hashes matched but diff failed" % self._test_name) @@ -332,6 +343,8 @@ class SingleTestRunner(object): diff, err_str = self._port.diff_image(reference_driver_output.image, actual_driver_output.image) if diff: failures.append(test_failures.FailureReftestMismatch(reference_filename)) + elif err_str: + _log.error(err_str) else: _log.warning(" %s -> ref test hashes didn't match but diff passed" % self._test_name) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py index 67ffe8616f6..310d901a74d 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py @@ -203,7 +203,7 @@ class TestExpectationParser(object): # FIXME: Update the original specifiers and remove this once the old syntax is gone. _configuration_tokens_list = [ - 'Mac', 'SnowLeopard', 'Lion', 'MountainLion', + 'Mac', 'SnowLeopard', 'Lion', 'Retina', 'MountainLion', 'Win', 'XP', 'Win7', 'Linux', 'Android', @@ -922,22 +922,21 @@ class TestExpectations(object): if not expectations_dict: expectations_dict = port.expectations_dict() - expectations_dict_index = 0 - # Populate generic expectations (always enabled). - if port.path_to_generic_test_expectations_file() in expectations_dict: - expectations = self._parser.parse(expectations_dict.keys()[expectations_dict_index], expectations_dict.values()[expectations_dict_index]) - self._add_expectations(expectations, self._model) - self._expectations += expectations - expectations_dict_index += 1 - - # Populate override expectations (if enabled by include_overrides). - while len(expectations_dict) > expectations_dict_index and include_overrides: - expectations = self._parser.parse(expectations_dict.keys()[expectations_dict_index], expectations_dict.values()[expectations_dict_index]) - model = TestExpectationsModel(self._shorten_filename) - self._add_expectations(expectations, model) - self._expectations += expectations - expectations_dict_index += 1 - self._model.merge_model(model) + # Always parse the generic expectations (the generic file is required + # to be the first one in the expectations_dict, which must be an OrderedDict). + generic_path, generic_exps = expectations_dict.items()[0] + expectations = self._parser.parse(generic_path, generic_exps) + self._add_expectations(expectations, self._model) + self._expectations += expectations + + # Now add the overrides if so requested. + if include_overrides: + for path, contents in expectations_dict.items()[1:]: + expectations = self._parser.parse(path, contents) + model = TestExpectationsModel(self._shorten_filename) + self._add_expectations(expectations, model) + self._expectations += expectations + self._model.merge_model(model) # FIXME: move ignore_tests into port.skipped_layout_tests() self.add_extra_skipped_tests(port.skipped_layout_tests(tests).union(set(port.get_option('ignore_tests', [])))) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py index 3579d23bf47..6e905d99020 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py @@ -28,6 +28,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import logging +import signal import time from webkitpy.layout_tests.models import test_expectations @@ -36,6 +37,7 @@ from webkitpy.layout_tests.models import test_failures _log = logging.getLogger(__name__) +INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128 class TestRunResults(object): def __init__(self, expectations, num_tests): @@ -62,6 +64,7 @@ class TestRunResults(object): self.tests_by_timeline[timeline] = expectations.get_tests_with_timeline(timeline) self.slow_tests = set() self.interrupted = False + self.keyboard_interrupted = False self.run_time = 0 # The wall clock time spent running the tests (layout_test_runner.run()). def add(self, test_result, expected, test_is_slow): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py index fb07090df6e..4cb097ef14a 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py @@ -47,7 +47,7 @@ def get_result(test_name, result_type=test_expectations.PASS, run_time=0): def run_results(port, extra_skipped_tests=[]): - tests = ['passes/text.html', 'failures/expected/timeout.html', 'failures/expected/crash.html', 'failures/expected/hang.html', + tests = ['passes/text.html', 'failures/expected/timeout.html', 'failures/expected/crash.html', 'failures/expected/keyboard.html', 'failures/expected/audio.html', 'passes/skipped/skip.html'] expectations = test_expectations.TestExpectations(port, tests) if extra_skipped_tests: @@ -79,8 +79,8 @@ def summarized_results(port, expected, passing, flaky, only_include_failing=Fals initial_results.add(get_result('failures/expected/timeout.html', test_expectations.CRASH, run_time=0.05), expected, test_is_slow) initial_results.add(get_result('failures/expected/crash.html', test_expectations.TIMEOUT), expected, test_is_slow) - # we only list hang.html here, since normally this is WontFix - initial_results.add(get_result('failures/expected/hang.html', test_expectations.SKIP), expected, test_is_slow) + # we only list keyboard.html here, since normally this is WontFix + initial_results.add(get_result('failures/expected/keyboard.html', test_expectations.SKIP), expected, test_is_slow) if flaky: retry_results = run_results(port, extra_skipped_tests) @@ -152,7 +152,7 @@ class SummarizedResultsTest(unittest.TestCase): def test_summarized_results_wontfix(self): self.port._options.builder_name = 'dummy builder' summary = summarized_results(self.port, expected=False, passing=False, flaky=False) - self.assertEquals(summary['tests']['failures']['expected']['hang.html']['expected'], 'WONTFIX') + self.assertEquals(summary['tests']['failures']['expected']['keyboard.html']['expected'], 'WONTFIX') self.assertTrue(summary['tests']['passes']['text.html']['is_unexpected']) def test_summarized_results_expected_pass(self): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py index 7ffc06cf10b..5837ac20c0b 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py @@ -35,6 +35,8 @@ import subprocess import threading import time +from multiprocessing.pool import ThreadPool + from webkitpy.layout_tests.port import chromium from webkitpy.layout_tests.port import linux from webkitpy.layout_tests.port import driver @@ -182,9 +184,10 @@ class AndroidCommands(object): _adb_command_path = None _adb_command_path_options = [] - def __init__(self, executive, device_serial): + def __init__(self, executive, device_serial, debug_logging): self._executive = executive self._device_serial = device_serial + self._debug_logging = debug_logging # Local public methods. @@ -220,8 +223,8 @@ class AndroidCommands(object): else: error_handler = None - result = self._executive.run_command(self.adb_command() + command, - error_handler=error_handler) + result = self._executive.run_command(self.adb_command() + command, error_handler=error_handler, + debug_logging=self._debug_logging) # We limit the length to avoid outputting too verbose commands, such as "adb logcat". self._log_debug('Run adb result: ' + result[:80]) @@ -231,14 +234,14 @@ class AndroidCommands(object): return self._device_serial def adb_command(self): - return [AndroidCommands.adb_command_path(self._executive), '-s', self._device_serial] + return [AndroidCommands.adb_command_path(self._executive, self._debug_logging), '-s', self._device_serial] @staticmethod def set_adb_command_path_options(paths): AndroidCommands._adb_command_path_options = paths @staticmethod - def adb_command_path(executive): + def adb_command_path(executive, debug_logging): if AndroidCommands._adb_command_path: return AndroidCommands._adb_command_path @@ -247,7 +250,7 @@ class AndroidCommands(object): command_path = None command_version = None for path_option in AndroidCommands._adb_command_path_options: - path_version = AndroidCommands._determine_adb_version(path_option, executive) + path_version = AndroidCommands._determine_adb_version(path_option, executive, debug_logging) if not path_version: continue if command_version != None and path_version < command_version: @@ -266,14 +269,19 @@ class AndroidCommands(object): def _log_error(self, message): _log.error('[%s] %s' % (self._device_serial, message)) + def _log_info(self, message): + _log.info('[%s] %s' % (self._device_serial, message)) + def _log_debug(self, message): - _log.debug('[%s] %s' % (self._device_serial, message)) + if self._debug_logging: + _log.debug('[%s] %s' % (self._device_serial, message)) @staticmethod - def _determine_adb_version(adb_command_path, executive): + def _determine_adb_version(adb_command_path, executive, debug_logging): re_version = re.compile('^.*version ([\d\.]+)$') try: - output = executive.run_command([adb_command_path, 'version'], error_handler=executive.ignore_error) + output = executive.run_command([adb_command_path, 'version'], error_handler=executive.ignore_error, + debug_logging=debug_logging) except OSError: return None @@ -291,17 +299,21 @@ class AndroidDevices(object): # to participate in running the layout tests. MINIMUM_BATTERY_PERCENTAGE = 30 - def __init__(self, executive, default_device=None): + def __init__(self, executive, default_device=None, debug_logging=False): self._usable_devices = [] self._default_device = default_device self._prepared_devices = [] + self._debug_logging = debug_logging + + def prepared_devices(self): + return self._prepared_devices def usable_devices(self, executive): if self._usable_devices: return self._usable_devices if self._default_device: - self._usable_devices = [AndroidCommands(executive, self._default_device)] + self._usable_devices = [AndroidCommands(executive, self._default_device, self._debug_logging)] return self._usable_devices # Example "adb devices" command output: @@ -309,22 +321,27 @@ class AndroidDevices(object): # 0123456789ABCDEF device re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE) - result = executive.run_command([AndroidCommands.adb_command_path(executive), 'devices'], - error_handler=executive.ignore_error) + result = executive.run_command([AndroidCommands.adb_command_path(executive, debug_logging=self._debug_logging), 'devices'], + error_handler=executive.ignore_error, debug_logging=self._debug_logging) devices = re_device.findall(result) if not devices: raise AssertionError('Unable to find attached Android devices. ADB output: %s' % result) - for device_serial in devices: - commands = AndroidCommands(executive, device_serial) + for device_serial in sorted(devices): + commands = AndroidCommands(executive, device_serial, self._debug_logging) if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM_BATTERY_PERCENTAGE: + _log.warning('Device with serial "%s" skipped because it has less than %d percent battery.' + % (commands.get_serial(), AndroidDevices.MINIMUM_BATTERY_PERCENTAGE)) + continue + + if not self._is_device_screen_on(commands): + _log.warning('Device with serial "%s" skipped because the screen must be on.' % commands.get_serial()) continue self._usable_devices.append(commands) if not self._usable_devices: - raise AssertionError('No devices attached with more than %d percent battery.' % - AndroidDevices.MINIMUM_BATTERY_PERCENTAGE) + raise AssertionError('No usable devices are available for running layout tests.') return self._usable_devices @@ -350,6 +367,10 @@ class AndroidDevices(object): return int(re.findall('level: (\d+)', battery_status)[0]) + def _is_device_screen_on(self, commands): + power_status = commands.run(['shell', 'dumpsys', 'power']) + return 'mScreenOn=true' in power_status or 'mScreenOn=SCREEN_ON_BIT' in power_status + class AndroidPort(chromium.ChromiumPort): port_name = 'android' @@ -383,12 +404,20 @@ class AndroidPort(chromium.ChromiumPort): if hasattr(self._options, 'adb_device') and len(self._options.adb_device): default_device = self._options.adb_device - self._devices = AndroidDevices(self._executive, default_device) + self._debug_logging = self.get_option('android_logging') + self._devices = AndroidDevices(self._executive, default_device, self._debug_logging) # Tell AndroidCommands where to search for the "adb" command. AndroidCommands.set_adb_command_path_options(['adb', self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')]) + prepared_devices = self.get_option('prepared_devices', []) + for serial in prepared_devices: + self._devices.set_device_prepared(serial) + + def default_smoke_test_only(self): + return True + # Local public methods. def path_to_forwarder(self): return self._build_path('forwarder') @@ -399,10 +428,6 @@ class AndroidPort(chromium.ChromiumPort): def path_to_md5sum_host(self): return self._build_path(MD5SUM_HOST_FILE_NAME) - # Overridden public methods. - def buildbot_archives_baselines(self): - return False - def additional_drt_flag(self): return self._driver_details.additional_command_line_flags() @@ -429,11 +454,15 @@ class AndroidPort(chromium.ChromiumPort): def check_wdiff(self, logging=True): return self._host_port.check_wdiff(logging) - def check_build(self, needs_http): - result = super(AndroidPort, self).check_build(needs_http) + def check_build(self, needs_http, printer): + result = super(AndroidPort, self).check_build(needs_http, printer) result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility') and result result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result + if result: + # FIXME: We should figure out how to handle failures here better. + self._check_devices(printer) + if not result: _log.error('For complete Android build requirements, please see:') _log.error('') @@ -441,6 +470,63 @@ class AndroidPort(chromium.ChromiumPort): return result + def _check_devices(self, printer): + # Printer objects aren't threadsafe, so we need to protect calls to them. + lock = threading.Lock() + pool = None + + # Push the executables and other files to the devices; doing this now + # means we can do this in parallel in the manager process and not mix + # this in with starting and stopping workers. + def setup_device(worker_number): + d = self.create_driver(worker_number) + serial = d._android_commands.get_serial() + + def log_safely(msg, throttled=True): + if throttled: + callback = printer.write_throttled_update + else: + callback = printer.write_update + lock.acquire() + try: + callback("[%s] %s" % (serial, msg)) + finally: + lock.release() + + log_safely("preparing device", throttled=False) + try: + d._setup_test(log_safely) + except KeyboardInterrupt: + if pool: + pool.terminate() + log_safely("device prepared", throttled=False) + + # FIXME: It would be nice if we knew how many workers we needed. + num_workers = self.default_child_processes() + num_child_processes = self.get_option('child_processes') + if num_child_processes: + num_workers = min(num_workers, num_child_processes) + if num_workers > 1: + pool = ThreadPool(num_workers) + try: + pool.map(setup_device, range(num_workers)) + except KeyboardInterrupt: + pool.terminate() + raise + else: + setup_device(0) + + def setup_test_run(self): + super(AndroidPort, self).setup_test_run() + + # By setting this on the options object, we can propagate the list + # of prepared devices to the workers (it is read in __init__()). + if self._devices._prepared_devices: + self._options.prepared_devices = self._devices.prepared_devices() + else: + # We were called with --no-build, so assume the devices are up to date. + self._options.prepared_devices = [d.get_serial() for d in self._devices.usable_devices(self.host.executive)] + def check_sys_deps(self, needs_http): for (font_dirs, font_file, package) in HOST_FONT_FILES: exists = False @@ -521,9 +607,9 @@ class AndroidPort(chromium.ChromiumPort): # Local private methods. @staticmethod - def _android_server_process_constructor(port, server_name, cmd_line, env=None): + def _android_server_process_constructor(port, server_name, cmd_line, env=None, logging=False): return server_process.ServerProcess(port, server_name, cmd_line, env, - universal_newlines=True, treat_no_data_as_crash=True) + universal_newlines=True, treat_no_data_as_crash=True, logging=logging) class AndroidPerf(SingleFileOutputProfiler): @@ -660,6 +746,7 @@ class ChromiumAndroidDriver(driver.Driver): self._android_devices = android_devices self._android_commands = android_devices.get_device(port._executive, worker_number) self._driver_details = driver_details + self._debug_logging = self._port._debug_logging # FIXME: If we taught ProfileFactory about "target" devices we could # just use the logic in Driver instead of duplicating it here. @@ -723,22 +810,26 @@ class ChromiumAndroidDriver(driver.Driver): return symfs_path - def _setup_md5sum_and_push_data_if_needed(self): + def _setup_md5sum_and_push_data_if_needed(self, log_callback): self._md5sum_path = self._port.path_to_md5sum() if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH): if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_PATH): raise AssertionError('Could not push md5sum to device') - self._push_executable() - self._push_fonts() - self._push_test_resources() + self._push_executable(log_callback) + self._push_fonts(log_callback) + self._push_test_resources(log_callback) + + def _setup_test(self, log_callback): + # FIXME: Move this routine and its subroutines off of the AndroidDriver + # class and onto AndroidCommands or some other helper class, so that we + # can initialize the device without needing to create a driver. - def _setup_test(self): if self._android_devices.is_device_prepared(self._android_commands.get_serial()): return self._android_commands.restart_as_root() - self._setup_md5sum_and_push_data_if_needed() + self._setup_md5sum_and_push_data_if_needed(log_callback) self._setup_performance() # Required by webkit_support::GetWebKitRootDirFilePath(). @@ -760,7 +851,8 @@ class ChromiumAndroidDriver(driver.Driver): _log.error('[%s] %s' % (self._android_commands.get_serial(), message)) def _log_debug(self, message): - _log.debug('[%s] %s' % (self._android_commands.get_serial(), message)) + if self._debug_logging: + _log.debug('[%s] %s' % (self._android_commands.get_serial(), message)) def _abort(self, message): raise AssertionError('[%s] %s' % (self._android_commands.get_serial(), message)) @@ -770,47 +862,56 @@ class ChromiumAndroidDriver(driver.Driver): assert md5sum_output return [line.split(' ')[0] for line in md5sum_output] - def _push_file_if_needed(self, host_file, device_file): - assert os.path.exists(host_file) + def _files_match(self, host_file, device_file): + assert self._port.host.filesystem.exists(host_file) device_hashes = self._extract_hashes_from_md5sum_output( self._port.host.executive.popen(self._android_commands.adb_command() + ['shell', MD5SUM_DEVICE_PATH, device_file], stdout=subprocess.PIPE).stdout) host_hashes = self._extract_hashes_from_md5sum_output( self._port.host.executive.popen(args=['%s_host' % self._md5sum_path, host_file], stdout=subprocess.PIPE).stdout) - if host_hashes and device_hashes == host_hashes: - return + return host_hashes and device_hashes == host_hashes - self._android_commands.push(host_file, device_file) + def _push_file_if_needed(self, host_file, device_file, log_callback): + basename = self._port.host.filesystem.basename(host_file) + log_callback("checking %s" % basename) + if not self._files_match(host_file, device_file): + log_callback("pushing %s" % basename) + self._android_commands.push(host_file, device_file) - def _push_executable(self): - self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path()) + def _push_executable(self, log_callback): + self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path(), log_callback) for resource in self._driver_details.additional_resources(): - self._push_file_if_needed(self._port._build_path(resource), self._driver_details.device_directory() + resource) + self._push_file_if_needed(self._port._build_path(resource), self._driver_details.device_directory() + resource, log_callback) - self._push_file_if_needed(self._port._build_path('android_main_fonts.xml'), self._driver_details.device_directory() + 'android_main_fonts.xml') - self._push_file_if_needed(self._port._build_path('android_fallback_fonts.xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml') + self._push_file_if_needed(self._port._build_path('android_main_fonts.xml'), self._driver_details.device_directory() + 'android_main_fonts.xml', log_callback) + self._push_file_if_needed(self._port._build_path('android_fallback_fonts.xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml', log_callback) + + log_callback("checking apk") + if self._files_match(self._port._build_path('apks', 'ContentShell.apk'), + '/data/app/org.chromium.content_shell_apk-1.apk'): + return + log_callback("uninstalling apk") self._android_commands.run(['uninstall', self._driver_details.package_name()]) driver_host_path = self._port._path_to_driver() + log_callback("installing apk") install_result = self._android_commands.run(['install', driver_host_path]) if install_result.find('Success') == -1: self._abort('Failed to install %s onto device: %s' % (driver_host_path, install_result)) - def _push_fonts(self): - self._log_debug('Pushing fonts') + def _push_fonts(self, log_callback): path_to_ahem_font = self._port._build_path('AHEM____.TTF') - self._push_file_if_needed(path_to_ahem_font, self._driver_details.device_fonts_directory() + 'AHEM____.TTF') + self._push_file_if_needed(path_to_ahem_font, self._driver_details.device_fonts_directory() + 'AHEM____.TTF', log_callback) for (host_dirs, font_file, package) in HOST_FONT_FILES: for host_dir in host_dirs: host_font_path = host_dir + font_file if self._port._check_file_exists(host_font_path, '', logging=False): - self._push_file_if_needed(host_font_path, self._driver_details.device_fonts_directory() + font_file) + self._push_file_if_needed(host_font_path, self._driver_details.device_fonts_directory() + font_file, log_callback) - def _push_test_resources(self): - self._log_debug('Pushing test resources') + def _push_test_resources(self, log_callback): for resource in TEST_RESOURCES_TO_PUSH: - self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource) + self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource, log_callback) def _get_last_stacktrace(self): tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones']) @@ -915,7 +1016,7 @@ class ChromiumAndroidDriver(driver.Driver): super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args) def _start(self, pixel_tests, per_test_args): - self._setup_test() + assert self._android_devices.is_device_prepared(self._android_commands.get_serial()) for retries in range(3): if self._start_once(pixel_tests, per_test_args): @@ -993,6 +1094,7 @@ class ChromiumAndroidDriver(driver.Driver): # Inform the deadlock detector that the startup is successful without deadlock. normal_startup_event.set() + self._log_debug("content_shell is ready") return True def _pid_from_android_ps_output(self, ps_output, package_name): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py index 14db69240df..56248ed2977 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py @@ -69,8 +69,16 @@ class MockAndroidDebugBridge: return self._get_device_output() if len(args) > 3 and args[3] == 'command': return 'mockoutput' + if len(args) > 3 and args[3] == 'install': + return 'Success' + if len(args) > 3 and args[3] in ('push', 'wait-for-device'): + return 'mockoutput' if len(args) > 5 and args[5] == 'battery': return 'level: 99' + if len(args) > 5 and args[5] == 'force-stop': + return 'mockoutput' + if len(args) > 5 and args[5] == 'power': + return 'mScreenOn=true' return '' @@ -98,14 +106,14 @@ class AndroidCommandsTest(unittest.TestCase): return MockExecutive2(run_command_fn=self._mock_executive.run_command) def make_android_commands(self, device_count, serial): - return android.AndroidCommands(self.make_executive(device_count), serial) + return android.AndroidCommands(self.make_executive(device_count), serial, debug_logging=False) # The "adb" binary with the latest version should be used. def serial_test_adb_command_path(self): executive = self.make_executive(0) android.AndroidCommands.set_adb_command_path_options(['path1', 'path2', 'path3']) - self.assertEqual('path2', android.AndroidCommands.adb_command_path(executive)) + self.assertEqual('path2', android.AndroidCommands.adb_command_path(executive, debug_logging=False)) # The used adb command should include the device's serial number, and get_serial() should reflect this. def test_adb_command_and_get_serial(self): @@ -145,6 +153,13 @@ class AndroidPortTest(chromium_port_testcase.ChromiumPortTestCase): port._executive = MockExecutive2(run_command_fn=port._mock_adb.run_command) return port + def test_check_build(self): + host = MockSystemHost() + host.filesystem.exists = lambda p: True + port = self.make_port(host=host) + port.check_build(needs_http=True, printer=chromium_port_testcase.FakePrinter()) + + def make_wdiff_available(self, port): port._wdiff_available = True port._host_port._wdiff_available = True @@ -176,7 +191,7 @@ class ChromiumAndroidDriverTest(unittest.TestCase): self._mock_adb = MockAndroidDebugBridge(1) self._mock_executive = MockExecutive2(run_command_fn=self._mock_adb.run_command) - android_commands = android.AndroidCommands(self._mock_executive, '123456789ABCDEF0') + android_commands = android.AndroidCommands(self._mock_executive, '123456789ABCDEF0', debug_logging=False) self._port = android.AndroidPort(MockSystemHost(executive=self._mock_executive), 'android') self._driver = android.ChromiumAndroidDriver(self._port, worker_number=0, pixel_tests=True, driver_details=android.ContentShellDriverDetails(), android_devices=self._port._devices) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py index 5ab1250bda5..4637f585f04 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py @@ -164,12 +164,14 @@ class Port(object): # FIXME: Disable until they are run by default on build.webkit.org. return False + def default_smoke_test_only(self): + return False + def default_timeout_ms(self): timeout_ms = 6 * 1000 if self.get_option('configuration') == 'Debug': - # Debug is 6x slower than Release - # FIXME: It should be closer to 2x. See crbug.com/254188 - return 6 * timeout_ms + # Debug is usually 2x-3x slower than Release. + return 3 * timeout_ms return timeout_ms def driver_stop_timeout(self): @@ -243,7 +245,7 @@ class Port(object): return factory.get(target_port).default_baseline_search_path() return [] - def check_build(self, needs_http): + def check_build(self, needs_http, printer): """This routine is used to ensure that the build is up to date and all the needed binaries are present.""" if self.get_option('build'): @@ -869,44 +871,46 @@ class Port(object): def setup_environ_for_server(self, server_name=None): # We intentionally copy only a subset of os.environ when # launching subprocesses to ensure consistent test results. - clean_env = {} + clean_env = { + 'LOCAL_RESOURCE_ROOT': self.layout_tests_dir(), # FIXME: Is this used? + } variables_to_copy = [ - # For Linux: - 'XAUTHORITY', - 'HOME', - 'LANG', - 'LD_LIBRARY_PATH', - 'DBUS_SESSION_BUS_ADDRESS', - 'XDG_DATA_DIRS', - - # Darwin: - 'DYLD_LIBRARY_PATH', - 'HOME', - - # CYGWIN: - 'HOMEDRIVE', - 'HOMEPATH', - '_NT_SYMBOL_PATH', - - # Windows: - 'PATH', - - # Most ports (?): - 'WEBKIT_TESTFONTS', - 'WEBKITOUTPUTDIR', - - # Chromium: + 'WEBKIT_TESTFONTS', # FIXME: Is this still used? + 'WEBKITOUTPUTDIR', # FIXME: Is this still used? 'CHROME_DEVEL_SANDBOX', 'CHROME_IPC_LOGGING', 'ASAN_OPTIONS', - ] + if self.host.platform.is_linux() or self.host.platform.is_freebsd(): + variables_to_copy += [ + 'XAUTHORITY', + 'HOME', + 'LANG', + 'LD_LIBRARY_PATH', + 'DBUS_SESSION_BUS_ADDRESS', + 'XDG_DATA_DIRS', + ] + clean_env['DISPLAY'] = self._value_or_default_from_environ('DISPLAY', ':1') + if self.host.platform.is_mac(): + clean_env['DYLD_LIBRARY_PATH'] = self._build_path() + clean_env['DYLD_FRAMEWORK_PATH'] = self._build_path() + variables_to_copy += [ + 'HOME', + ] + if self.host.platform.is_win(): + variables_to_copy += [ + 'PATH', + ] + if self.host.platform.is_cygwin(): + variables_to_copy += [ + 'HOMEDRIVE', + 'HOMEPATH', + '_NT_SYMBOL_PATH', + ] + for variable in variables_to_copy: self._copy_value_from_environ_if_set(clean_env, variable) - # For Linux: - clean_env['DISPLAY'] = self._value_or_default_from_environ('DISPLAY', ':1') - for string_variable in self.get_option('additional_env_var', []): [name, value] = string_variable.split('=', 1) clean_env[name] = value diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py index 91c7c7a0c8a..c07c82b791d 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py @@ -41,8 +41,7 @@ from webkitpy.common.memoized import memoized _exact_matches = { "WebKit XP": {"port_name": "win-xp"}, "WebKit Win7": {"port_name": "win-win7"}, - "WebKit Win7 (dbg)(1)": {"port_name": "win-win7"}, - "WebKit Win7 (dbg)(2)": {"port_name": "win-win7"}, + "WebKit Win7 (dbg)": {"port_name": "win-win7"}, "WebKit Linux": {"port_name": "linux-x86_64"}, "WebKit Linux 32": {"port_name": "linux-x86"}, "WebKit Linux (dbg)": {"port_name": "linux-x86_64"}, @@ -51,6 +50,8 @@ _exact_matches = { "WebKit Mac10.7": {"port_name": "mac-lion"}, "WebKit Mac10.7 (dbg)": {"port_name": "mac-lion"}, "WebKit Mac10.8": {"port_name": "mac-mountainlion"}, + "WebKit Mac10.8 (retina)": {"port_name": "mac-retina"}, + "WebKit Android (Nexus4)": {"port_name": "android"}, } @@ -63,12 +64,11 @@ _deps_builders = { "mac-snowleopard": "WebKit Mac10.6 (deps)", "mac-lion": "WebKit Mac10.6 (deps)", "mac-mountainlion": "WebKit Mac10.6 (deps)", + "mac-retina": "WebKit Mac10.6 (deps)", } _ports_without_builders = [ - # FIXME: Move to _exact_matches. - "android", ] diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py index 55ff2906542..68286acc06d 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py @@ -51,6 +51,12 @@ class ChromiumPort(Port): ALL_SYSTEMS = ( ('snowleopard', 'x86'), ('lion', 'x86'), + + # FIXME: We treat Retina (High-DPI) devices as if they are running + # a different operating system version. This isn't accurate, but will work until + # we need to test and support baselines across multiple O/S versions. + ('retina', 'x86'), + ('mountainlion', 'x86'), ('xp', 'x86'), ('win7', 'x86'), @@ -58,22 +64,20 @@ class ChromiumPort(Port): ('lucid', 'x86_64'), # FIXME: Technically this should be 'arm', but adding a third architecture type breaks TestConfigurationConverter. # If we need this to be 'arm' in the future, then we first have to fix TestConfigurationConverter. - # FIXME: Until we have an android bot to do rebaselines, we need to not include it in ALL_SYSTEMS - # because otherwise rebaselines won't correclty remove lines from TestExpectations. - #('icecreamsandwich', 'x86'), + ('icecreamsandwich', 'x86'), ) ALL_BASELINE_VARIANTS = [ - 'mac-mountainlion', 'mac-lion', 'mac-snowleopard', + 'mac-mountainlion', 'mac-retina', 'mac-lion', 'mac-snowleopard', 'win-win7', 'win-xp', 'linux-x86_64', 'linux-x86', ] CONFIGURATION_SPECIFIER_MACROS = { - 'mac': ['snowleopard', 'lion', 'mountainlion'], + 'mac': ['snowleopard', 'lion', 'retina', 'mountainlion'], 'win': ['xp', 'win7'], 'linux': ['lucid'], - # 'android': ['icecreamsandwich'], # FIXME: see comment above next to 'icecreamsandwich'. + 'android': ['icecreamsandwich'], } DEFAULT_BUILD_DIRECTORIES = ('out',) @@ -159,7 +163,7 @@ class ChromiumPort(Port): return False return True - def check_build(self, needs_http): + def check_build(self, needs_http, printer): result = True dump_render_tree_binary_path = self._path_to_driver() @@ -349,6 +353,23 @@ class ChromiumPort(Port): VirtualTestSuite('virtual/threaded/transitions', 'transitions', ['--enable-threaded-compositing']), + VirtualTestSuite('virtual/web-animations-css/animations', + 'animations', + ['--enable-web-animations-css']), + VirtualTestSuite('virtual/stable/webexposed', + 'webexposed', + ['--stable-release-mode']), + VirtualTestSuite('virtual/stable/media', + 'media/stable', + ['--stable-release-mode']), + VirtualTestSuite('virtual/android/fullscreen', + 'fullscreen', + ['--force-compositing-mode', '--allow-webui-compositing', '--enable-threaded-compositing', + '--enable-fixed-position-compositing', '--enable-accelerated-overflow-scroll', '--enable-accelerated-scrollable-frames', + '--enable-composited-scrolling-for-frames', '--enable-gesture-tap-highlight', '--enable-pinch', + '--enable-overlay-fullscreen-video', '--enable-overlay-scrollbars', '--enable-overscroll-notifications', + '--enable-fixed-layout', '--enable-viewport', '--disable-canvas-aa', + '--disable-composited-antialiasing']), ] # diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py index faa7a6bf02b..ade4233fbb9 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py @@ -42,11 +42,19 @@ from webkitpy.layout_tests.models.test_configuration import TestConfiguration from webkitpy.layout_tests.port import port_testcase +class FakePrinter(object): + def write_update(self, msg): + pass + + def write_throttled_update(self, msg): + pass + + class ChromiumPortTestCase(port_testcase.PortTestCase): def test_check_build(self): port = self.make_port() - port.check_build(needs_http=True) + port.check_build(needs_http=True, printer=FakePrinter()) def test_default_max_locked_shards(self): port = self.make_port() @@ -76,6 +84,8 @@ class ChromiumPortTestCase(port_testcase.PortTestCase): TestConfiguration('snowleopard', 'x86', 'release'), TestConfiguration('lion', 'x86', 'debug'), TestConfiguration('lion', 'x86', 'release'), + TestConfiguration('retina', 'x86', 'debug'), + TestConfiguration('retina', 'x86', 'release'), TestConfiguration('mountainlion', 'x86', 'debug'), TestConfiguration('mountainlion', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug'), @@ -86,6 +96,8 @@ class ChromiumPortTestCase(port_testcase.PortTestCase): TestConfiguration('lucid', 'x86', 'release'), TestConfiguration('lucid', 'x86_64', 'debug'), TestConfiguration('lucid', 'x86_64', 'release'), + TestConfiguration('icecreamsandwich', 'x86', 'debug'), + TestConfiguration('icecreamsandwich', 'x86', 'release'), ])) class TestMacPort(mac.MacPort): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py index c30d1141eaa..ddbb3db11f0 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py @@ -256,11 +256,6 @@ class Driver(object): self._run_post_start_tasks() def _setup_environ_for_driver(self, environment): - environment['DYLD_LIBRARY_PATH'] = self._port._build_path() - environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path() - environment['LOCAL_RESOURCE_ROOT'] = self._port.layout_tests_dir() - if 'WEBKITOUTPUTDIR' in os.environ: - environment['WEBKITOUTPUTDIR'] = os.environ['WEBKITOUTPUTDIR'] if self._profiler: environment = self._profiler.adjusted_environment(environment) return environment @@ -274,7 +269,7 @@ class Driver(object): self._crashed_process_name = None self._crashed_pid = None cmd_line = self.cmd_line(pixel_tests, per_test_args) - self._server_process = self._port._server_process_constructor(self._port, server_name, cmd_line, environment) + self._server_process = self._port._server_process_constructor(self._port, server_name, cmd_line, environment, logging=self._port.get_option("driver_logging")) self._server_process.start() self._current_cmd_line = cmd_line @@ -399,6 +394,9 @@ class Driver(object): def _strip_eof(self, line): if line and line.endswith("#EOF\n"): return line[:-5], True + if line and line.endswith("#EOF\r\n"): + _log.error("Got a CRLF-terminated #EOF - this is a driver bug.") + return line[:-6], True return line, False def _read_block(self, deadline, wait_for_stderr_eof=False): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py index d1a947d5bfb..741cc0de5b3 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py @@ -66,7 +66,7 @@ class LinuxPort(chromium.ChromiumPort): @staticmethod def _determine_architecture(filesystem, executive, driver_path): file_output = '' - if filesystem.exists(driver_path): + if filesystem.isfile(driver_path): # The --dereference flag tells file to follow symlinks file_output = executive.run_command(['file', '--brief', '--dereference', driver_path], return_stderr=True) @@ -112,8 +112,8 @@ class LinuxPort(chromium.ChromiumPort): def _modules_to_search_for_symbols(self): return [self._build_path('libffmpegsumo.so')] - def check_build(self, needs_http): - result = chromium.ChromiumPort.check_build(self, needs_http) + def check_build(self, needs_http, printer): + result = chromium.ChromiumPort.check_build(self, needs_http, printer) if not result: _log.error('For complete Linux build requirements, please see:') _log.error('') diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py index fde3e39e9e8..f8c3ba5e668 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py @@ -42,7 +42,7 @@ class LinuxPortTest(chromium_port_testcase.ChromiumPortTestCase): def assert_architecture(self, port_name=None, file_output=None, expected_architecture=None): host = MockSystemHost() - host.filesystem.exists = lambda x: 'content_shell' in x + host.filesystem.isfile = lambda x: 'content_shell' in x if file_output: host.executive = executive_mock.MockExecutive2(file_output) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py index 4a62eb920c9..c925584b489 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py @@ -38,13 +38,18 @@ _log = logging.getLogger(__name__) class MacPort(chromium.ChromiumPort): - SUPPORTED_VERSIONS = ('snowleopard', 'lion', 'mountainlion') + SUPPORTED_VERSIONS = ('snowleopard', 'lion', 'retina', 'mountainlion') port_name = 'mac' FALLBACK_PATHS = { 'mountainlion': [ 'mac' ]} FALLBACK_PATHS['lion'] = ['mac-lion'] + FALLBACK_PATHS['mountainlion'] FALLBACK_PATHS['snowleopard'] = ['mac-snowleopard'] + FALLBACK_PATHS['lion'] + # FIXME: We treat Retina (High-DPI) devices as if they are running + # a different operating system version. This isn't accurate, but will work until + # we need to test and support baselines across multiple O/S versions. + FALLBACK_PATHS['retina'] = ['mac-retina'] + FALLBACK_PATHS['mountainlion'] + DEFAULT_BUILD_DIRECTORIES = ('xcodebuild', 'out') CONTENT_SHELL_NAME = 'Content Shell' @@ -52,6 +57,8 @@ class MacPort(chromium.ChromiumPort): @classmethod def determine_full_port_name(cls, host, options, port_name): if port_name.endswith('mac'): + if host.platform.is_highdpi(): + return "mac-retina" return port_name + '-' + host.platform.os_version return port_name @@ -63,8 +70,8 @@ class MacPort(chromium.ChromiumPort): def _modules_to_search_for_symbols(self): return [self._build_path('ffmpegsumo.so')] - def check_build(self, needs_http): - result = chromium.ChromiumPort.check_build(self, needs_http) + def check_build(self, needs_http, printer): + result = chromium.ChromiumPort.check_build(self, needs_http, printer) if not result: _log.error('For complete Mac build requirements, please see:') _log.error('') diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py index 2ee659c2040..7992a00a12c 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py @@ -68,7 +68,7 @@ class MockDRTPort(object): def __getattr__(self, name): return getattr(self.__delegate, name) - def check_build(self, needs_http): + def check_build(self, needs_http, printer): return True def check_sys_deps(self, needs_http): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py index 9868210ea2f..05a50016a81 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py @@ -100,7 +100,7 @@ class PortTestCase(unittest.TestCase): def test_default_timeout_ms(self): self.assertEqual(self.make_port(options=MockOptions(configuration='Release')).default_timeout_ms(), 6000) - self.assertEqual(self.make_port(options=MockOptions(configuration='Debug')).default_timeout_ms(), 36000) + self.assertEqual(self.make_port(options=MockOptions(configuration='Debug')).default_timeout_ms(), 18000) def test_default_pixel_tests(self): self.assertEqual(self.make_port().default_pixel_tests(), True) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py index b24ef809182..58ccb5c7ad3 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py @@ -30,6 +30,7 @@ import errno import logging +import re import signal import sys import time @@ -37,14 +38,20 @@ import time # Note that although win32 python does provide an implementation of # the win32 select API, it only works on sockets, and not on the named pipes # used by subprocess, so we have to use the native APIs directly. +_quote_cmd = None + if sys.platform == 'win32': import msvcrt import win32pipe import win32file + import subprocess + _quote_cmd = subprocess.list2cmdline else: import fcntl import os + import pipes import select + _quote_cmd = lambda cmdline: ' '.join(pipes.quote(arg) for arg in cmdline) from webkitpy.common.system.executive import ScriptError @@ -52,6 +59,19 @@ from webkitpy.common.system.executive import ScriptError _log = logging.getLogger(__name__) +_trailing_spaces_re = re.compile('(.*[^ ])?( +)$') + + +def quote_data(data): + txt = repr(data).replace('\\n', '\\n\n')[1:-1] + lines = [] + for l in txt.splitlines(): + m = _trailing_spaces_re.match(l) + if m: + l = m.group(1) + m.group(2).replace(' ', '\x20') + lines.append(l) + return lines + class ServerProcess(object): """This class provides a wrapper around a subprocess that implements a simple request/response usage model. The primary benefit @@ -59,7 +79,8 @@ class ServerProcess(object): indefinitely. The class also handles transparently restarting processes as necessary to keep issuing commands.""" - def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False): + def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False, + logging=False): self._port = port_obj self._name = name # Should be the command name (e.g. content_shell, image_diff) self._cmd = cmd @@ -68,6 +89,7 @@ class ServerProcess(object): # Don't set if there will be binary data or the data must be ASCII encoded. self._universal_newlines = universal_newlines self._treat_no_data_as_crash = treat_no_data_as_crash + self._logging = logging self._host = self._port.host self._pid = None self._reset() @@ -109,6 +131,11 @@ class ServerProcess(object): self._reset() # close_fds is a workaround for http://bugs.python.org/issue2320 close_fds = not self._host.platform.is_win() + if self._logging: + env_str = '' + if self._env: + env_str += '\n'.join("%s=%s" % (k, v) for k, v in self._env.items()) + '\n' + _log.info('CMD: \n%s%s\n', env_str, _quote_cmd(self._cmd)) self._proc = self._host.executive.popen(self._cmd, stdin=self._host.executive.PIPE, stdout=self._host.executive.PIPE, stderr=self._host.executive.PIPE, @@ -149,6 +176,7 @@ class ServerProcess(object): if not self._proc: self._start() try: + self._log_data(' IN', bytes) self._proc.stdin.write(bytes) except IOError, e: self.stop(0.0) @@ -209,6 +237,11 @@ class ServerProcess(object): _log.info('') _log.info(message) + def _log_data(self, prefix, data): + if self._logging and data and len(data): + for line in quote_data(data): + _log.info('%s: %s', prefix, line) + def _handle_timeout(self): self.timed_out = True self._port.sample_process(self._name, self._proc.pid) @@ -252,12 +285,14 @@ class ServerProcess(object): data = self._proc.stdout.read() if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()): self._crashed = True + self._log_data('OUT', data) self._output += data if err_fd in read_fds: data = self._proc.stderr.read() if not data and not stopping and (self._treat_no_data_as_crash or self._proc.poll()): self._crashed = True + self._log_data('ERR', data) self._error += data except IOError, e: # We can ignore the IOErrors because we will detect if the subporcess crashed @@ -273,7 +308,9 @@ class ServerProcess(object): err_fh = msvcrt.get_osfhandle(self._proc.stderr.fileno()) while (self._proc.poll() is None) and (now < deadline): output = self._non_blocking_read_win32(out_fh) + self._log_data('OUT', output) error = self._non_blocking_read_win32(err_fh) + self._log_data('ERR', error) if output or error: if output: self._output += output @@ -332,6 +369,8 @@ class ServerProcess(object): now = time.time() if self._proc.stdin: + if self._logging: + _log.info(' IN: ^D') self._proc.stdin.close() self._proc.stdin = None killed = False diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py index d234ebdc399..99cf2d5757b 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py @@ -28,13 +28,15 @@ class MockServerProcess(object): - def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, lines=None, crashed=False): + def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, treat_no_data_as_crash=False, logging=False, lines=None, crashed=False): self.timed_out = False self.lines = lines or [] self.crashed = crashed self.writes = [] self.cmd = cmd self.env = env + self.treat_no_data_as_crash = treat_no_data_as_crash + self.logging = logging self.started = False self.stopped = False diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py index e17d7a9e762..0b1db739173 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py @@ -37,7 +37,6 @@ from webkitpy.common.system.systemhost_mock import MockSystemHost from webkitpy.common.system.outputcapture import OutputCapture - class TrivialMockPort(object): def __init__(self): self.host = MockSystemHost() @@ -147,3 +146,24 @@ class TestServerProcess(unittest.TestCase): self.assertTrue(server_process.has_crashed()) self.assertIsNone(server_process._proc) self.assertEqual(server_process.broken_pipes, [server_process.stdin]) + + +class TestQuoteData(unittest.TestCase): + def test_plain(self): + qd = server_process.quote_data + self.assertEqual(qd("foo"), ["foo"]) + + def test_trailing_spaces(self): + qd = server_process.quote_data + self.assertEqual(qd("foo "), + ["foo\x20\x20"]) + + def test_newlines(self): + qd = server_process.quote_data + self.assertEqual(qd("foo \nbar\n"), + ["foo\x20\\n", "bar\\n"]) + + def test_binary_data(self): + qd = server_process.quote_data + self.assertEqual(qd("\x00\x01ab"), + ["\\x00\\x01ab"]) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py index 8871c8732c6..8257e324181 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py @@ -47,7 +47,6 @@ class TestInstance(object): self.crash = False self.web_process_crash = False self.exception = False - self.hang = False self.keyboard = False self.error = '' self.timeout = False @@ -82,8 +81,8 @@ class TestList(object): test.__dict__[key] = value self.tests[name] = test - def add_reftest(self, name, reference_name, same_image): - self.add(name, actual_checksum='xxx', actual_image='XXX', is_reftest=True) + def add_reftest(self, name, reference_name, same_image, crash=False): + self.add(name, actual_checksum='xxx', actual_image='XXX', is_reftest=True, crash=crash) if same_image: self.add(reference_name, actual_checksum='xxx', actual_image='XXX', is_reftest=True) else: @@ -101,18 +100,17 @@ class TestList(object): # # These numbers may need to be updated whenever we add or delete tests. # -TOTAL_TESTS = 106 -TOTAL_SKIPS = 27 +TOTAL_TESTS = 105 +TOTAL_SKIPS = 25 UNEXPECTED_PASSES = 1 -UNEXPECTED_FAILURES = 22 +UNEXPECTED_FAILURES = 23 def unit_test_list(): tests = TestList() tests.add('failures/expected/crash.html', crash=True) tests.add('failures/expected/exception.html', exception=True) tests.add('failures/expected/timeout.html', timeout=True) - tests.add('failures/expected/hang.html', hang=True) tests.add('failures/expected/missing_text.html', expected_text=None) tests.add('failures/expected/needsrebaseline.html', actual_text='needsrebaseline text') tests.add('failures/expected/needsmanualrebaseline.html', actual_text='needsmanualrebaseline text') @@ -207,11 +205,11 @@ layer at (0,0) size 800x34 tests.add_reftest('passes/phpreftest.php', 'passes/phpreftest-expected-mismatch.svg', same_image=False) tests.add_reftest('failures/expected/reftest.html', 'failures/expected/reftest-expected.html', same_image=False) tests.add_reftest('failures/expected/mismatch.html', 'failures/expected/mismatch-expected-mismatch.html', same_image=True) + tests.add_reftest('failures/unexpected/crash-reftest.html', 'failures/unexpected/crash-reftest-expected.html', same_image=True, crash=True) tests.add_reftest('failures/unexpected/reftest.html', 'failures/unexpected/reftest-expected.html', same_image=False) tests.add_reftest('failures/unexpected/mismatch.html', 'failures/unexpected/mismatch-expected-mismatch.html', same_image=True) tests.add('failures/unexpected/reftest-nopixel.html', actual_checksum=None, actual_image=None, is_reftest=True) tests.add('failures/unexpected/reftest-nopixel-expected.html', actual_checksum=None, actual_image=None, is_reftest=True) - # FIXME: Add a reftest which crashes. tests.add('reftests/foo/test.html') tests.add('reftests/foo/test-ref.html') @@ -270,9 +268,9 @@ PERF_TEST_DIR = '/test.checkout/PerformanceTests' # we don't need a real filesystem to run the tests. def add_unit_tests_to_mock_filesystem(filesystem): # Add the test_expectations file. - filesystem.maybe_make_directory(LAYOUT_TEST_DIR + '/platform/test') - if not filesystem.exists(LAYOUT_TEST_DIR + '/platform/test/TestExpectations'): - filesystem.write_text_file(LAYOUT_TEST_DIR + '/platform/test/TestExpectations', """ + filesystem.maybe_make_directory('/mock-checkout/LayoutTests') + if not filesystem.exists('/mock-checkout/LayoutTests/TestExpectations'): + filesystem.write_text_file('/mock-checkout/LayoutTests/TestExpectations', """ Bug(test) failures/expected/crash.html [ Crash ] Bug(test) failures/expected/image.html [ ImageOnlyFailure ] Bug(test) failures/expected/needsrebaseline.html [ NeedsRebaseline ] @@ -290,7 +288,6 @@ Bug(test) failures/expected/newlines_with_excess_CR.html [ Failure ] Bug(test) failures/expected/reftest.html [ ImageOnlyFailure ] Bug(test) failures/expected/text.html [ Failure ] Bug(test) failures/expected/timeout.html [ Timeout ] -Bug(test) failures/expected/hang.html [ WontFix ] Bug(test) failures/expected/keyboard.html [ WontFix ] Bug(test) failures/expected/exception.html [ WontFix ] Bug(test) failures/unexpected/pass.html [ Failure ] @@ -367,7 +364,17 @@ class TestPort(Port): Port.__init__(self, host, port_name or TestPort.default_port_name, **kwargs) self._tests = unit_test_list() self._flakes = set() - self._generic_expectations_path = LAYOUT_TEST_DIR + '/TestExpectations' + + # FIXME: crbug.com/279494. This needs to be in the "real layout tests + # dir" in a mock filesystem, rather than outside of the checkout, so + # that tests that want to write to a TestExpectations file can share + # this between "test" ports and "real" ports. This is the result of + # rebaseline_unittest.py having tests that refer to "real" port names + # and real builders instead of fake builders that point back to the + # test ports. rebaseline_unittest.py needs to not mix both "real" ports + # and "test" ports + + self._generic_expectations_path = '/mock-checkout/LayoutTests/TestExpectations' self._results_directory = None self._operating_system = 'mac' @@ -412,7 +419,7 @@ class TestPort(Port): def worker_startup_delay_secs(self): return 0 - def check_build(self, needs_http): + def check_build(self, needs_http, printer): return True def check_sys_deps(self, needs_http): @@ -571,8 +578,6 @@ class TestDriver(Driver): raise KeyboardInterrupt if test.exception: raise ValueError('exception from ' + test_name) - if test.hang: - time.sleep((float(driver_input.timeout) * 4) / 1000.0 + 1.0) # The 1.0 comes from thread_padding_sec in layout_test_runnery. audio = None actual_text = test.actual_text diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py index 08aac077fa4..df9db13fbc4 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py @@ -91,8 +91,8 @@ class WinPort(chromium.ChromiumPort): # See https://bugs.webkit.org/show_bug.cgi?id=89706. return [] - def check_build(self, needs_http): - result = chromium.ChromiumPort.check_build(self, needs_http) + def check_build(self, needs_http, printer): + result = chromium.ChromiumPort.check_build(self, needs_http, printer) if not result: _log.error('For complete Windows build requirements, please see:') _log.error('') diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times.py new file mode 100755 index 00000000000..15bc1f00e33 --- /dev/null +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times.py @@ -0,0 +1,150 @@ +# Copyright (C) 2013 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: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. + +import json +import optparse + +from webkitpy.layout_tests.port import Port + + +def main(host, argv): + parser = optparse.OptionParser(usage='%prog [times_ms.json]') + parser.add_option('-f', '--forward', action='store', type='int', + help='group times by first N directories of test') + parser.add_option('-b', '--backward', action='store', type='int', + help='group times by last N directories of test') + parser.add_option('--fastest', action='store', type='float', + help='print a list of tests that will take N % of the time') + + epilog = """ + You can print out aggregate times per directory using the -f and -b + flags. The value passed to each flag indicates the "depth" of the flag, + similar to positive and negative arguments to python arrays. + + For example, given fast/forms/week/week-input-type.html, -f 1 + truncates to 'fast', -f 2 and -b 2 truncates to 'fast/forms', and -b 1 + truncates to fast/forms/week . -f 0 truncates to '', which can be used + to produce a single total time for the run.""" + parser.epilog = '\n'.join(s.lstrip() for s in epilog.splitlines()) + + options, args = parser.parse_args(argv) + + port = host.port_factory.get() + if args and args[0]: + times_ms_path = args[0] + else: + times_ms_path = host.filesystem.join(port.results_directory(), 'times_ms.json') + + times_trie = json.loads(host.filesystem.read_text_file(times_ms_path)) + + times = convert_trie_to_flat_paths(times_trie) + + if options.fastest: + if options.forward is None and options.backward is None: + options.forward = 0 + print_fastest(host, port, options, times) + else: + print_times(host, options, times) + + +def print_times(host, options, times): + by_key = times_by_key(times, options.forward, options.backward) + for key in sorted(by_key): + if key: + host.print_("%s %d" % (key, by_key[key])) + else: + host.print_("%d" % by_key[key]) + + +def print_fastest(host, port, options, times): + total = times_by_key(times, 0, None)[''] + by_key = times_by_key(times, options.forward, options.backward) + keys_by_time = sorted(by_key, key=lambda k: (by_key[k], k)) + + tests_by_key = {} + for test_name in sorted(times): + key = key_for(test_name, options.forward, options.backward) + if key in sorted(tests_by_key): + tests_by_key[key].append(test_name) + else: + tests_by_key[key] = [test_name] + + fast_tests_by_key = {} + total_so_far = 0 + per_key = total * options.fastest / (len(keys_by_time) * 100.0) + budget = 0 + while keys_by_time: + budget += per_key + key = keys_by_time.pop(0) + tests_by_time = sorted(tests_by_key[key], key=lambda t: (times[t], t)) + fast_tests_by_key[key] = [] + while tests_by_time and total_so_far <= budget: + test = tests_by_time.pop(0) + test_time = times[test] + # Make sure test time > 0 so we don't include tests that are skipped. + if test_time and total_so_far + test_time <= budget: + fast_tests_by_key[key].append(test) + total_so_far += test_time + + for k in sorted(fast_tests_by_key): + for t in fast_tests_by_key[k]: + host.print_("%s %d" % (t, times[t])) + return + + +def key_for(path, forward, backward): + sep = Port.TEST_PATH_SEPARATOR + if forward is not None: + return sep.join(path.split(sep)[:-1][:forward]) + if backward is not None: + return sep.join(path.split(sep)[:-backward]) + return path + + +def times_by_key(times, forward, backward): + by_key = {} + for test_name in times: + key = key_for(test_name, forward, backward) + if key in by_key: + by_key[key] += times[test_name] + else: + by_key[key] = times[test_name] + return by_key + + +def convert_trie_to_flat_paths(trie, prefix=None): + result = {} + for name, data in trie.iteritems(): + if prefix: + name = prefix + "/" + name + if isinstance(data, int): + result[name] = data + else: + result.update(convert_trie_to_flat_paths(data, name)) + + return result diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times_unittest.py new file mode 100755 index 00000000000..4409c86d5eb --- /dev/null +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times_unittest.py @@ -0,0 +1,109 @@ +# Copyright (C) 2013 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: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. + +import webkitpy.thirdparty.unittest2 as unittest + +from webkitpy.common.host_mock import MockHost +from webkitpy.layout_tests.print_layout_test_times import main + + +class PrintLayoutTestTimesTest(unittest.TestCase): + + def check(self, args, expected_output, files=None): + host = MockHost() + fs = host.filesystem + results_directory = host.port_factory.get().results_directory() + if files: + fs.files = files + else: + fs.write_text_file(fs.join(results_directory, 'times_ms.json'), """ + {"foo": {"foo1": {"fast1.html": 10, + "fast2.html": 10, + "slow1.html": 80}, + "foo2": {"fast3.html": 10, + "fast4.html": 10, + "slow2.html": 80}}, + "bar": {"bar1": {"fast5.html": 10, + "fast6.html": 10, + "slow3.html": 80}}} + """) + main(host, args) + self.assertEqual(host.stdout.getvalue(), expected_output) + + def test_fastest_overall(self): + # This is the fastest 10% of the tests overall (ignoring dir structure, equivalent to -f 0). + self.check(['--fastest', '10'], + "bar/bar1/fast5.html 10\n" + "bar/bar1/fast6.html 10\n" + "foo/foo1/fast1.html 10\n") + + def test_fastest_forward_1(self): + # Note that we don't get anything from foo/foo2, as foo/foo1 used up the budget for foo. + self.check(['-f', '1', '--fastest', '10'], + "bar/bar1/fast5.html 10\n" + "foo/foo1/fast1.html 10\n" + "foo/foo1/fast2.html 10\n") + + def test_fastest_back_1(self): + # Here we get one test from each dir, showing that we are going properly breadth-first. + self.check(['-b', '1', '--fastest', '10'], + "bar/bar1/fast5.html 10\n" + "foo/foo1/fast1.html 10\n" + "foo/foo2/fast3.html 10\n") + + def test_no_args(self): + # This should be every test, sorted lexicographically. + self.check([], + "bar/bar1/fast5.html 10\n" + "bar/bar1/fast6.html 10\n" + "bar/bar1/slow3.html 80\n" + "foo/foo1/fast1.html 10\n" + "foo/foo1/fast2.html 10\n" + "foo/foo1/slow1.html 80\n" + "foo/foo2/fast3.html 10\n" + "foo/foo2/fast4.html 10\n" + "foo/foo2/slow2.html 80\n") + + def test_total(self): + self.check(['-f', '0'], "300\n") + + def test_forward_one(self): + self.check(['-f', '1'], + "bar 100\n" + "foo 200\n") + + def test_backward_one(self): + self.check(['-b', '1'], + "bar/bar1 100\n" + "foo/foo1 100\n" + "foo/foo2 100\n") + + def test_path_to_file(self): + # Tests that we can use a custom file rather than the port's default. + self.check(['/tmp/times_ms.json'], "foo/bar.html 1\n", + files={'/tmp/times_ms.json': '{"foo":{"bar.html": 1}}'}) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index 0d8578a36aa..1f22807b5ab 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py @@ -31,12 +31,12 @@ import logging import optparse import os -import signal import sys import traceback from webkitpy.common.host import Host from webkitpy.layout_tests.controllers.manager import Manager +from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS from webkitpy.layout_tests.port import configuration_options, platform_options from webkitpy.layout_tests.views import buildbot_results from webkitpy.layout_tests.views import printing @@ -45,9 +45,6 @@ from webkitpy.layout_tests.views import printing _log = logging.getLogger(__name__) -# This mirrors what the shell normally does. -INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128 - # This is a randomly chosen exit code that can be tested against to # indicate that an unexpected exception occurred. EXCEPTIONAL_EXIT_STATUS = 254 @@ -78,11 +75,12 @@ def main(argv, stdout, stderr): try: run_details = run(port, options, args, stderr) - if run_details.exit_code != -1: + if run_details.exit_code != -1 and not run_details.initial_results.keyboard_interrupted: bot_printer = buildbot_results.BuildBotPrinter(stdout, options.debug_rwt_logging) bot_printer.print_results(run_details) return run_details.exit_code + # We need to still handle KeyboardInterrupt, atleast for webkitpy unittest cases. except KeyboardInterrupt: return INTERRUPTED_EXIT_STATUS except BaseException as e: @@ -99,14 +97,15 @@ def parse_args(args): option_group_definitions.append(("Configuration options", configuration_options())) option_group_definitions.append(("Printing Options", printing.print_options())) - # FIXME: These options should move onto the ChromiumPort. - option_group_definitions.append(("Chromium-specific Options", [ - optparse.make_option("--nocheck-sys-deps", action="store_true", - default=False, - help="Don't check the system dependencies (themes)"), + option_group_definitions.append(("Android-specific Options", [ optparse.make_option("--adb-device", action="append", default=[], help="Run Android layout tests on these devices."), + + # FIXME: Flip this to be off by default once we can log the device setup more cleanly. + optparse.make_option("--no-android-logging", + action="store_false", dest='android_logging', default=True, + help="Do not log android-specific debug messages (default is to log as part of --debug-rwt-logging"), ])) option_group_definitions.append(("Results Options", [ @@ -166,6 +165,10 @@ def parse_args(args): help="Show all failures in results.html, rather than only regressions"), optparse.make_option("--clobber-old-results", action="store_true", default=False, help="Clobbers test results from previous runs."), + optparse.make_option("--smoke", action="store_true", + help="Run just the SmokeTests"), + optparse.make_option("--no-smoke", dest="smoke", action="store_false", + help="Do not run just the SmokeTests"), ])) option_group_definitions.append(("Testing Options", [ @@ -177,6 +180,9 @@ def parse_args(args): optparse.make_option("-n", "--dry-run", action="store_true", default=False, help="Do everything but actually run the tests or upload results."), + optparse.make_option("--nocheck-sys-deps", action="store_true", + default=False, + help="Don't check the system dependencies (themes)"), optparse.make_option("--wrapper", help="wrapper command to insert before invocations of " "the driver; option is split on whitespace before " @@ -193,7 +199,7 @@ def parse_args(args): "option ('layout' or 'deps').")), optparse.make_option("--test-list", action="append", help="read list of tests to run from file", metavar="FILE"), - optparse.make_option("--skipped", action="store", default="default", + optparse.make_option("--skipped", action="store", default=None, help=("control how tests marked SKIP are run. " "'default' == Skip tests unless explicitly listed on the command line, " "'ignore' == Run them anyway, " @@ -216,7 +222,7 @@ def parse_args(args): help=("Run a the tests in batches (n), after every n tests, " "the driver is relaunched."), type="int", default=None), optparse.make_option("--run-singly", action="store_true", - default=False, help="run a separate driver for each test (implies --verbose)"), + default=False, help="DEPRECATED, same as --batch-size=1 --verbose"), optparse.make_option("--child-processes", help="Number of drivers to run in parallel."), # FIXME: Display default number of child processes that will run. @@ -236,11 +242,6 @@ def parse_args(args): dest="retry_failures", help="Don't re-try any tests that produce unexpected results."), - # FIXME: Remove this after we remove the flag from the v8 bot. - optparse.make_option("--retry-crashes", action="store_true", - default=False, - help="ignored (we now always retry crashes when we retry failures)."), - optparse.make_option("--max-locked-shards", type="int", default=0, help="Set the maximum number of locked shards"), optparse.make_option("--additional-env-var", type="string", action="append", default=[], @@ -249,6 +250,8 @@ def parse_args(args): help="Output per-test profile information."), optparse.make_option("--profiler", action="store", help="Output per-test profile information, using the specified profiler."), + optparse.make_option("--driver-logging", action="store_true", + help="Print detailed logging of the driver/content_shell"), ])) option_group_definitions.append(("Miscellaneous Options", [ @@ -283,7 +286,7 @@ def parse_args(args): return option_parser.parse_args(args) -def _set_up_derived_options(port, options): +def _set_up_derived_options(port, options, args): """Sets the options values that depend on other options values.""" if not options.child_processes: options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES", @@ -330,8 +333,24 @@ def _set_up_derived_options(port, options): options.pixel_test_directories = list(varified_dirs) if options.run_singly: + options.batch_size = 1 options.verbose = True + if not args and not options.test_list and options.smoke is None: + options.smoke = port.default_smoke_test_only() + if options.smoke: + if not args and not options.test_list and options.retry_failures is None: + # Retry failures by default if we're doing just a smoke test (no additional tests). + options.retry_failures = True + + if not options.test_list: + options.test_list = [] + options.test_list.append(port.host.filesystem.join(port.layout_tests_dir(), 'SmokeTests')) + if not options.skipped: + options.skipped = 'always' + + if not options.skipped: + options.skipped = 'default' def run(port, options, args, logging_stream): logger = logging.getLogger() @@ -340,7 +359,7 @@ def run(port, options, args, logging_stream): try: printer = printing.Printer(port, options, logging_stream, logger=logger) - _set_up_derived_options(port, options) + _set_up_derived_options(port, options, args) manager = Manager(port, options, printer) printer.print_config(port.results_directory()) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py index 54b928dfc74..3b36471ec0b 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py @@ -50,6 +50,7 @@ from webkitpy.common.host_mock import MockHost from webkitpy.layout_tests import port from webkitpy.layout_tests import run_webkit_tests +from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS from webkitpy.layout_tests.port import Port from webkitpy.layout_tests.port import test from webkitpy.test.skip import skip_if @@ -116,8 +117,8 @@ def run_and_capture(port_obj, options, parsed_args, shared_port=True): return (run_details, logging_stream) -def get_tests_run(args, host=None): - results = get_test_results(args, host) +def get_tests_run(args, host=None, port_obj=None): + results = get_test_results(args, host=host, port_obj=port_obj) return [result.test_name for result in results] @@ -136,11 +137,11 @@ def get_test_batches(args, host=None): return batches -def get_test_results(args, host=None): +def get_test_results(args, host=None, port_obj=None): options, parsed_args = parse_args(args, tests_included=True) host = host or MockHost() - port_obj = host.port_factory.get(port_name=options.platform, options=options) + port_obj = port_obj or host.port_factory.get(port_name=options.platform, options=options) oc = outputcapture.OutputCapture() oc.capture_output() @@ -288,21 +289,15 @@ class RunTest(unittest.TestCase, StreamTestingMixin): self.assertEqual(details.exit_code, 0) self.assertEqual(len(host.user.opened_urls), 1) - def test_hung_thread(self): - details, err, _ = logging_run(['--run-singly', '--time-out-ms=50', 'failures/expected/hang.html'], tests_included=True) - # Note that hang.html is marked as WontFix and all WontFix tests are - # expected to Pass, so that actually running them generates an "unexpected" error. - self.assertEqual(details.exit_code, 1) - self.assertNotEmpty(err) - def test_keyboard_interrupt(self): # Note that this also tests running a test marked as SKIP if # you specify it explicitly. - self.assertRaises(KeyboardInterrupt, logging_run, ['failures/expected/keyboard.html', '--child-processes', '1'], tests_included=True) + details, _, _ = logging_run(['failures/expected/keyboard.html', '--child-processes', '1'], tests_included=True) + self.assertEqual(details.exit_code, INTERRUPTED_EXIT_STATUS) if self.should_test_processes: - self.assertRaises(KeyboardInterrupt, logging_run, - ['failures/expected/keyboard.html', 'passes/text.html', '--child-processes', '2', '--skipped=ignore'], tests_included=True, shared_port=False) + _, regular_output, _ = logging_run(['failures/expected/keyboard.html', 'passes/text.html', '--child-processes', '2', '--skipped=ignore'], tests_included=True, shared_port=False) + self.assertTrue(any(['Interrupted, exiting' in line for line in regular_output.buflist])) def test_no_tests_found(self): details, err, _ = logging_run(['resources'], tests_included=True) @@ -442,10 +437,6 @@ class RunTest(unittest.TestCase, StreamTestingMixin): has_passes_text = has_passes_text or ('passes/text.html' in batch) self.assertTrue(has_passes_text) - def test_run_singly_actually_runs_tests(self): - details, _, _ = logging_run(['--run-singly'], tests_included=True) - self.assertEqual(details.exit_code, test.UNEXPECTED_FAILURES - 1) # failures/expected/hang.html actually passes w/ --run-singly. - def test_single_file(self): tests_run = get_tests_run(['passes/text.html']) self.assertEqual(tests_run, ['passes/text.html']) @@ -482,6 +473,37 @@ class RunTest(unittest.TestCase, StreamTestingMixin): tests_run = get_tests_run(['--test-list=%s' % filename], host=host) self.assertEqual(['passes/text.html'], tests_run) + def test_smoke_test(self): + host = MockHost() + smoke_test_filename = test.LAYOUT_TEST_DIR + '/SmokeTests' + host.filesystem.write_text_file(smoke_test_filename, 'passes/text.html\n') + + # Test the default smoke testing. + tests_run = get_tests_run(['--smoke'], host=host) + self.assertEqual(['passes/text.html'], tests_run) + + # Test running the smoke tests plus some manually-specified tests. + tests_run = get_tests_run(['--smoke', 'passes/image.html'], host=host) + self.assertEqual(['passes/image.html', 'passes/text.html'], tests_run) + + # Test running the smoke tests plus some manually-specified tests. + tests_run = get_tests_run(['--no-smoke', 'passes/image.html'], host=host) + self.assertEqual(['passes/image.html'], tests_run) + + # Test that we don't run just the smoke tests by default on a normal test port. + tests_run = get_tests_run([], host=host) + self.assertNotEqual(['passes/text.html'], tests_run) + + # Create a port that does run only the smoke tests by default, and verify that works as expected. + port_obj = host.port_factory.get('test') + port_obj.default_smoke_test_only = lambda: True + tests_run = get_tests_run([], host=host, port_obj=port_obj) + self.assertEqual(['passes/text.html'], tests_run) + + # Verify that --no-smoke continues to work on a smoke-by-default port. + tests_run = get_tests_run(['--no-smoke'], host=host, port_obj=port_obj) + self.assertNotEqual(['passes/text.html'], tests_run) + def test_missing_and_unexpected_results(self): # Test that we update expectations in place. If the expectation # is missing, update the expected generic location. @@ -601,7 +623,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin): host = MockHost() details, err, _ = logging_run(['--debug-rwt-logging', 'failures/unexpected'], tests_included=True, host=host) - self.assertEqual(details.exit_code, 16) + self.assertEqual(details.exit_code, 17) # FIXME: This should be a constant in test.py . self.assertTrue('Retrying' in err.getvalue()) def test_retrying_default_value_test_list(self): @@ -616,7 +638,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin): filename = '/tmp/foo.txt' host.filesystem.write_text_file(filename, 'failures') details, err, _ = logging_run(['--debug-rwt-logging', '--test-list=%s' % filename], tests_included=True, host=host) - self.assertEqual(details.exit_code, 16) + self.assertEqual(details.exit_code, 17) self.assertTrue('Retrying' in err.getvalue()) def test_retrying_and_flaky_tests(self): @@ -711,6 +733,11 @@ class RunTest(unittest.TestCase, StreamTestingMixin): self.assertEqual(results["num_regressions"], 5) self.assertEqual(results["num_flaky"], 0) + def test_reftest_crash(self): + test_results = get_test_results(['failures/unexpected/crash-reftest.html']) + # The list of references should be empty since the test crashed and we didn't run any references. + self.assertEqual(test_results[0].references, []) + def test_additional_platform_directory(self): self.assertTrue(passing_run(['--additional-platform-directory', '/tmp/foo'])) self.assertTrue(passing_run(['--additional-platform-directory', '/tmp/../foo'])) @@ -789,6 +816,13 @@ class RunTest(unittest.TestCase, StreamTestingMixin): self.assertTrue('text.html passed' in logging_stream.getvalue()) self.assertTrue('image.html passed' in logging_stream.getvalue()) + def disabled_test_driver_logging(self): + # FIXME: Figure out how to either use a mock-test port to + # get output or mack mock ports work again. + host = Host() + _, err, _ = logging_run(['--platform', 'mock-win', '--driver-logging', 'fast/harness/results.html'], + tests_included=True, host=host) + self.assertTrue('OUT:' in err.getvalue()) class EndToEndTest(unittest.TestCase): def test_reftest_with_two_notrefs(self): @@ -961,7 +995,7 @@ class MainTest(unittest.TestCase): try: run_webkit_tests.run = interrupting_run res = run_webkit_tests.main([], stdout, stderr) - self.assertEqual(res, run_webkit_tests.INTERRUPTED_EXIT_STATUS) + self.assertEqual(res, INTERRUPTED_EXIT_STATUS) run_webkit_tests.run = successful_run res = run_webkit_tests.main(['--platform', 'test'], stdout, stderr) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py index b723816763b..55b045f1775 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py @@ -415,6 +415,9 @@ class Printer(object): if self._options.debug_rwt_logging: self.writeln(msg) + def write_throttled_update(self, msg): + self._meter.write_throttled_update(msg) + def write_update(self, msg): self._meter.write_update(msg) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py index e510b7a7bb5..5e6aec7d170 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py @@ -176,7 +176,14 @@ class PerfTestsRunner(object): def run(self): needs_http = self._port.requires_http_server() - if not self._port.check_build(needs_http=needs_http): + class FakePrinter(object): + def write_update(self, msg): + print msg + + def write_throttled_update(self, msg): + pass + + if not self._port.check_build(needs_http=needs_http, printer=FakePrinter()): _log.error("Build not up to date for %s" % self._port._path_to_driver()) return self.EXIT_CODE_BAD_BUILD diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py index c6357da4b7b..f7cd3e5ef4f 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py @@ -38,7 +38,6 @@ import sys from checkers.common import categories as CommonCategories from checkers.common import CarriageReturnChecker from checkers.cpp import CppChecker -from checkers.cmake import CMakeChecker from checkers.jsonchecker import JSONChecker from checkers.png import PNGChecker from checkers.python import PythonChecker @@ -127,113 +126,7 @@ _PATH_RULES_SPECIFIER = [ # discipline as WebCore. ([# There is no clean way to avoid "yy_*" names used by flex. - "Source/WebCore/css/CSSParser.cpp", - # Qt code uses '_' in some places (such as private slots - # and on test xxx_data methos on tests) - "Source/JavaScriptCore/qt/", - "Source/WebKit/qt/tests/", - "Source/WebKit/qt/declarative/", - "Source/WebKit/qt/examples/"], - ["-readability/naming"]), - - ([# The Qt APIs use Qt declaration style, it puts the * to - # the variable name, not to the class. - "Source/WebKit/qt/Api/", - "Source/WebKit/qt/WidgetApi/"], - ["-readability/naming", - "-whitespace/declaration"]), - - ([# Qt's MiniBrowser has no config.h - "Tools/MiniBrowser/qt", - "Tools/MiniBrowser/qt/raw"], - ["-build/include"]), - - ([# The Qt APIs use Qt/QML naming style, which includes - # naming parameters in h files. - "Source/WebKit2/UIProcess/API/qt"], - ["-readability/parameter_name"]), - - ([# The GTK+ port uses the autotoolsconfig.h header in some C sources - # to serve the same purpose of config.h. - "Tools/GtkLauncher/main.c"], - ["-build/include_order"]), - - ([# The GTK+ APIs use GTK+ naming style, which includes - # lower-cased, underscore-separated values, whitespace before - # parens for function calls, and always having variable names. - # Also, GTK+ allows the use of NULL. - "Source/WebCore/bindings/scripts/test/GObject", - "Source/WebKit/gtk/webkit/", - "Tools/DumpRenderTree/gtk/"], - ["-readability/naming", - "-readability/parameter_name", - "-readability/null", - "-readability/enum_casing", - "-whitespace/parens"]), - - ([# The GTK+ API use upper case, underscore separated, words in - # certain types of enums (e.g. signals, properties). - "Source/WebKit2/UIProcess/API/gtk", - "Source/WebKit2/WebProcess/InjectedBundle/API/gtk"], - ["-readability/enum_casing"]), - - ([# Header files in ForwardingHeaders have no header guards or - # exceptional header guards (e.g., WebCore_FWD_Debugger_h). - "/ForwardingHeaders/"], - ["-build/header_guard"]), - ([# assembler has lots of opcodes that use underscores, so - # we don't check for underscores in that directory. - "Source/JavaScriptCore/assembler/", - "Source/JavaScriptCore/jit/JIT"], - ["-readability/naming/underscores"]), - ([# JITStubs has an usual syntax which causes false alarms for a few checks. - "JavaScriptCore/jit/JITStubs.cpp"], - ["-readability/parameter_name", - "-whitespace/parens"]), - - ([# The EFL APIs use EFL naming style, which includes - # both lower-cased and camel-cased, underscore-sparated - # values. - "Source/WebKit/efl/ewk/", - "Source/WebKit2/UIProcess/API/efl/"], - ["-readability/naming", - "-readability/parameter_name"]), - ([# EWebLauncher and MiniBrowser are EFL simple application. - # They need to use efl coding style and they don't have config.h. - "Tools/EWebLauncher/", - "Tools/MiniBrowser/efl/"], - ["-readability/naming", - "-readability/parameter_name", - "-whitespace/declaration", - "-build/include_order"]), - - # WebKit2 rules: - # WebKit2 and certain directories have idiosyncracies. - ([# NPAPI has function names with underscores. - "Source/WebKit2/WebProcess/Plugins/Netscape"], - ["-readability/naming"]), - ([# The WebKit2 C API has names with underscores and whitespace-aligned - # struct members. Also, we allow unnecessary parameter names in - # WebKit2 APIs because we're matching CF's header style. - # Additionally, we use word which starts with non-capital letter 'k' - # for types of enums. - "Source/WebKit2/UIProcess/API/C/", - "Source/WebKit2/Shared/API/c/", - "Source/WebKit2/WebProcess/InjectedBundle/API/c/"], - ["-readability/enum_casing", - "-readability/naming", - "-readability/parameter_name", - "-whitespace/declaration"]), - ([# These files define GObjects, which implies some definitions of - # variables and functions containing underscores. - "Source/WebCore/platform/graphics/clutter/GraphicsLayerActor.cpp", - "Source/WebCore/platform/graphics/clutter/GraphicsLayerActor.h", - "Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer1.cpp", - "Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp", - "Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp", - "Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp", - "Source/WebCore/platform/network/soup/ProxyResolverSoup.cpp", - "Source/WebCore/platform/network/soup/ProxyResolverSoup.h"], + "Source/core/css/CSSParser-in.cpp"], ["-readability/naming"]), # For third-party Python code, keep only the following checks-- @@ -248,27 +141,18 @@ _PATH_RULES_SPECIFIER = [ "+pep8/W291", # Trailing white space "+whitespace/carriage_return"]), - ([# glu's libtess is third-party code, and doesn't follow WebKit style. - "Source/ThirdParty/glu"], - ["-readability", - "-whitespace", - "-build/header_guard", - "-build/include_order"]), - - ([# There is no way to avoid the symbols __jit_debug_register_code - # and __jit_debug_descriptor when integrating with gdb. - "Source/JavaScriptCore/jit/GDBInterface.cpp"], - ["-readability/naming"]), - - ([# On some systems the trailing CR is causing parser failure. - "Source/JavaScriptCore/parser/Keywords.table"], - ["+whitespace/carriage_return"]), - ([# Jinja templates: files have .cpp or .h extensions, but contain # template code, which can't be handled, so disable tests. "Source/bindings/templates", "Source/core/scripts/templates"], ["-"]), + + ([# IDL compiler reference output + # Conforming to style significantly increases the complexity of the code + # generator and decreases *its* readability, which is of more concern + # than style of the machine-generated code itself. + "Source/bindings/tests/results"], + ["-"]), ] @@ -283,12 +167,9 @@ _JSON_FILE_EXTENSION = 'json' _PYTHON_FILE_EXTENSION = 'py' _TEXT_FILE_EXTENSIONS = [ - 'ac', 'cc', 'cgi', 'css', - 'exp', - 'flex', 'gyp', 'gypi', 'html', @@ -299,13 +180,9 @@ _TEXT_FILE_EXTENSIONS = [ 'php', 'pl', 'pm', - 'pri', - 'pro', 'rb', 'sh', - 'table', 'txt', - 'wm', 'xhtml', 'y', ] @@ -319,8 +196,6 @@ _XML_FILE_EXTENSIONS = [ _PNG_FILE_EXTENSION = 'png' -_CMAKE_FILE_EXTENSION = 'cmake' - # Files to skip that are less obvious. # # Some files should be skipped when checking style. For example, @@ -515,7 +390,6 @@ class FileType: # WATCHLIST = 7 XML = 8 XCODEPROJ = 9 - CMAKE = 10 class CheckerDispatcher(object): @@ -586,8 +460,6 @@ class CheckerDispatcher(object): return FileType.XCODEPROJ elif file_extension == _PNG_FILE_EXTENSION: return FileType.PNG - elif ((file_extension == _CMAKE_FILE_EXTENSION) or os.path.basename(file_path) == 'CMakeLists.txt'): - return FileType.CMAKE elif ((not file_extension and os.path.join("Tools", "Scripts") in file_path) or file_extension in _TEXT_FILE_EXTENSIONS or os.path.basename(file_path) == 'TestExpectations'): return FileType.TEXT @@ -613,8 +485,6 @@ class CheckerDispatcher(object): checker = XcodeProjectFileChecker(file_path, handle_style_error) elif file_type == FileType.PNG: checker = PNGChecker(file_path, handle_style_error) - elif file_type == FileType.CMAKE: - checker = CMakeChecker(file_path, handle_style_error) elif file_type == FileType.TEXT: basename = os.path.basename(file_path) if basename == 'TestExpectations': diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker_unittest.py index c19367fc2b9..1c588732051 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker_unittest.py @@ -213,43 +213,7 @@ class GlobalVariablesTest(unittest.TestCase): "build/include") assertCheck("random_path.cpp", "readability/naming") - assertNoCheck("Source/WebKit/gtk/webkit/webkit.h", - "readability/naming") - assertNoCheck("Tools/DumpRenderTree/gtk/DumpRenderTree.cpp", - "readability/null") - assertNoCheck("Source/WebKit/efl/ewk/ewk_view.h", - "readability/naming") - assertNoCheck("Source/WebCore/css/CSSParser.cpp", - "readability/naming") - - # Test if Qt exceptions are indeed working - assertCheck("Source/WebKit/qt/WidgetApi/qwebpage.cpp", - "readability/braces") - assertCheck("Source/WebKit/qt/tests/qwebelement/tst_qwebelement.cpp", - "readability/braces") - assertCheck("Source/WebKit/qt/declarative/platformplugin/WebPlugin.cpp", - "readability/braces") - assertCheck("Source/WebKit/qt/examples/platformplugin/WebPlugin.cpp", - "readability/braces") - assertNoCheck("Source/WebKit/qt/WidgetApi/qwebpage.cpp", - "readability/naming") - assertNoCheck("Source/WebKit/qt/tests/qwebelement/tst_qwebelement.cpp", - "readability/naming") - assertNoCheck("Source/WebKit/qt/declarative/platformplugin/WebPlugin.cpp", - "readability/naming") - assertNoCheck("Source/WebKit/qt/examples/platformplugin/WebPlugin.cpp", - "readability/naming") - - assertNoCheck("Tools/MiniBrowser/qt/UrlLoader.cpp", - "build/include") - - assertNoCheck("Source/WebKit2/UIProcess/API/qt", - "readability/parameter_name") - - assertNoCheck("Source/WebCore/ForwardingHeaders/debugger/Debugger.h", - "build/header_guard") - - assertNoCheck("Source/WebCore/platform/graphics/gstreamer/VideoSinkGStreamer.cpp", + assertNoCheck("Source/core/css/CSSParser-in.cpp", "readability/naming") # Third-party Python code: webkitpy/thirdparty @@ -260,13 +224,6 @@ class GlobalVariablesTest(unittest.TestCase): assertCheck(path, "pep8/W291") assertCheck(path, "whitespace/carriage_return") - # Test if the exception for GDBInterface.cpp is in place. - assertNoCheck("Source/JavaScriptCore/jit/GDBInterface.cpp", - "readability/naming") - - # Javascript keywords. - assertCheck("Source/JavaScriptCore/parser/Keywords.table", "whitespace/carriage_return") - def test_max_reports_per_category(self): """Check that _MAX_REPORTS_PER_CATEGORY is valid.""" all_categories = self._all_categories() @@ -499,12 +456,9 @@ class CheckerDispatcherDispatchTest(unittest.TestCase): def test_text_paths(self): """Test paths that should be checked as text.""" paths = [ - "foo.ac", "foo.cc", "foo.cgi", "foo.css", - "foo.exp", - "foo.flex", "foo.gyp", "foo.gypi", "foo.html", @@ -515,12 +469,9 @@ class CheckerDispatcherDispatchTest(unittest.TestCase): "foo.php", "foo.pl", "foo.pm", - "foo.pri", - "foo.pro", "foo.rb", "foo.sh", "foo.txt", - "foo.wm", "foo.xhtml", "foo.y", os.path.join("Source", "WebCore", "inspector", "front-end", "inspector.js"), diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake.py deleted file mode 100644 index 06b8929fab6..00000000000 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) 2012 Intel 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "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 THE COPYRIGHT -# OWNER 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. - - -"""Supports checking WebKit style in cmake files.(.cmake, CMakeLists.txt)""" - -import re - -from common import TabChecker - - -class CMakeChecker(object): - - """Processes CMake lines for checking style.""" - - # NO_SPACE_CMDS list are based on commands section of CMake document. - # Now it is generated from - # http://www.cmake.org/cmake/help/v2.8.10/cmake.html#section_Commands. - # Some commands are from default CMake modules such as pkg_check_modules. - # Please keep list in alphabet order. - # - # For commands in this list, spaces should not be added it and its - # parentheses. For eg, message("testing"), not message ("testing") - # - # The conditional commands like if, else, endif, foreach, endforeach, - # while, endwhile and break are listed in ONE_SPACE_CMDS - NO_SPACE_CMDS = [ - 'add_custom_command', 'add_custom_target', 'add_definitions', - 'add_dependencies', 'add_executable', 'add_library', - 'add_subdirectory', 'add_test', 'aux_source_directory', - 'build_command', - 'cmake_minimum_required', 'cmake_policy', 'configure_file', - 'create_test_sourcelist', - 'define_property', - 'enable_language', 'enable_testing', 'endfunction', 'endmacro', - 'execute_process', 'export', - 'file', 'find_file', 'find_library', 'find_package', 'find_path', - 'find_program', 'fltk_wrap_ui', 'function', - 'get_cmake_property', 'get_directory_property', - 'get_filename_component', 'get_property', 'get_source_file_property', - 'get_target_property', 'get_test_property', - 'include', 'include_directories', 'include_external_msproject', - 'include_regular_expression', 'install', - 'link_directories', 'list', 'load_cache', 'load_command', - 'macro', 'mark_as_advanced', 'math', 'message', - 'option', - #From FindPkgConfig.cmake - 'pkg_check_modules', - 'project', - 'qt_wrap_cpp', 'qt_wrap_ui', - 'remove_definitions', 'return', - 'separate_arguments', 'set', 'set_directory_properties', 'set_property', - 'set_source_files_properties', 'set_target_properties', - 'set_tests_properties', 'site_name', 'source_group', 'string', - 'target_link_libraries', 'try_compile', 'try_run', - 'unset', - 'variable_watch', - ] - - # CMake conditional commands, require one space between command and - # its parentheses, such as "if (", "foreach (", etc. - ONE_SPACE_CMDS = [ - 'if', 'else', 'elseif', 'endif', - 'foreach', 'endforeach', - 'while', 'endwhile', - 'break', - ] - - def __init__(self, file_path, handle_style_error): - self._handle_style_error = handle_style_error - self._tab_checker = TabChecker(file_path, handle_style_error) - - def check(self, lines): - self._tab_checker.check(lines) - self._num_lines = len(lines) - for l in xrange(self._num_lines): - self._process_line(l + 1, lines[l]) - - def _process_line(self, line_number, line_content): - if re.match('(^|\ +)#', line_content): - # ignore comment line - return - l = line_content.expandtabs(4) - # check command like message( "testing") - if re.search('\(\ +', l): - self._handle_style_error(line_number, 'whitespace/parentheses', 5, - 'No space after "("') - # check command like message("testing" ) - if re.search('\ +\)', l) and not re.search('^\ +\)$', l): - self._handle_style_error(line_number, 'whitespace/parentheses', 5, - 'No space before ")"') - self._check_trailing_whitespace(line_number, l) - self._check_no_space_cmds(line_number, l) - self._check_one_space_cmds(line_number, l) - self._check_indent(line_number, line_content) - - def _check_trailing_whitespace(self, line_number, line_content): - line_content = line_content.rstrip('\n') # chr(10), newline - line_content = line_content.rstrip('\r') # chr(13), carriage return - line_content = line_content.rstrip('\x0c') # chr(12), form feed, ^L - stripped = line_content.rstrip() - if line_content != stripped: - self._handle_style_error(line_number, 'whitespace/trailing', 5, - 'No trailing spaces') - - def _check_no_space_cmds(self, line_number, line_content): - # check command like "SET (" or "Set(" - for t in self.NO_SPACE_CMDS: - self._check_non_lowercase_cmd(line_number, line_content, t) - if re.search('(^|\ +)' + t.lower() + '\ +\(', line_content): - msg = 'No space between command "' + t.lower() + '" and its parentheses, should be "' + t + '("' - self._handle_style_error(line_number, 'whitespace/parentheses', 5, msg) - - def _check_one_space_cmds(self, line_number, line_content): - # check command like "IF (" or "if(" or "if (" or "If ()" - for t in self.ONE_SPACE_CMDS: - self._check_non_lowercase_cmd(line_number, line_content, t) - if re.search('(^|\ +)' + t.lower() + '(\(|\ \ +\()', line_content): - msg = 'One space between command "' + t.lower() + '" and its parentheses, should be "' + t + ' ("' - self._handle_style_error(line_number, 'whitespace/parentheses', 5, msg) - - def _check_non_lowercase_cmd(self, line_number, line_content, cmd): - if re.search('(^|\ +)' + cmd + '\ *\(', line_content, flags=re.IGNORECASE) and \ - (not re.search('(^|\ +)' + cmd.lower() + '\ *\(', line_content)): - msg = 'Use lowercase command "' + cmd.lower() + '"' - self._handle_style_error(line_number, 'command/lowercase', 5, msg) - - def _check_indent(self, line_number, line_content): - #TODO (halton): add indent checking - pass diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake_unittest.py deleted file mode 100644 index eefc8f7f19f..00000000000 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake_unittest.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2012 Intel 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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. - -"""Unit test for cmake.py.""" - -import webkitpy.thirdparty.unittest2 as unittest - -from cmake import CMakeChecker - - -class CMakeCheckerTest(unittest.TestCase): - - """Tests CMakeChecker class.""" - - def test_init(self): - """Test __init__() method.""" - def _mock_handle_style_error(self): - pass - - checker = CMakeChecker("foo.cmake", _mock_handle_style_error) - self.assertEqual(checker._handle_style_error, _mock_handle_style_error) - - def test_check(self): - """Test check() method.""" - errors = [] - - def _mock_handle_style_error(line_number, category, confidence, - message): - error = (line_number, category, confidence, message) - errors.append(error) - - checker = CMakeChecker("foo.cmake", _mock_handle_style_error) - - lines = [ - '# This file is sample input for cmake_unittest.py and includes below problems:\n', - 'IF ()', - '\tmessage("Error line with Tab")\n', - ' message("Error line with endding spaces") \n', - ' message( "Error line with space after (")\n', - ' message("Error line with space before (" )\n', - ' MESSAGE("Error line with upper case non-condtional command")\n', - ' MESSage("Error line with upper case non-condtional command")\n', - ' message("correct message line")\n', - 'ENDif ()\n', - '\n', - 'if()\n', - 'endif ()\n', - '\n', - 'macro ()\n', - 'ENDMacro()\n', - '\n', - 'function ()\n', - 'endfunction()\n', - ] - checker.check(lines) - - self.maxDiff = None - self.assertEqual(errors, [ - (3, 'whitespace/tab', 5, 'Line contains tab character.'), - (2, 'command/lowercase', 5, 'Use lowercase command "if"'), - (4, 'whitespace/trailing', 5, 'No trailing spaces'), - (5, 'whitespace/parentheses', 5, 'No space after "("'), - (6, 'whitespace/parentheses', 5, 'No space before ")"'), - (7, 'command/lowercase', 5, 'Use lowercase command "message"'), - (8, 'command/lowercase', 5, 'Use lowercase command "message"'), - (10, 'command/lowercase', 5, 'Use lowercase command "endif"'), - (12, 'whitespace/parentheses', 5, 'One space between command "if" and its parentheses, should be "if ("'), - (15, 'whitespace/parentheses', 5, 'No space between command "macro" and its parentheses, should be "macro("'), - (16, 'command/lowercase', 5, 'Use lowercase command "endmacro"'), - (18, 'whitespace/parentheses', 5, 'No space between command "function" and its parentheses, should be "function("'), - ]) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py index e1543ce6de2..806f9413470 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py @@ -3305,8 +3305,8 @@ def check_identifier_name_in_declaration(filename, line_number, line, file_state the state of things in the file. error: The function to call with any errors found. """ - # We don't check a return statement. - if match(r'\s*(return|delete)\b', line): + # We don't check return and delete statements and conversion operator declarations. + if match(r'\s*(return|delete|operator)\b', line): return # Basically, a declaration is a type name followed by whitespaces diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py index 4e22dc3b2b2..ab53f175ab6 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py @@ -4999,6 +4999,9 @@ class WebKitStyleTest(CppStyleTestBase): self.assert_lint('OwnPtr<uint32_t> under_score(new uint32_t);', 'under_score' + name_underscore_error_message) + # Conversion operator declaration. + self.assert_lint('operator int64_t();', '') + def test_parameter_names(self): # Leave meaningless variable names out of function declarations. meaningless_variable_name_error_message = 'The parameter name "%s" adds no information, so it should be removed. [readability/parameter_name] [5]' diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py index 50b7b10c3a5..2aabb4ddcfb 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py @@ -22,6 +22,7 @@ """Supports checking WebKit style in Python files.""" +import os import re import sys @@ -73,12 +74,17 @@ class PythonChecker(object): def _run_pylint(self, path): wkf = WebKitFinder(FileSystem()) executive = Executive() + env = os.environ.copy() + env['PYTHONPATH'] = ('%s%s%s' % (wkf.path_from_webkit_base('Tools', 'Scripts'), + os.pathsep, + wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'thirdparty'))) return executive.run_command([sys.executable, wkf.path_from_depot_tools_base('pylint.py'), '--output-format=parseable', '--errors-only', '--rcfile=' + wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'pylintrc'), path], - error_handler=executive.ignore_error) + env=env, + error_handler=executive.ignore_error) def _parse_pylint_output(self, output): # We filter out these messages because they are bugs in pylint that produce false positives. diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py index 89c78c67234..087147eabb7 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py @@ -99,7 +99,7 @@ class PrintExpectationsTest(unittest.TestCase): def test_paths(self): self.run_test([], - ('LayoutTests/TestExpectations\n' + ('/mock-checkout/LayoutTests/TestExpectations\n' 'LayoutTests/platform/test/TestExpectations\n' 'LayoutTests/platform/test-win-xp/TestExpectations\n'), paths=True) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py index 214395fdd17..a4a952b6e60 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py @@ -32,6 +32,8 @@ import optparse import re import sys import time +import traceback +import urllib import urllib2 from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer @@ -39,7 +41,7 @@ from webkitpy.common.memoized import memoized from webkitpy.common.system.executive import ScriptError from webkitpy.layout_tests.controllers.test_result_writer import TestResultWriter from webkitpy.layout_tests.models import test_failures -from webkitpy.layout_tests.models.test_expectations import TestExpectations, BASELINE_SUFFIX_LIST +from webkitpy.layout_tests.models.test_expectations import TestExpectations, BASELINE_SUFFIX_LIST, SKIP from webkitpy.layout_tests.port import builders from webkitpy.layout_tests.port import factory from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand @@ -150,6 +152,11 @@ class CopyExistingBaselinesInternal(BaseInternalRebaselineCommand): _log.debug("Existing baseline at %s, not copying over it." % new_baseline) continue + expectations = TestExpectations(port, [test_name]) + if SKIP in expectations.get_expectations(test_name): + _log.debug("%s is skipped on %s." % (test_name, port.name())) + continue + old_baselines.append(old_baseline) new_baselines.append(new_baseline) @@ -399,6 +406,9 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): self._run_webkit_patch(['optimize-baselines', '--suffixes', ','.join(all_suffixes), test], verbose) def _update_expectations_files(self, lines_to_remove): + # FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations + # objects and (re-)writing the actual expectations file N times, for each test we update. + # We should be able to update everything in memory, once, and then write the file out a single time. for test in lines_to_remove: for builder in lines_to_remove[test]: port = self._tool.port_factory.get_from_builder_name(builder) @@ -409,6 +419,27 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): expectationsString = expectations.remove_configuration_from_test(test, test_configuration) self._tool.filesystem.write_text_file(path, expectationsString) + for port_name in self._tool.port_factory.all_port_names(): + port = self._tool.port_factory.get(port_name) + generic_expectations = TestExpectations(port, tests=[test], include_overrides=False) + if self._port_skips_test(port, test, generic_expectations): + for test_configuration in port.all_test_configurations(): + if test_configuration.version == port.test_configuration().version: + expectationsString = generic_expectations.remove_configuration_from_test(test, test_configuration) + generic_path = port.path_to_generic_test_expectations_file() + self._tool.filesystem.write_text_file(generic_path, expectationsString) + + def _port_skips_test(self, port, test, generic_expectations): + fs = port.host.filesystem + if port.default_smoke_test_only(): + smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests') + if fs.exists(smoke_test_filename) and test not in fs.read_text_file(smoke_test_filename): + return True + + full_expectations = TestExpectations(port, tests=[test], include_overrides=True) + return (SKIP in full_expectations.get_expectations(test) and + SKIP not in generic_expectations.get_expectations(test)) + def _run_in_parallel_and_update_scm(self, commands): command_results = self._tool.executive.run_in_parallel(commands) log_output = '\n'.join(result[2] for result in command_results).replace('\n\n', '\n') @@ -429,8 +460,10 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): _log.debug(" %s: %s" % (builder, ",".join(suffixes))) copy_baseline_commands, rebaseline_commands = self._rebaseline_commands(test_prefix_list, options) - self._run_in_parallel_and_update_scm(copy_baseline_commands) - self._run_in_parallel_and_update_scm(rebaseline_commands) + if copy_baseline_commands: + self._run_in_parallel_and_update_scm(copy_baseline_commands) + if rebaseline_commands: + self._run_in_parallel_and_update_scm(rebaseline_commands) if options.optimize: self._optimize_baselines(test_prefix_list, options.verbose) @@ -566,18 +599,74 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): self.no_optimize_option, # FIXME: Remove this option. self.results_directory_option, + optparse.make_option("--log-server", help="Server to send logs to.") ]) - def latest_revision_processed_on_all_bots(self): + def _log_to_server(self, log_server, query): + if not log_server: + return + urllib2.urlopen("http://" + log_server + "/updatelog", data=urllib.urlencode(query)) + + # Logs when there are no NeedsRebaseline lines in TestExpectations. + # These entries overwrite the existing log entry if the existing + # entry is also a noneedsrebaseline entry. This is special cased + # so that the log doesn't get bloated with entries like this + # when there are no tests that needs rebaselining. + def _log_no_needs_rebaseline_lines(self, log_server): + self._log_to_server(log_server, { + "noneedsrebaseline": "on", + }) + + # Uploaded log entries append to the existing entry unless the + # newentry flag is set. In that case it starts a new entry to + # start appending to. So, we need to call this on any fresh run + # that is going to end up logging stuff (i.e. any run that isn't + # a noneedsrebaseline run). + def _start_new_log_entry(self, log_server): + self._log_to_server(log_server, { + "log": "", + "newentry": "on", + }) + + def _configure_logging(self, log_server): + if not log_server: + return + + def _log_alias(query): + self._log_to_server(log_server, query) + + class LogHandler(logging.Handler): + def __init__(self): + logging.Handler.__init__(self) + self._records = [] + + # Since this does not have the newentry flag, it will append + # to the most recent log entry (i.e. the one created by + # _start_new_log_entry. + def emit(self, record): + _log_alias({ + "log": record.getMessage(), + }) + + handler = LogHandler() + _log.setLevel(logging.DEBUG) + handler.setLevel(logging.DEBUG) + _log.addHandler(handler) + + def bot_revision_data(self, log_server): revisions = [] for result in self.builder_data().values(): if result.run_was_interrupted(): - _log.error("Can't rebaseline. The latest run on %s did not complete." % builder_name) - return 0 - revisions.append(result.blink_revision()) - return int(min(revisions)) - - def tests_to_rebaseline(self, tool, min_revision, print_revisions): + self._start_new_log_entry(log_server) + _log.error("Can't rebaseline because the latest run on %s exited early." % result.builder_name()) + return [] + revisions.append({ + "builder": result.builder_name(), + "revision": result.blink_revision(), + }) + return revisions + + def tests_to_rebaseline(self, tool, min_revision, print_revisions, log_server): port = tool.port_factory.get() expectations_file_path = port.path_to_generic_test_expectations_file() @@ -585,10 +674,16 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): revision = None author = None bugs = set() + has_any_needs_rebaseline_lines = False for line in tool.scm().blame(expectations_file_path).split("\n"): if "NeedsRebaseline" not in line: continue + + if not has_any_needs_rebaseline_lines: + self._start_new_log_entry(log_server) + has_any_needs_rebaseline_lines = True + parsed_line = re.match("^(\S*)[^(]*\((\S*).*?([^ ]*)\ \[[^[]*$", line) commit_hash = parsed_line.group(1) @@ -615,7 +710,7 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): _log.info("Too many tests to rebaseline in one patch. Doing the first %d." % self.MAX_LINES_TO_REBASELINE) break - return tests, revision, author, bugs + return tests, revision, author, bugs, has_any_needs_rebaseline_lines def link_to_patch(self, revision): return "http://src.chromium.org/viewvc/blink?view=revision&revision=" + str(revision) @@ -663,9 +758,9 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): def tree_status(self): blink_tree_status_url = "http://blink-status.appspot.com/status" status = urllib2.urlopen(blink_tree_status_url).read().lower() - if status.find('closed') != -1 or status == 0: + if status.find('closed') != -1 or status == "0": return 'closed' - elif status.find('open') != -1 or status == 1: + elif status.find('open') != -1 or status == "1": return 'open' return 'unknown' @@ -678,25 +773,36 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): _log.error("Cannot proceed with working directory changes. Clean working directory first.") return - min_revision = self.latest_revision_processed_on_all_bots() - if not min_revision: + self._configure_logging(options.log_server) + + revision_data = self.bot_revision_data(options.log_server) + if not revision_data: + return + + min_revision = int(min([item["revision"] for item in revision_data])) + tests, revision, author, bugs, has_any_needs_rebaseline_lines = self.tests_to_rebaseline(tool, min_revision, print_revisions=options.verbose, log_server=options.log_server) + + if not has_any_needs_rebaseline_lines: + self._log_no_needs_rebaseline_lines(options.log_server) return if options.verbose: - _log.info("Bot min revision is %s." % min_revision) - - tests, revision, author, bugs = self.tests_to_rebaseline(tool, min_revision, print_revisions=options.verbose) - test_prefix_list, lines_to_remove = self.get_test_prefix_list(tests) + _log.info("Min revision across all bots is %s." % min_revision) + for item in revision_data: + _log.info("%s: r%s" % (item["builder"], item["revision"])) if not tests: _log.debug('No tests to rebaseline.') return - _log.info('Rebaselining %s for r%s by %s.' % (list(tests), revision, author)) if self.tree_status() == 'closed': _log.info('Cannot proceed. Tree is closed.') return + _log.info('Rebaselining %s for r%s by %s.' % (list(tests), revision, author)) + + test_prefix_list, lines_to_remove = self.get_test_prefix_list(tests) + try: old_branch_name = tool.scm().current_branch() tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME) @@ -712,6 +818,8 @@ class AutoRebaseline(AbstractParallelRebaselineCommand): tool.scm().commit_locally_with_message(self.commit_message(author, revision, bugs)) + # FIXME: Log the upload, pull and dcommit stdout/stderr to the log-server. + # FIXME: It would be nice if we could dcommit the patch without uploading, but still # go through all the precommit hooks. For rebaselines with lots of files, uploading # takes a long time and sometimes fails, but we don't want to commit if, e.g. the @@ -738,10 +846,14 @@ class RebaselineOMatic(AbstractDeclarativeCommand): def execute(self, options, args, tool): while True: - tool.executive.run_command(['git', 'pull']) - rebaseline_command = [tool.filesystem.join(tool.scm().checkout_root, 'Tools', 'Scripts', 'webkit-patch'), 'auto-rebaseline'] - if options.verbose: - rebaseline_command.append('--verbose') - # Use call instead of run_command so that stdout doesn't get swallowed. - tool.executive.call(rebaseline_command) + try: + tool.executive.run_command(['git', 'pull']) + rebaseline_command = [tool.filesystem.join(tool.scm().checkout_root, 'Tools', 'Scripts', 'webkit-patch'), 'auto-rebaseline', '--log-server', 'blinkrebaseline.appspot.com'] + if options.verbose: + rebaseline_command.append('--verbose') + # Use call instead of run_command so that stdout doesn't get swallowed. + tool.executive.call(rebaseline_command) + except: + traceback.print_exc(file=sys.stderr) + time.sleep(self.SLEEP_TIME_IN_SECONDS) diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py index 9602cf0bee9..cf8aa8a4c3b 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py @@ -51,9 +51,10 @@ class _BaseTestCase(unittest.TestCase): self.lion_port = self.tool.port_factory.get_from_builder_name("WebKit Mac10.7") self.lion_expectations_path = self.lion_port.path_to_generic_test_expectations_file() - # FIXME: we should override builders._exact_matches here to point to a set - # of test ports and restore the value in tearDown(), and that way the - # individual tests wouldn't have to worry about it. + # FIXME: crbug.com/279494. We should override builders._exact_matches + # here to point to a set of test ports and restore the value in + # tearDown(), and that way the individual tests wouldn't have to worry + # about it. def _expand(self, path): if self.tool.filesystem.isabs(path): @@ -88,12 +89,13 @@ class _BaseTestCase(unittest.TestCase): } } });""") + # FIXME: crbug.com/279494 - we shouldn't be mixing mock and real builder names. for builder in ['MOCK builder', 'MOCK builder (Debug)', 'WebKit Mac10.7']: self.command._builder_data[builder] = data class TestCopyExistingBaselinesInternal(_BaseTestCase): - command_constructor = CopyExistingBaselinesInternal # AKA webkit-patch rebaseline-test-internal + command_constructor = CopyExistingBaselinesInternal def setUp(self): super(TestCopyExistingBaselinesInternal, self).setUp() @@ -101,7 +103,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): def test_copying_overwritten_baseline(self): self.tool.executive = MockExecutive2() - # FIXME: it's confusing that this is the test- port, and not the regular lion port. Really all of the tests should be using the test ports. + # FIXME: crbug.com/279494. it's confusing that this is the test- port, and not the regular lion port. Really all of the tests should be using the test ports. port = self.tool.port_factory.get('test-mac-snowleopard') self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-mac-snowleopard/failures/expected/image-expected.txt'), 'original snowleopard result') @@ -127,7 +129,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): def test_copying_overwritten_baseline_to_multiple_locations(self): self.tool.executive = MockExecutive2() - # FIXME: it's confusing that this is the test- port, and not the regular win port. Really all of the tests should be using the test ports. + # FIXME: crbug.com/279494. it's confusing that this is the test- port, and not the regular win port. Really all of the tests should be using the test ports. port = self.tool.port_factory.get('test-win-win7') self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') @@ -181,6 +183,37 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt'))) self.assertMultiLineEqual(out, '{"add": [], "remove-lines": []}\n') + def test_no_copy_skipped_test(self): + self.tool.executive = MockExecutive2() + + port = self.tool.port_factory.get('test-win-win7') + fs = self.tool.filesystem + self._write(fs.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') + expectations_path = fs.join(port.path_to_generic_test_expectations_file()) + self._write(expectations_path, ( + "[ Win ] failures/expected/image.html [ Failure ]\n" + "[ Linux ] failures/expected/image.html [ Skip ]\n")) + old_exact_matches = builders._exact_matches + oc = OutputCapture() + try: + builders._exact_matches = { + "MOCK Linux": {"port_name": "test-linux-x86_64", "specifiers": set(["mock-specifier"])}, + "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, + "MOCK Win7": {"port_name": "test-win-win7", "specifiers": set(["mock-specifier"])}, + } + + options = MockOptions(builder="MOCK Win7", suffixes="txt", verbose=True, test="failures/expected/image.html", results_directory=None) + + oc.capture_output() + self.command.execute(options, [], self.tool) + finally: + out, _, _ = oc.restore_output() + builders._exact_matches = old_exact_matches + + self.assertFalse(fs.exists(fs.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt'))) + self.assertEqual(self._read(fs.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), + 'original win7 result') + class TestRebaselineTest(_BaseTestCase): command_constructor = RebaselineTest # AKA webkit-patch rebaseline-test-internal @@ -306,6 +339,32 @@ class TestRebaselineJson(_BaseTestCase): builders._exact_matches = self.old_exact_matches super(TestRebaselineJson, self).tearDown() + def test_rebaseline_test_passes_on_all_builders(self): + self._setup_mock_builder_data() + + def builder_data(): + self.command._builder_data['MOCK builder'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ + "tests": { + "userscripts": { + "first-test.html": { + "expected": "NEEDSREBASELINE", + "actual": "PASS" + } + } + } +});""") + return self.command._builder_data + + self.command.builder_data = builder_data + + options = MockOptions(optimize=True, verbose=True, results_directory=None) + self._write("userscripts/first-test.html", "Dummy test contents") + self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder": ["txt", "png"]}}) + + # Note that we have one run_in_parallel() call followed by a run_command() + self.assertEqual(self.tool.executive.calls, + [['echo', '--verbose', 'optimize-baselines', '--suffixes', '', 'userscripts/first-test.html']]) + def test_rebaseline_all(self): self._setup_mock_builder_data() @@ -384,7 +443,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase): self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) new_expectations = self._read(self.lion_expectations_path) - self.assertMultiLineEqual(new_expectations, "Bug(x) [ MountainLion SnowLeopard ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n") + self.assertMultiLineEqual(new_expectations, "Bug(x) [ MountainLion Retina SnowLeopard ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n") def test_rebaseline_updates_expectations_file_all_platforms(self): options = MockOptions(optimize=False, verbose=True, results_directory=None) @@ -396,7 +455,62 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase): self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) new_expectations = self._read(self.lion_expectations_path) - self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux MountainLion SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") + self.assertMultiLineEqual(new_expectations, "Bug(x) [ Android Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") + + def test_rebaseline_handles_platform_skips(self): + # This test is just like test_rebaseline_updates_expectations_file_all_platforms(), + # except that if a particular port happens to SKIP a test in an overrides file, + # we count that as passing, and do not think that we still need to rebaseline it. + options = MockOptions(optimize=False, verbose=True, results_directory=None) + + self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") + self._write("NeverFixTests", "Bug(y) [ Android ] userscripts [ Skip ]\n") + self._write("userscripts/first-test.html", "Dummy test contents") + self._setup_mock_builder_data() + + self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) + + new_expectations = self._read(self.lion_expectations_path) + self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") + + def test_rebaseline_handles_skips_in_file(self): + # This test is like test_Rebaseline_handles_platform_skips, except that the + # Skip is in the same (generic) file rather than a platform file. In this case, + # the Skip line should be left unmodified. Note that the first line is now + # qualified as "[Linux Mac Win]"; if it was unqualified, it would conflict with + # the second line. + options = MockOptions(optimize=False, verbose=True, results_directory=None) + + self._write(self.lion_expectations_path, + ("Bug(x) [ Linux Mac Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n" + "Bug(y) [ Android ] userscripts/first-test.html [ Skip ]\n")) + self._write("userscripts/first-test.html", "Dummy test contents") + self._setup_mock_builder_data() + + self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) + + new_expectations = self._read(self.lion_expectations_path) + self.assertMultiLineEqual(new_expectations, + ("Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n" + "Bug(y) [ Android ] userscripts/first-test.html [ Skip ]\n")) + + def test_rebaseline_handles_smoke_tests(self): + # This test is just like test_rebaseline_handles_platform_skips, except that we check for + # a test not being in the SmokeTests file, instead of using overrides files. + # If a test is not part of the smoke tests, we count that as passing on ports that only + # run smoke tests, and do not think that we still need to rebaseline it. + options = MockOptions(optimize=False, verbose=True, results_directory=None) + + self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") + self._write("SmokeTests", "fast/html/article-element.html") + self._write("userscripts/first-test.html", "Dummy test contents") + self._setup_mock_builder_data() + + self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) + + new_expectations = self._read(self.lion_expectations_path) + self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") + class TestRebaseline(_BaseTestCase): @@ -591,7 +705,8 @@ class TestAutoRebaseline(_BaseTestCase): def setUp(self): super(TestAutoRebaseline, self).setUp() - self.command.latest_revision_processed_on_all_bots = lambda: 9000 + self.command.latest_revision_processed_on_all_bots = lambda log_server: 9000 + self.command.bot_revision_data = lambda log_server: [{"builder": "Mock builder", "revision": "9000"}] def test_tests_to_rebaseline(self): def blame(path): @@ -607,11 +722,12 @@ class TestAutoRebaseline(_BaseTestCase): self.tool.scm().blame = blame min_revision = 9000 - self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), ( + self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False, log_server=None), ( set(['path/to/rebaseline-without-bug-number.html', 'path/to/rebaseline-with-modifiers.html', 'path/to/rebaseline-without-modifiers.html']), 5678, 'foobarbaz1@chromium.org', - set(['24182', '234']))) + set(['24182', '234']), + True)) def test_tests_to_rebaseline_over_limit(self): def blame(path): @@ -626,11 +742,12 @@ class TestAutoRebaseline(_BaseTestCase): expected_list_of_tests.append("path/to/rebaseline-%s.html" % i) min_revision = 9000 - self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), ( + self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False, log_server=None), ( set(expected_list_of_tests), 5678, 'foobarbaz1@chromium.org', - set(['24182']))) + set(['24182']), + True)) def test_commit_message(self): author = "foo@chromium.org" @@ -661,7 +778,7 @@ TBR=foo@chromium.org """ self.tool.scm().blame = blame - self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) + self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False, log_server=None), [], self.tool) self.assertEqual(self.tool.executive.calls, []) def test_execute(self): @@ -684,6 +801,7 @@ TBR=foo@chromium.org return test_port return original_get(port_name, options, **kwargs) # Need to make sure all the ports grabbed use the test checkout path instead of the mock checkout path. + # FIXME: crbug.com/279494 - we shouldn't be doing this. self.tool.port_factory.get = get_test_port old_builder_data = self.command.builder_data @@ -738,13 +856,13 @@ crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] } self.command.tree_status = lambda: 'closed' - self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) + self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False, log_server=None), [], self.tool) self.assertEqual(self.tool.executive.calls, []) self.command.tree_status = lambda: 'open' self.tool.executive.calls = [] - self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) + self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False, log_server=None), [], self.tool) self.assertEqual(self.tool.executive.calls, [ [ ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-chocolate.html'], @@ -790,6 +908,7 @@ crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] return test_port return original_get(port_name, options, **kwargs) # Need to make sure all the ports grabbed use the test checkout path instead of the mock checkout path. + # FIXME: crbug.com/279494 - we shouldn't be doing this. self.tool.port_factory.get = get_test_port old_builder_data = self.command.builder_data @@ -826,7 +945,7 @@ Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] } self.command.tree_status = lambda: 'open' - self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) + self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False, log_server=None), [], self.tool) self.assertEqual(self.tool.executive.calls, [ [ ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'], diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter.py index f1f99b13423..5b102754d4d 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter.py @@ -32,28 +32,59 @@ import re from webkitpy.common.host import Host from webkitpy.common.webkit_finder import WebKitFinder -from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, Tag +from HTMLParser import HTMLParser _log = logging.getLogger(__name__) -class W3CTestConverter(object): +def convert_for_webkit(new_path, filename, host=Host()): + """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit. - def __init__(self): - self._host = Host() + Returns the list of modified properties and the modified text if the file was modifed, None otherwise.""" + contents = host.filesystem.read_binary_file(filename) + converter = _W3CTestConverter(new_path, filename, host) + if filename.endswith('.css'): + return converter.add_webkit_prefix_to_unprefixed_properties(contents) + else: + converter.feed(contents) + converter.close() + return converter.output() + + +class _W3CTestConverter(HTMLParser): + def __init__(self, new_path, filename, host=Host()): + HTMLParser.__init__(self) + + self._host = host self._filesystem = self._host.filesystem self._webkit_root = WebKitFinder(self._filesystem).webkit_base() + self.converted_data = [] + self.converted_properties = [] + self.in_style_tag = False + self.style_data = [] + self.filename = filename + + resources_path = self.path_from_webkit_root('LayoutTests', 'resources') + resources_relpath = self._filesystem.relpath(resources_path, new_path) + self.new_test_harness_path = resources_relpath + # These settings might vary between WebKit and Blink self._css_property_file = self.path_from_webkit_root('Source', 'core', 'css', 'CSSPropertyNames.in') self._css_property_split_string = 'alias_for=' self.prefixed_properties = self.read_webkit_prefixed_css_property_list() + self.test_harness_re = re.compile('/resources/testharness') + + self.prefixed_properties = self.read_webkit_prefixed_css_property_list() prop_regex = '([\s{]|^)(' + "|".join(prop.replace('-webkit-', '') for prop in self.prefixed_properties) + ')(\s+:|:)' self.prop_re = re.compile(prop_regex) + def output(self): + return (self.converted_properties, ''.join(self.converted_data)) + def path_from_webkit_root(self, *comps): return self._filesystem.abspath(self._filesystem.join(self._webkit_root, *comps)) @@ -78,104 +109,7 @@ class W3CTestConverter(object): # Ignore any prefixed properties for which an unprefixed version is supported return [prop for prop in prefixed_properties if prop not in unprefixed_properties] - def convert_for_webkit(self, new_path, filename): - """ Converts a file's |contents| so it will function correctly in its |new_path| in Webkit. - - Returns the list of modified properties and the modified text if the file was modifed, None otherwise.""" - contents = self._filesystem.read_binary_file(filename) - if filename.endswith('.css'): - return self.convert_css(contents, filename) - return self.convert_html(new_path, contents, filename) - - def convert_css(self, contents, filename): - return self.add_webkit_prefix_to_unprefixed_properties(contents, filename) - - def convert_html(self, new_path, contents, filename): - doc = BeautifulSoup(contents) - did_modify_paths = self.convert_testharness_paths(doc, new_path, filename) - converted_properties_and_content = self.convert_prefixed_properties(doc, filename) - return converted_properties_and_content if (did_modify_paths or converted_properties_and_content[0]) else None - - def convert_testharness_paths(self, doc, new_path, filename): - """ Update links to testharness.js in the BeautifulSoup |doc| to point to the copy in |new_path|. - - Returns whether the document was modified.""" - - # Look for the W3C-style path to any testharness files - scripts (.js) or links (.css) - pattern = re.compile('/resources/testharness') - script_tags = doc.findAll(src=pattern) - link_tags = doc.findAll(href=pattern) - testharness_tags = script_tags + link_tags - - if not testharness_tags: - return False - - resources_path = self.path_from_webkit_root('LayoutTests', 'resources') - resources_relpath = self._filesystem.relpath(resources_path, new_path) - - for tag in testharness_tags: - # FIXME: We need to handle img, audio, video tags also. - attr = 'src' - if tag.name != 'script': - attr = 'href' - - if not attr in tag.attrMap: - # FIXME: Figure out what to do w/ invalid tags. For now, we return False - # and leave the document unmodified, which means that it'll probably fail to run. - _log.error("Missing an attr in %s" % filename) - return False - - old_path = tag[attr] - new_tag = Tag(doc, tag.name, tag.attrs) - new_tag[attr] = re.sub(pattern, resources_relpath + '/testharness', old_path) - - self.replace_tag(tag, new_tag) - - return True - - def convert_prefixed_properties(self, doc, filename): - """ Searches a BeautifulSoup |doc| for any CSS properties requiring the -webkit- prefix and converts them. - - Returns the list of converted properties and the modified document as a string """ - - converted_properties = [] - - # Look for inline and document styles. - inline_styles = doc.findAll(style=re.compile('.*')) - style_tags = doc.findAll('style') - all_styles = inline_styles + style_tags - - for tag in all_styles: - - # Get the text whether in a style tag or style attribute. - style_text = '' - if tag.name == 'style': - if not tag.contents: - continue - style_text = tag.contents[0] - else: - style_text = tag['style'] - - updated_style_text = self.add_webkit_prefix_to_unprefixed_properties(style_text, filename) - - # Rewrite tag only if changes were made. - if updated_style_text[0]: - converted_properties.extend(list(updated_style_text[0])) - - new_tag = Tag(doc, tag.name, tag.attrs) - new_tag.insert(0, updated_style_text[1]) - - self.replace_tag(tag, new_tag) - - # FIXME: Doing the replace in the parsed document and then writing it back out - # is normalizing the HTML, which may in fact alter the intent of some tests. - # We should probably either just do basic string-replaces, or have some other - # way of flagging tests that are sensitive to being rewritten. - # https://bugs.webkit.org/show_bug.cgi?id=119159 - - return (converted_properties, doc.prettify()) - - def add_webkit_prefix_to_unprefixed_properties(self, text, filename): + def add_webkit_prefix_to_unprefixed_properties(self, text): """ Searches |text| for instances of properties requiring the -webkit- prefix and adds the prefix to them. Returns the list of converted properties and the modified text.""" @@ -193,9 +127,65 @@ class W3CTestConverter(object): _log.info(' converting %s', prop) # FIXME: Handle the JS versions of these properties and GetComputedStyle, too. - return (converted_properties, text) + return (converted_properties, ''.join(text_chunks)) + + def convert_style_data(self, data): + converted = self.add_webkit_prefix_to_unprefixed_properties(data) + if converted[0]: + self.converted_properties.extend(list(converted[0])) + return converted[1] + + def convert_attributes_if_needed(self, tag, attrs): + converted = self.get_starttag_text() + if tag in ('script', 'link'): + attr_name = 'src' + if tag != 'script': + attr_name = 'href' + for attr in attrs: + if attr[0] == attr_name: + new_path = re.sub(self.test_harness_re, self.new_test_harness_path + '/testharness', attr[1]) + converted = re.sub(attr[1], new_path, converted) + + for attr in attrs: + if attr[0] == 'style': + new_style = self.convert_style_data(attr[1]) + converted = re.sub(attr[1], new_style, converted) + + self.converted_data.append(converted) + + def handle_starttag(self, tag, attrs): + if tag == 'style': + self.in_style_tag = True + self.convert_attributes_if_needed(tag, attrs) + + def handle_endtag(self, tag): + if tag == 'style': + self.converted_data.append(self.convert_style_data(''.join(self.style_data))) + self.in_style_tag = False + self.style_data = [] + self.converted_data.extend(['</', tag, '>']) + + def handle_startendtag(self, tag, attrs): + self.convert_attributes_if_needed(tag, attrs) + + def handle_data(self, data): + if self.in_style_tag: + self.style_data.append(data) + else: + self.converted_data.append(data) + + def handle_entityref(self, name): + self.converted_data.extend(['&', name, ';']) + + def handle_charref(self, name): + self.converted_data.extend(['&#', name, ';']) + + def handle_comment(self, data): + self.converted_data.extend(['<!-- ', data, ' -->']) + + def handle_decl(self, decl): + self.converted_data.extend(['<!', decl, '>']) + + def handle_pi(self, data): + self.converted_data.extend(['<?', data, '>']) - def replace_tag(self, old_tag, new_tag): - index = old_tag.parent.contents.index(old_tag) - old_tag.parent.insert(index, new_tag) - old_tag.extract() diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter_unittest.py index a08896a212b..403ceff7797 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter_unittest.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter_unittest.py @@ -31,23 +31,29 @@ import os import re import webkitpy.thirdparty.unittest2 as unittest +from webkitpy.common.host import Host from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.common.webkit_finder import WebKitFinder from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup -from webkitpy.w3c.test_converter import W3CTestConverter - +from webkitpy.w3c.test_converter import _W3CTestConverter DUMMY_FILENAME = 'dummy.html' +DUMMY_PATH = 'dummy/testharness/path' class W3CTestConverterTest(unittest.TestCase): - def fake_dir_path(self, converter, dirname): - return converter.path_from_webkit_root("LayoutTests", "css", dirname) + # FIXME: When we move to using a MockHost, this method should be removed, since + # then we can just pass in a dummy dir path + def fake_dir_path(self, dirname): + filesystem = Host().filesystem + webkit_root = WebKitFinder(filesystem).webkit_base() + return filesystem.abspath(filesystem.join(webkit_root, "LayoutTests", "css", dirname)) def test_read_prefixed_property_list(self): """ Tests that the current list of properties requiring the -webkit- prefix load correctly """ # FIXME: We should be passing in a MockHost here ... - converter = W3CTestConverter() + converter = _W3CTestConverter(DUMMY_PATH, DUMMY_FILENAME) prop_list = converter.prefixed_properties self.assertTrue(prop_list, 'No prefixed properties found') @@ -72,16 +78,18 @@ CONTENT OF TEST </body> </html> """ - converter = W3CTestConverter() + converter = _W3CTestConverter(DUMMY_PATH, DUMMY_FILENAME) oc = OutputCapture() oc.capture_output() try: - converted = converter.convert_html('/nothing/to/convert', test_html, DUMMY_FILENAME) + converter.feed(test_html) + converter.close() + converted = converter.output() finally: oc.restore_output() - self.verify_no_conversion_happened(converted) + self.verify_no_conversion_happened(converted, test_html) def test_convert_for_webkit_harness_only(self): """ Tests convert_for_webkit() using a basic JS test that uses testharness.js only and has no prefixed properties """ @@ -91,10 +99,11 @@ CONTENT OF TEST <script src="/resources/testharness.js"></script> </head> """ - converter = W3CTestConverter() - fake_dir_path = self.fake_dir_path(converter, "harnessonly") - - converted = converter.convert_html(fake_dir_path, test_html, DUMMY_FILENAME) + fake_dir_path = self.fake_dir_path("harnessonly") + converter = _W3CTestConverter(fake_dir_path, DUMMY_FILENAME) + converter.feed(test_html) + converter.close() + converted = converter.output() self.verify_conversion_happened(converted) self.verify_test_harness_paths(converter, converted[1], fake_dir_path, 1, 1) @@ -118,14 +127,16 @@ CONTENT OF TEST </body> </html> """ - converter = W3CTestConverter() - fake_dir_path = self.fake_dir_path(converter, 'harnessandprops') + fake_dir_path = self.fake_dir_path('harnessandprops') + converter = _W3CTestConverter(fake_dir_path, DUMMY_FILENAME) test_content = self.generate_test_content(converter.prefixed_properties, 1, test_html) oc = OutputCapture() oc.capture_output() try: - converted = converter.convert_html(fake_dir_path, test_content[1], DUMMY_FILENAME) + converter.feed(test_content[1]) + converter.close() + converted = converter.output() finally: oc.restore_output() @@ -153,14 +164,16 @@ CONTENT OF TEST </body> </html> """ - converter = W3CTestConverter() - fake_dir_path = self.fake_dir_path(converter, 'harnessandprops') + fake_dir_path = self.fake_dir_path('harnessandprops') + converter = _W3CTestConverter(fake_dir_path, DUMMY_FILENAME) oc = OutputCapture() oc.capture_output() try: test_content = self.generate_test_content(converter.prefixed_properties, 2, test_html) - converted = converter.convert_html(fake_dir_path, test_content[1], DUMMY_FILENAME) + converter.feed(test_content[1]) + converter.close() + converted = converter.output() finally: oc.restore_output() @@ -177,20 +190,20 @@ CONTENT OF TEST <script src="/resources/testharnessreport.js"></script> </head> """ - converter = W3CTestConverter() + fake_dir_path = self.fake_dir_path('testharnesspaths') + converter = _W3CTestConverter(fake_dir_path, DUMMY_FILENAME) - fake_dir_path = self.fake_dir_path(converter, 'testharnesspaths') - - doc = BeautifulSoup(test_html) oc = OutputCapture() oc.capture_output() try: - converted = converter.convert_testharness_paths(doc, fake_dir_path, DUMMY_FILENAME) + converter.feed(test_html) + converter.close() + converted = converter.output() finally: oc.restore_output() self.verify_conversion_happened(converted) - self.verify_test_harness_paths(converter, doc, fake_dir_path, 2, 1) + self.verify_test_harness_paths(converter, converted[1], fake_dir_path, 2, 1) def test_convert_prefixed_properties(self): """ Tests convert_prefixed_properties() file that has 20 properties requiring the -webkit- prefix: @@ -255,14 +268,15 @@ CONTENT OF TEST ]]></style> </html> """ - converter = W3CTestConverter() - + converter = _W3CTestConverter(DUMMY_PATH, DUMMY_FILENAME) test_content = self.generate_test_content(converter.prefixed_properties, 20, test_html) oc = OutputCapture() oc.capture_output() try: - converted = converter.convert_prefixed_properties(BeautifulSoup(test_content[1]), DUMMY_FILENAME) + converter.feed(test_content[1]) + converter.close() + converted = converter.output() finally: oc.restore_output() @@ -272,8 +286,8 @@ CONTENT OF TEST def verify_conversion_happened(self, converted): self.assertTrue(converted, "conversion didn't happen") - def verify_no_conversion_happened(self, converted): - self.assertEqual(converted, None, 'test should not have been converted') + def verify_no_conversion_happened(self, converted, original): + self.assertEqual(converted[1], original, 'test should not have been converted') def verify_test_harness_paths(self, converter, converted, test_path, num_src_paths, num_href_paths): if isinstance(converted, basestring): diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py index 1f4e3d81af6..668a01baca7 100644 --- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py +++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py @@ -96,7 +96,7 @@ from webkitpy.common.host import Host from webkitpy.common.webkit_finder import WebKitFinder from webkitpy.common.system.executive import ScriptError from webkitpy.w3c.test_parser import TestParser -from webkitpy.w3c.test_converter import W3CTestConverter +from webkitpy.w3c.test_converter import convert_for_webkit TEST_STATUS_UNKNOWN = 'unknown' @@ -286,7 +286,6 @@ class TestImporter(object): 'reftests': reftests, 'jstests': jstests, 'total_tests': total_tests}) def import_tests(self): - converter = W3CTestConverter() total_imported_tests = 0 total_imported_reftests = 0 total_imported_jstests = 0 @@ -343,7 +342,7 @@ class TestImporter(object): # FIXME: Eventually, so should js when support is added for this type of conversion mimetype = mimetypes.guess_type(orig_filepath) if 'html' in str(mimetype[0]) or 'xml' in str(mimetype[0]) or 'css' in str(mimetype[0]): - converted_file = converter.convert_for_webkit(new_path, filename=orig_filepath) + converted_file = convert_for_webkit(new_path, filename=orig_filepath) if not converted_file: shutil.copyfile(orig_filepath, new_filepath) # The file was unmodified. |
