summaryrefslogtreecommitdiff
path: root/chromium/third_party/WebKit/Tools/Scripts/webkitpy
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2013-12-11 21:33:03 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-12-13 12:34:07 +0100
commitf2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch)
tree0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/third_party/WebKit/Tools/Scripts/webkitpy
parent5362912cdb5eea702b68ebe23702468d17c3017a (diff)
downloadqtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Tools/Scripts/webkitpy')
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py287
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/urls.py15
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot.py4
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot/chromiumbuildbot_unittest.py43
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive.py5
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/executive_mock.py8
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py7
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_mock.py6
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/systemhost_mock.py67
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py88
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py18
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py15
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py33
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py8
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py204
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py21
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py72
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py8
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py35
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py14
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py10
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py6
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py13
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py41
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py4
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py22
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py37
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py4
-rwxr-xr-xchromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times.py150
-rwxr-xr-xchromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_times_unittest.py109
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py57
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py76
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py146
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker_unittest.py51
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake.py150
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cmake_unittest.py90
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py4
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py8
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py164
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py153
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter.py204
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_converter_unittest.py72
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py5
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.