diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 11:40:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 12:42:11 +0000 |
commit | 5d87695f37678f96492b258bbab36486c59866b4 (patch) | |
tree | be9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/third_party/blink/tools/blinkpy | |
parent | 6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff) | |
download | qtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz |
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/third_party/blink/tools/blinkpy')
57 files changed, 1423 insertions, 651 deletions
diff --git a/chromium/third_party/blink/tools/blinkpy/bindings/bindings_tests.py b/chromium/third_party/blink/tools/blinkpy/bindings/bindings_tests.py index 8e7a42a56d4..6db448d87f2 100644 --- a/chromium/third_party/blink/tools/blinkpy/bindings/bindings_tests.py +++ b/chromium/third_party/blink/tools/blinkpy/bindings/bindings_tests.py @@ -34,6 +34,7 @@ from blinkpy.common.system.executive import Executive from blinkpy.common import path_finder path_finder.add_bindings_scripts_dir_to_sys_path() +path_finder.add_build_scripts_dir_to_sys_path() from code_generator_v8 import CodeGeneratorDictionaryImpl from code_generator_v8 import CodeGeneratorV8 @@ -47,6 +48,7 @@ from idl_compiler import (generate_bindings, generate_union_type_containers, generate_dictionary_impl, generate_callback_function_impl) +from json5_generator import Json5File from utilities import ComponentInfoProviderCore from utilities import ComponentInfoProviderModules from utilities import get_file_contents @@ -68,9 +70,9 @@ TBR=someone in third_party/blink/renderer/bindings/OWNERS or WATCHLISTS:bindings SOURCE_PATH = path_finder.get_source_dir() DEPENDENCY_IDL_FILES = frozenset([ - 'test_implements.idl', - 'test_implements_2.idl', - 'test_implements_3.idl', + 'test_interface_mixin.idl', + 'test_interface_mixin_2.idl', + 'test_interface_mixin_3.idl', 'test_interface_partial.idl', 'test_interface_partial_2.idl', 'test_interface_partial_3.idl', @@ -103,7 +105,7 @@ def TemporaryDirectory(): shutil.rmtree(name) -def generate_interface_dependencies(): +def generate_interface_dependencies(runtime_enabled_features): def idl_paths_recursive(directory): # This is slow, especially on Windows, due to os.walk making # excess stat() calls. Faster versions may appear in Python 3.5 or @@ -137,7 +139,7 @@ def generate_interface_dependencies(): # To avoid this issue, we need to clear relative_dir here. for value in info['interfaces_info'].itervalues(): value['relative_dir'] = '' - component_info = info_collector.get_component_info_as_dict() + component_info = info_collector.get_component_info_as_dict(runtime_enabled_features) return info, component_info # We compute interfaces info for *all* IDL files, not just test IDL @@ -275,8 +277,18 @@ def bindings_tests(output_directory, verbose, suppress_diff): return False return True + def make_runtime_features_dict(): + input_filename = os.path.join(TEST_INPUT_DIRECTORY, 'runtime_enabled_features.json5') + json5_file = Json5File.load_from_files([input_filename]) + features_map = {} + for feature in json5_file.name_dictionaries: + features_map[str(feature['name'])] = { + 'in_origin_trial': feature['in_origin_trial'] + } + return features_map + try: - generate_interface_dependencies() + generate_interface_dependencies(make_runtime_features_dict()) for component in COMPONENT_DIRECTORY: output_dir = os.path.join(output_directory, component) if not os.path.exists(output_dir): diff --git a/chromium/third_party/blink/tools/blinkpy/common/config/builders.json b/chromium/third_party/blink/tools/blinkpy/common/config/builders.json index 5c3e7c6016a..c38cd6bc555 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/config/builders.json +++ b/chromium/third_party/blink/tools/blinkpy/common/config/builders.json @@ -51,6 +51,14 @@ "port_name": "win-win7", "specifiers": ["Win7", "Debug"] }, + "Win10 Tests x64": { + "port_name": "win-win10", + "specifiers": ["Win10", "Release"] + }, + "Win10 Tests x64 (dbg)": { + "port_name": "win-win10", + "specifiers": ["Win10", "Debug"] + }, "linux-blink-rel": { "port_name": "linux-trusty", "specifiers": ["Trusty", "Release"], diff --git a/chromium/third_party/blink/tools/blinkpy/common/host_mock.py b/chromium/third_party/blink/tools/blinkpy/common/host_mock.py index 2131a01952a..ee20fed59a3 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/host_mock.py +++ b/chromium/third_party/blink/tools/blinkpy/common/host_mock.py @@ -36,6 +36,7 @@ from blinkpy.common.system.system_host_mock import MockSystemHost from blinkpy.web_tests.builder_list import BuilderList from blinkpy.web_tests.port.factory import PortFactory from blinkpy.web_tests.port.test import add_unit_tests_to_mock_filesystem +from blinkpy.w3c.wpt_manifest import BASE_MANIFEST_NAME class MockHost(MockSystemHost): @@ -116,6 +117,5 @@ class MockHost(MockSystemHost): external_dir = path_finder.path_from_web_tests('external') filesystem.maybe_make_directory(filesystem.join(external_dir, 'wpt')) - # This filename should match the constant BASE_MANIFEST_NAME. - manifest_base_path = filesystem.join(external_dir, 'WPT_BASE_MANIFEST_5.json') + manifest_base_path = filesystem.join(external_dir, BASE_MANIFEST_NAME) filesystem.files[manifest_base_path] = '{"manifest": "base"}' diff --git a/chromium/third_party/blink/tools/blinkpy/common/net/git_cl.py b/chromium/third_party/blink/tools/blinkpy/common/net/git_cl.py index 6b6fcbca004..0491eaaee89 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/net/git_cl.py +++ b/chromium/third_party/blink/tools/blinkpy/common/net/git_cl.py @@ -52,12 +52,22 @@ class GitCL(object): self._cwd = cwd self._git_executable_name = Git.find_executable_name(host.executive, host.platform) - def run(self, args): - """Runs git-cl with the given arguments and returns the output.""" + def run(self, args, return_stderr=False): + """Runs git-cl with the given arguments and returns the output. + + Args: + args: A list of arguments passed to `git cl`. + return_stderr: Whether to include stderr in the returned output (the + default is False because git-cl will show a warning when running + on Swarming bots with local git cache). + + Returns: + A string (the output from git-cl). + """ command = [self._git_executable_name, 'cl'] + args if self._auth_refresh_token_json and args[0] in _COMMANDS_THAT_TAKE_REFRESH_TOKEN: command += ['--auth-refresh-token-json', self._auth_refresh_token_json] - return self._host.executive.run_command(command, cwd=self._cwd) + return self._host.executive.run_command(command, cwd=self._cwd, return_stderr=return_stderr) def trigger_try_jobs(self, builders, bucket=None): """Triggers try jobs on the given builders. @@ -136,6 +146,7 @@ class GitCL(object): def closed_status_or_none(): status = self._get_cl_status() + _log.debug('CL status is: %s', status) if status == 'closed': self._host.print_('CL is closed.') return status diff --git a/chromium/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py b/chromium/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py index 6e302cbcde5..4567df12a29 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py +++ b/chromium/third_party/blink/tools/blinkpy/common/net/git_cl_mock.py @@ -3,6 +3,7 @@ # found in the LICENSE file. from blinkpy.common.net.git_cl import CLStatus, GitCL +from blinkpy.common.system.executive import ScriptError # pylint: disable=unused-argument @@ -10,7 +11,7 @@ class MockGitCL(object): def __init__( self, host, try_job_results=None, status='closed', - issue_number='1234', time_out=False): + issue_number='1234', time_out=False, git_error_output=None): """Constructs a fake GitCL with canned return values. Args: @@ -19,16 +20,21 @@ class MockGitCL(object): status: CL status string. issue_number: CL issue number as a string. time_out: Whether to simulate timing out while waiting. + git_error_output: A dict of git-cl args to exception output. """ self._builders = host.builders.all_try_builder_names() self._status = status self._try_job_results = try_job_results self._issue_number = issue_number self._time_out = time_out + self._git_error_output = git_error_output self.calls = [] def run(self, args): self.calls.append(['git', 'cl'] + args) + arg_key = "".join(args) + if self._git_error_output and arg_key in self._git_error_output.keys(): + raise ScriptError(output=self._git_error_output[arg_key]) return 'mock output' def trigger_try_jobs(self, builders, bucket=None): diff --git a/chromium/third_party/blink/tools/blinkpy/common/path_finder.py b/chromium/third_party/blink/tools/blinkpy/common/path_finder.py index 904b5c4f218..c829a09ba0c 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/path_finder.py +++ b/chromium/third_party/blink/tools/blinkpy/common/path_finder.py @@ -44,6 +44,12 @@ def add_bindings_scripts_dir_to_sys_path(): sys.path.insert(0, path_to_bindings_scripts) +def add_build_scripts_dir_to_sys_path(): + path_to_build_scripts = os.path.join(get_source_dir(), 'build', 'scripts') + if path_to_build_scripts not in sys.path: + sys.path.insert(0, path_to_build_scripts) + + def add_blinkpy_thirdparty_dir_to_sys_path(): path = get_blinkpy_thirdparty_dir() if path not in sys.path: @@ -148,6 +154,18 @@ class PathFinder(object): def path_from_web_tests(self, *comps): return self._filesystem.join(self.web_tests_dir(), *comps) + def strip_web_tests_path(self, wpt_test_abs_path): + web_tests_path = self.path_from_web_tests('') + if wpt_test_abs_path.startswith(web_tests_path): + return wpt_test_abs_path[len(web_tests_path):] + return wpt_test_abs_path + + def strip_webdriver_tests_path(self, wpt_webdriver_test_path): + webdriver_prefix = self._filesystem.join('external', 'wpt', 'webdriver', '') + if wpt_webdriver_test_path.startswith(webdriver_prefix): + return wpt_webdriver_test_path[len(webdriver_prefix):] + return wpt_webdriver_test_path + @memoized def depot_tools_base(self): """Returns the path to depot_tools, or None if not found. diff --git a/chromium/third_party/blink/tools/blinkpy/common/path_finder_unittest.py b/chromium/third_party/blink/tools/blinkpy/common/path_finder_unittest.py index 13baca96746..e2b96d3f87e 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/path_finder_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/common/path_finder_unittest.py @@ -67,3 +67,25 @@ class TestPathFinder(unittest.TestCase): finder = PathFinder(filesystem) self.assertEqual( finder.depot_tools_base(), '/checkout/third_party/depot_tools') + + def test_strip_web_tests_path(self): + finder = PathFinder(MockFileSystem()) + path_with_web_tests = '/mock-checkout/' + RELATIVE_WEB_TESTS + 'external/wpt' + self.assertEqual( + finder.strip_web_tests_path(path_with_web_tests), + 'external/wpt') + path_without_web_tests = '/checkout/' + RELATIVE_WEB_TESTS + 'external/wpt' + self.assertEqual( + finder.strip_web_tests_path(path_without_web_tests), + path_without_web_tests) + + def test_strip_webdriver_tests_path(self): + finder = PathFinder(MockFileSystem()) + path_with_webdriver_prefix = 'external/wpt/webdriver/' + 'foo/bar.py::test' + self.assertEqual( + finder.strip_webdriver_tests_path(path_with_webdriver_prefix), + 'foo/bar.py::test') + path_without_webdriver_prefix = 'external/wpt' + 'bar/foo.py::test' + self.assertEqual( + finder.strip_webdriver_tests_path(path_without_webdriver_prefix), + path_without_webdriver_prefix) diff --git a/chromium/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py b/chromium/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py index 7b728fa7c06..232bd655751 100644 --- a/chromium/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/common/pretty_diff_unittest.py @@ -30,8 +30,8 @@ class TestFileDiff(unittest.TestCase): 'similarity index 100%', 'rename from platform/modules/offscreencanvas/OWNERS', 'rename to platform/modules/frame_sinks/OWNERS', - 'diff --git a/platform/modules/frame_sinks/embedded_frame_sink.mojom ' + - 'b/platform/modules/frame_sinks/embedded_frame_sink.mojom'] + 'diff --git a/mojom/frame_sinks/embedded_frame_sink.mojom ' + + 'b/mojom/frame_sinks/embedded_frame_sink.mojom'] diff, remaining_lines = DiffFile.parse(lines) self.assertIsNotNone(diff) self.assertEquals(remaining_lines[0], lines[4]) diff --git a/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index ceb6c930d55..81c8a26a81c 100755 --- a/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py @@ -32,6 +32,7 @@ _CONFIG = [ # //base constructs that are allowed everywhere 'base::AdoptRef', 'base::AutoReset', + 'base::CreateSequencedTaskRunnerWithTraits', 'base::ElapsedTimer', 'base::File', 'base::FilePath', @@ -40,10 +41,10 @@ _CONFIG = [ 'base::MakeRefCounted', 'base::Optional', 'base::OptionalOrNullptr', + 'base::PlatformThread', 'base::PlatformThreadId', 'base::RefCountedData', 'base::RunLoop', - 'base::CreateSequencedTaskRunnerWithTraits', 'base::ReadOnlySharedMemoryMapping', 'base::ReadOnlySharedMemoryRegion', 'base::SequencedTaskRunner', @@ -56,6 +57,11 @@ _CONFIG = [ 'base::TimeDelta', 'base::TimeTicks', 'base::ThreadTicks', + 'base::trace_event::MemoryAllocatorDump', + 'base::trace_event::MemoryDumpArgs', + 'base::trace_event::MemoryDumpManager', + 'base::trace_event::MemoryDumpProvider', + 'base::trace_event::ProcessMemoryDump', 'base::UnguessableToken', 'base::UnguessableTokenHash', 'base::UnsafeSharedMemoryRegion', @@ -86,6 +92,12 @@ _CONFIG = [ 'base::RepeatingCallback', 'base::RepeatingClosure', + # //base/mac/scoped_nsobject.h + 'base::scoped_nsobject', + + # //base/memory/scoped_policy.h + 'base::scoped_policy::RETAIN', + # //base/memory/ptr_util.h. 'base::WrapUnique', @@ -95,6 +107,9 @@ _CONFIG = [ # //base/metrics/histogram_functions.h 'base::UmaHistogram.+', + # //base/metrics/histogram.h + 'base::LinearHistogram', + # //base/metrics/field_trial_params.h. 'base::GetFieldTrialParamValueByFeature', 'base::GetFieldTrialParamByFeatureAsBool', @@ -139,6 +154,11 @@ _CONFIG = [ 'base::CheckOr', 'base::CheckXor', + # //base/numerics/clamped_math.h. + 'base::ClampAdd', + 'base::ClampSub', + 'base::MakeClampedNum', + # Debugging helpers from //base/debug are allowed everywhere. 'base::debug::.+', @@ -346,6 +366,11 @@ _CONFIG = [ # Permit using crash keys inside Blink without jumping through # hoops. 'crash_reporter::.*CrashKey.*', + + # Useful for platform-specific code. + 'base::mac::(CFToNSCast|NSToCFCast)', + 'base::mac::Is(AtMost|AtLeast)?OS.+', + 'base::(scoped_nsobject|ScopedCFTypeRef)', ], 'disallowed': [ '.+', @@ -358,6 +383,16 @@ _CONFIG = [ 'allowed': ['gin::.+'], }, { + 'paths': ['third_party/blink/renderer/bindings/core/v8/script_streamer.cc'], + 'allowed': [ + # For the script streaming to be able to block when reading from a + # mojo datapipe. + 'base::ScopedAllowBaseSyncPrimitives', + 'base::ScopedBlockingCall', + 'base::BlockingType', + ], + }, + { 'paths': ['third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc'], 'allowed': [ # For memory reduction histogram. @@ -371,6 +406,12 @@ _CONFIG = [ ], }, { + 'paths': ['third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc'], + 'allowed': [ + 'base::MemoryPressureListener', + ], + }, + { 'paths': ['third_party/blink/renderer/core/animation'], 'allowed': [ '[a-z_]+_functions::.+', @@ -621,6 +662,22 @@ _CONFIG = [ 'base::OnTaskRunnerDeleter', 'sigslot::.+', ], + }, + # TODO(https://crbug.com/704441) : Added temporarily. + { + 'paths': ['third_party/blink/renderer/modules/exported/web_manifest_parser.cc'], + 'allowed': [ + 'base::StringPiece', + 'GURL', + ], + }, + { + 'paths': ['third_party/blink/renderer/modules/manifest/'], + 'allowed': [ + 'base::.+', + 'net::ParseMimeTypeWithoutParameter', + 'GURL', + ], } ] diff --git a/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp.py b/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp.py index 7d3936cfd92..e7ab3a76032 100644 --- a/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp.py +++ b/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp.py @@ -1866,7 +1866,7 @@ def check_language(filename, clean_lines, line_number, file_extension, include_s # FIXME: figure out if they're using default arguments in fn proto. # Check if they're using a precise-width integer type. - matched = search(r'\b((un)?signed\s+)?short\b', line) + matched = search(r'\b((un)?signed\s+)?(short|(long\s+)?long)\b', line) if matched: error(line_number, 'runtime/int', 1, 'Use a precise-width integer type from <stdint.h> or <cstdint>' diff --git a/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp_unittest.py b/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp_unittest.py index 36d83532abc..2ac8cc0ff4e 100644 --- a/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/style/checkers/cpp_unittest.py @@ -463,6 +463,18 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('uint16_t unsignedshort = 1', '') self.assert_lint('signed short a = 1', errmsg % 'signed short [runtime/int] [1]') self.assert_lint('short a = 1', errmsg % 'short [runtime/int] [1]') + self.assert_lint('unsigned long long a = 1', errmsg % 'unsigned long long [runtime/int] [1]') + self.assert_lint('signed long long a = 1', errmsg % 'signed long long [runtime/int] [1]') + self.assert_lint('long long a = 1', errmsg % 'long long [runtime/int] [1]') + self.assert_lint('uint64_t longlong = 1', '') + self.assert_lint('unsigned long a = 1', errmsg % 'unsigned long [runtime/int] [1]') + self.assert_lint('signed long a = 1', errmsg % 'signed long [runtime/int] [1]') + self.assert_lint('long a = 1', errmsg % 'long [runtime/int] [1]') + self.assert_lint('signed int long a = 1', errmsg % 'long [runtime/int] [1]') + self.assert_lint('unsigned long int a = 1', errmsg % 'unsigned long [runtime/int] [1]') + self.assert_lint('unsigned longlong = 1', '') + self.assert_lint('signed int a = 1', '') + self.assert_lint('int a = 1', '') # Test C-style cast cases. def test_cstyle_cast(self): @@ -1511,7 +1523,6 @@ class CppStyleTest(CppStyleTestBase): self.assert_lint('int a : 30;', errmsg) self.assert_lint('mutable int a : 14;', errmsg) self.assert_lint('const char a : 6;', errmsg) - self.assert_lint('long int a : 30;', errmsg) self.assert_lint('int a = 1 ? 0 : 30;', '') # Bitfields which are not declared unsigned or bool will generate a warning. diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/README.chromium b/chromium/third_party/blink/tools/blinkpy/third_party/README.chromium index ff596681578..fb6c057dff5 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/README.chromium +++ b/chromium/third_party/blink/tools/blinkpy/third_party/README.chromium @@ -32,7 +32,7 @@ Local Modifications: None Name: web-platform-tests - Test Suites for Web Platform specifications Short Name: wpt URL: https://github.com/web-platform-tests/wpt/ -Version: f759f0b3a8cb7457adbf30dc0734d3a158ce4990 +Version: 3d27143b5c6cbce8a9acc476b10c416aecd42a2d License: LICENSES FOR W3C TEST SUITES (http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html) License File: wpt/wpt/LICENSE.md Security Critical: no diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh index d927f1844d1..59555adc3ea 100755 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh @@ -9,7 +9,7 @@ cd $DIR TARGET_DIR=$DIR/wpt REMOTE_REPO="https://chromium.googlesource.com/external/github.com/web-platform-tests/wpt.git" -WPT_HEAD=f759f0b3a8cb7457adbf30dc0734d3a158ce4990 +WPT_HEAD=3d27143b5c6cbce8a9acc476b10c416aecd42a2d function clone { # Remove existing repo if already exists. diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch index 11fe8345bbb..ea26cc3c8b2 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/chromium.patch @@ -38,8 +38,10 @@ diff --git a/tools/wpt/paths b/tools/wpt/paths index 0ef13c96e3..93c97dc02b 100644 --- a/tools/wpt/paths +++ b/tools/wpt/paths -@@ -1,4 +1,3 @@ +@@ -1,6 +1,4 @@ -tools/ci/ tools/lint/ tools/manifest/ tools/serve/ +-tools/taskcluster/ + tools/wpt/ diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json index 3a674155c52..5715de520da 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json @@ -20,6 +20,10 @@ }, "aliases": [ { + "url-path": "/wpt_internal/", + "local-dir": "../../../../../web_tests/wpt_internal" + }, + { "url-path": "/wpt_automation/", "local-dir": "../../../../../web_tests/external/wpt_automation" }, diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py index 03f0ecc7780..bcf08d9be9c 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/lint/lint.py @@ -134,6 +134,28 @@ def check_worker_collision(repo_root, path): return [] +def check_gitignore_file(repo_root, path): + if not path.endswith(".gitignore"): + return [] + + path_parts = path.split(os.path.sep) + if len(path_parts) == 1: + return [] + + if path_parts[-1] != ".gitignore": + return [] + + if (path_parts[0] in ["tools", "docs"] or + path_parts[:2] == ["resources", "webidl2"] or + path_parts[:3] == ["css", "tools", "apiclient"]): + return [] + + return [("GITIGNORE", + ".gitignore found outside the root", + path, + None)] + + def check_ahem_copy(repo_root, path): lpath = path.lower() if "ahem" in lpath and lpath.endswith(".ttf"): @@ -914,7 +936,7 @@ def lint(repo_root, paths, output_format, ignore_glob): logger.info(line) return sum(itervalues(error_count)) -path_lints = [check_path_length, check_worker_collision, check_ahem_copy] +path_lints = [check_path_length, check_worker_collision, check_ahem_copy, check_gitignore_file] all_paths_lints = [check_css_globally_unique] file_lints = [check_regexp_line, check_parsed, check_python_ast, check_script_metadata] diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py index 2bbdf0d5a5b..b6d33e8452c 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/item.py @@ -1,24 +1,8 @@ +from copy import copy +from six import iteritems from six.moves.urllib.parse import urljoin, urlparse from abc import ABCMeta, abstractproperty - -class SourceFileCache(object): - def __init__(self): - self.source_files = {} - - def make_new(self, tests_root, path, url_base): - from .sourcefile import SourceFile - - return SourceFile(tests_root, path, url_base) - - def get(self, tests_root, manifest, path): - - if path not in self.source_files: - self.source_files[path] = self.make_new(tests_root, path, manifest.url_base) - - return self.source_files[path] - - item_types = {} @@ -38,48 +22,30 @@ class ManifestItemMeta(ABCMeta): class ManifestItem(object): __metaclass__ = ManifestItemMeta - item_type = None + __slots__ = ("_tests_root", "path") - source_file_cache = SourceFileCache() + item_type = None - def __init__(self, source_file, manifest=None): - self.source_file = source_file + def __init__(self, tests_root=None, path=None): + self._tests_root = tests_root + self.path = path @abstractproperty def id(self): """The test's id (usually its url)""" pass - @property - def meta_flags(self): - return set(self.source_file.meta_flags) - - @property - def path(self): - """The test path relative to the test_root""" - return self.source_file.rel_path - - @property - def https(self): - flags = self.meta_flags - return ("https" in flags or "serviceworker" in flags) - def key(self): """A unique identifier for the test""" return (self.item_type, self.id) - def meta_key(self): - """Extra metadata that doesn't form part of the test identity, but for - which changes mean regenerating the manifest (e.g. the test timeout.""" - return () - def __eq__(self, other): if not hasattr(other, "key"): return False return self.key() == other.key() def __hash__(self): - return hash(self.key() + self.meta_key()) + return hash(self.key()) def __repr__(self): return "<%s.%s id=%s, path=%s>" % (self.__module__, self.__class__.__name__, self.id, self.path) @@ -88,99 +54,117 @@ class ManifestItem(object): return [{}] @classmethod - def from_json(cls, manifest, tests_root, path, obj): - source_file = cls.source_file_cache.get(tests_root, manifest, path) - return cls(source_file, - manifest=manifest) + def from_json(cls, manifest, path, obj): + return cls(manifest.tests_root, path) class URLManifestItem(ManifestItem): - def __init__(self, source_file, url, url_base="/", manifest=None): - ManifestItem.__init__(self, source_file, manifest=manifest) - self._url = url + __slots__ = ("url_base", "_url", "_extras") + + def __init__(self, tests_root, path, url_base, url, **extras): + super(URLManifestItem, self).__init__(tests_root, path) + assert url_base[0] == "/" self.url_base = url_base + assert url[0] != "/" + self._url = url + self._extras = extras @property def id(self): return self.url @property - def meta_flags(self): - return set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1]) - - @property def url(self): + # we can outperform urljoin, because we know we just have path relative URLs + if self.url_base == "/": + return "/" + self._url return urljoin(self.url_base, self._url) + @property + def https(self): + flags = set(urlparse(self.url).path.rsplit("/", 1)[1].split(".")[1:-1]) + return ("https" in flags or "serviceworker" in flags) + def to_json(self): rv = [self._url, {}] return rv @classmethod - def from_json(cls, manifest, tests_root, path, obj): - source_file = cls.source_file_cache.get(tests_root, manifest, path) + def from_json(cls, manifest, path, obj): url, extras = obj - return cls(source_file, + return cls(manifest.tests_root, + path, + manifest.url_base, url, - url_base=manifest.url_base, - manifest=manifest) + **extras) class TestharnessTest(URLManifestItem): item_type = "testharness" - def __init__(self, source_file, url, url_base="/", timeout=None, testdriver=False, jsshell=False, manifest=None): - URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest) - self.timeout = timeout - self.testdriver = testdriver - self.jsshell = jsshell + @property + def timeout(self): + return self._extras.get("timeout") + + @property + def testdriver(self): + return self._extras.get("testdriver") - def meta_key(self): - return (self.timeout, self.testdriver) + @property + def jsshell(self): + return self._extras.get("jsshell") + + @property + def script_metadata(self): + return self._extras.get("script_metadata") def to_json(self): - rv = URLManifestItem.to_json(self) + rv = super(TestharnessTest, self).to_json() if self.timeout is not None: rv[-1]["timeout"] = self.timeout if self.testdriver: rv[-1]["testdriver"] = self.testdriver if self.jsshell: rv[-1]["jsshell"] = True + if self.script_metadata: + rv[-1]["script_metadata"] = self.script_metadata return rv - @classmethod - def from_json(cls, manifest, tests_root, path, obj): - source_file = cls.source_file_cache.get(tests_root, manifest, path) - url, extras = obj - return cls(source_file, - url, - url_base=manifest.url_base, - timeout=extras.get("timeout"), - testdriver=bool(extras.get("testdriver")), - jsshell=bool(extras.get("jsshell")), - manifest=manifest) +class RefTestBase(URLManifestItem): + __slots__ = ("references",) + item_type = "reftest_base" -class RefTestNode(URLManifestItem): - item_type = "reftest_node" + def __init__(self, tests_root, path, url_base, url, references=None, **extras): + super(RefTestBase, self).__init__(tests_root, path, url_base, url, **extras) + if references is None: + self.references = [] + else: + self.references = references - def __init__(self, source_file, url, references, url_base="/", timeout=None, - viewport_size=None, dpi=None, manifest=None): - URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest) - for _, ref_type in references: - if ref_type not in ["==", "!="]: - raise ValueError("Unrecognised ref_type %s" % ref_type) - self.references = tuple(references) - self.timeout = timeout - self.viewport_size = viewport_size - self.dpi = dpi + @property + def timeout(self): + return self._extras.get("timeout") - def meta_key(self): - return (self.timeout, self.viewport_size, self.dpi) + @property + def viewport_size(self): + return self._extras.get("viewport_size") + + @property + def dpi(self): + return self._extras.get("dpi") + + @property + def fuzzy(self): + rv = self._extras.get("fuzzy", []) + if isinstance(rv, list): + return {tuple(item[0]): item[1] + for item in self._extras.get("fuzzy", [])} + return rv def to_json(self): - rv = [self.url, self.references, {}] + rv = [self._url, self.references, {}] extras = rv[-1] if self.timeout is not None: extras["timeout"] = self.timeout @@ -188,37 +172,40 @@ class RefTestNode(URLManifestItem): extras["viewport_size"] = self.viewport_size if self.dpi is not None: extras["dpi"] = self.dpi + if self.fuzzy: + extras["fuzzy"] = list(iteritems(self.fuzzy)) return rv @classmethod - def from_json(cls, manifest, tests_root, path, obj): - source_file = cls.source_file_cache.get(tests_root, manifest, path) + def from_json(cls, manifest, path, obj): url, references, extras = obj - return cls(source_file, + return cls(manifest.tests_root, + path, + manifest.url_base, url, references, - url_base=manifest.url_base, - timeout=extras.get("timeout"), - viewport_size=extras.get("viewport_size"), - dpi=extras.get("dpi"), - manifest=manifest) + **extras) def to_RefTest(self): if type(self) == RefTest: return self - rv = RefTest.__new__(RefTest) - rv.__dict__.update(self.__dict__) + rv = copy(self) + rv.__class__ = RefTest return rv def to_RefTestNode(self): if type(self) == RefTestNode: return self - rv = RefTestNode.__new__(RefTestNode) - rv.__dict__.update(self.__dict__) + rv = copy(self) + rv.__class__ = RefTestNode return rv -class RefTest(RefTestNode): +class RefTestNode(RefTestBase): + item_type = "reftest_node" + + +class RefTest(RefTestBase): item_type = "reftest" @@ -241,31 +228,20 @@ class Stub(URLManifestItem): class WebDriverSpecTest(URLManifestItem): item_type = "wdspec" - def __init__(self, source_file, url, url_base="/", timeout=None, manifest=None): - URLManifestItem.__init__(self, source_file, url, url_base=url_base, manifest=manifest) - self.timeout = timeout + @property + def timeout(self): + return self._extras.get("timeout") def to_json(self): - rv = URLManifestItem.to_json(self) + rv = super(WebDriverSpecTest, self).to_json() if self.timeout is not None: rv[-1]["timeout"] = self.timeout return rv - @classmethod - def from_json(cls, manifest, tests_root, path, obj): - source_file = cls.source_file_cache.get(tests_root, manifest, path) - - url, extras = obj - return cls(source_file, - url, - url_base=manifest.url_base, - timeout=extras.get("timeout"), - manifest=manifest) - class SupportFile(ManifestItem): item_type = "support" @property def id(self): - return self.source_file.rel_path + return self.path diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py index 127d45dbc66..bfe57c7823b 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/manifest.py @@ -1,4 +1,5 @@ import itertools +import json import os from collections import defaultdict from six import iteritems, iterkeys, itervalues, string_types @@ -10,13 +11,11 @@ from .log import get_logger from .utils import from_os_path, to_os_path try: - import ujson as json - JSON_LIBRARY = 'ujson' + import ujson as fast_json except ImportError: - import json - JSON_LIBRARY = 'json' + fast_json = json -CURRENT_VERSION = 5 +CURRENT_VERSION = 6 class ManifestError(Exception): @@ -69,8 +68,8 @@ class TypeData(object): self.load(key) return self.data[key] - def __bool__(self): - return bool(self.data) + def __nonzero__(self): + return bool(self.data) or bool(self.json_data) def __len__(self): rv = len(self.data) @@ -87,6 +86,10 @@ class TypeData(object): raise KeyError def __setitem__(self, key, value): + if self.json_data is not None: + path = from_os_path(key) + if path in self.json_data: + del self.json_data[path] self.data[key] = value def __contains__(self, key): @@ -132,10 +135,7 @@ class TypeData(object): data = set() path = from_os_path(key) for test in iterfilter(self.meta_filters, self.json_data.get(path, [])): - manifest_item = self.type_cls.from_json(self.manifest, - self.tests_root, - path, - test) + manifest_item = self.type_cls.from_json(self.manifest, path, test) data.add(manifest_item) try: del self.json_data[path] @@ -154,10 +154,7 @@ class TypeData(object): continue data = set() for test in iterfilter(self.meta_filters, self.json_data.get(path, [])): - manifest_item = self.type_cls.from_json(self.manifest, - self.tests_root, - path, - test) + manifest_item = self.type_cls.from_json(self.manifest, path, test) data.add(manifest_item) self.data[key] = data self.json_data = None @@ -168,6 +165,21 @@ class TypeData(object): self.tests_root = tests_root self.json_data = data + def to_json(self): + data = { + from_os_path(path): + [t for t in sorted(test.to_json() for test in tests)] + for path, tests in iteritems(self.data) + } + + if self.json_data is not None: + if not data: + # avoid copying if there's nothing here yet + return self.json_data + data.update(self.json_data) + + return data + def paths(self): """Get a list of all paths containing items of this type, without actually constructing all the items""" @@ -202,11 +214,12 @@ class ManifestData(dict): class Manifest(object): - def __init__(self, url_base="/", meta_filters=None): + def __init__(self, tests_root=None, url_base="/", meta_filters=None): assert url_base is not None self._path_hash = {} self._data = ManifestData(self, meta_filters) self._reftest_nodes_by_url = None + self.tests_root = tests_root self.url_base = url_base def __iter__(self): @@ -262,7 +275,12 @@ class Manifest(object): changed = False reftest_changes = False - prev_files = self._data.paths() + # Create local variable references to these dicts so we avoid the + # attribute access in the hot loop below + path_hash = self._path_hash + data = self._data + + prev_files = data.paths() reftest_types = ("reftest", "reftest_node") @@ -270,74 +288,77 @@ class Manifest(object): if not update: rel_path = source_file seen_files.add(rel_path) + assert rel_path in path_hash + old_hash, old_type = path_hash[rel_path] + if old_type in reftest_types: + manifest_items = data[old_type][rel_path] + reftest_nodes.extend((item, old_hash) for item in manifest_items) else: rel_path = source_file.rel_path seen_files.add(rel_path) file_hash = source_file.hash - is_new = rel_path not in self._path_hash + is_new = rel_path not in path_hash hash_changed = False if not is_new: - old_hash, old_type = self._path_hash[rel_path] + old_hash, old_type = path_hash[rel_path] if old_hash != file_hash: new_type, manifest_items = source_file.manifest_items() hash_changed = True if new_type != old_type: - try: - del self._data[old_type][rel_path] - except KeyError: - pass + del data[old_type][rel_path] + if old_type in reftest_types: + reftest_changes = True else: - new_type, manifest_items = old_type, self._data[old_type][rel_path] - if old_type in reftest_types and new_type != old_type: - reftest_changes = True + new_type = old_type + if old_type in reftest_types: + manifest_items = data[old_type][rel_path] else: new_type, manifest_items = source_file.manifest_items() - if new_type in ("reftest", "reftest_node"): - reftest_nodes.extend(manifest_items) + if new_type in reftest_types: + reftest_nodes.extend((item, file_hash) for item in manifest_items) if is_new or hash_changed: reftest_changes = True - elif new_type: - self._data[new_type][rel_path] = set(manifest_items) - - self._path_hash[rel_path] = (file_hash, new_type) + elif is_new or hash_changed: + data[new_type][rel_path] = set(manifest_items) if is_new or hash_changed: + path_hash[rel_path] = (file_hash, new_type) changed = True deleted = prev_files - seen_files if deleted: changed = True for rel_path in deleted: - if rel_path in self._path_hash: - _, old_type = self._path_hash[rel_path] + if rel_path in path_hash: + _, old_type = path_hash[rel_path] if old_type in reftest_types: reftest_changes = True - del self._path_hash[rel_path] + del path_hash[rel_path] try: - del self._data[old_type][rel_path] + del data[old_type][rel_path] except KeyError: pass else: - for test_data in itervalues(self._data): + for test_data in itervalues(data): if rel_path in test_data: del test_data[rel_path] if reftest_changes: reftests, reftest_nodes, changed_hashes = self._compute_reftests(reftest_nodes) - self._data["reftest"].data = reftests - self._data["reftest_node"].data = reftest_nodes - self._path_hash.update(changed_hashes) + data["reftest"].data = reftests + data["reftest_node"].data = reftest_nodes + path_hash.update(changed_hashes) return changed def _compute_reftests(self, reftest_nodes): self._reftest_nodes_by_url = {} has_inbound = set() - for item in reftest_nodes: + for item, _ in reftest_nodes: for ref_url, ref_type in item.references: has_inbound.add(ref_url) @@ -345,31 +366,27 @@ class Manifest(object): references = defaultdict(set) changed_hashes = {} - for item in reftest_nodes: + for item, file_hash in reftest_nodes: if item.url in has_inbound: # This is a reference if isinstance(item, RefTest): item = item.to_RefTestNode() - changed_hashes[item.source_file.rel_path] = (item.source_file.hash, - item.item_type) - references[item.source_file.rel_path].add(item) + changed_hashes[item.path] = (file_hash, + item.item_type) + references[item.path].add(item) else: if isinstance(item, RefTestNode): item = item.to_RefTest() - changed_hashes[item.source_file.rel_path] = (item.source_file.hash, - item.item_type) - reftests[item.source_file.rel_path].add(item) + changed_hashes[item.path] = (file_hash, + item.item_type) + reftests[item.path].add(item) self._reftest_nodes_by_url[item.url] = item return reftests, references, changed_hashes def to_json(self): out_items = { - test_type: { - from_os_path(path): - [t for t in sorted(test.to_json() for test in tests)] - for path, tests in iteritems(type_paths) - } + test_type: type_paths.to_json() for test_type, type_paths in iteritems(self._data) if type_paths } rv = {"url_base": self.url_base, @@ -384,7 +401,7 @@ class Manifest(object): if version != CURRENT_VERSION: raise ManifestVersionMismatch - self = cls(url_base=obj.get("url_base", "/"), meta_filters=meta_filters) + self = cls(tests_root, url_base=obj.get("url_base", "/"), meta_filters=meta_filters) if not hasattr(obj, "items") and hasattr(obj, "paths"): raise ManifestError @@ -412,11 +429,11 @@ def load(tests_root, manifest, types=None, meta_filters=None): __load_cache = {} -def _load(logger, tests_root, manifest, types=None, meta_filters=None): +def _load(logger, tests_root, manifest, types=None, meta_filters=None, allow_cached=True): # "manifest" is a path or file-like object. manifest_path = (manifest if isinstance(manifest, string_types) else manifest.name) - if manifest_path in __load_cache: + if allow_cached and manifest_path in __load_cache: return __load_cache[manifest_path] if isinstance(manifest, string_types): @@ -427,7 +444,7 @@ def _load(logger, tests_root, manifest, types=None, meta_filters=None): try: with open(manifest) as f: rv = Manifest.from_json(tests_root, - json.load(f), + fast_json.load(f), types=types, meta_filters=meta_filters) except IOError: @@ -437,11 +454,12 @@ def _load(logger, tests_root, manifest, types=None, meta_filters=None): return None else: rv = Manifest.from_json(tests_root, - json.load(manifest), + fast_json.load(manifest), types=types, meta_filters=meta_filters) - __load_cache[manifest_path] = rv + if allow_cached: + __load_cache[manifest_path] = rv return rv @@ -455,7 +473,8 @@ def load_and_update(tests_root, working_copy=False, types=None, meta_filters=None, - write_manifest=True): + write_manifest=True, + allow_cached=True): logger = get_logger() manifest = None @@ -465,7 +484,8 @@ def load_and_update(tests_root, tests_root, manifest_path, types=types, - meta_filters=meta_filters) + meta_filters=meta_filters, + allow_cached=allow_cached) except ManifestVersionMismatch: logger.info("Manifest version changed, rebuilding") @@ -473,7 +493,7 @@ def load_and_update(tests_root, logger.info("Manifest url base did not match, rebuilding") if manifest is None: - manifest = Manifest(url_base, meta_filters=meta_filters) + manifest = Manifest(tests_root, url_base, meta_filters=meta_filters) update = True if update: @@ -492,12 +512,8 @@ def write(manifest, manifest_path): if not os.path.exists(dir_name): os.makedirs(dir_name) with open(manifest_path, "wb") as f: - if JSON_LIBRARY == 'ujson': - # ujson does not support the separators flag. - json.dump(manifest.to_json(), f, sort_keys=True, indent=1) - else: - # Use ',' instead of the default ', ' separator to prevent trailing - # spaces: https://docs.python.org/2/library/json.html#json.dump - json.dump(manifest.to_json(), f, - sort_keys=True, indent=1, separators=(',', ': ')) + # Use ',' instead of the default ', ' separator to prevent trailing + # spaces: https://docs.python.org/2/library/json.html#json.dump + json.dump(manifest.to_json(), f, + sort_keys=True, indent=1, separators=(',', ': ')) f.write("\n") diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py index d2e5df85526..97fe89bbda8 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/sourcefile.py @@ -1,6 +1,7 @@ import hashlib import re import os +from collections import deque from six import binary_type from six.moves.urllib.parse import urljoin from fnmatch import fnmatch @@ -13,7 +14,7 @@ import html5lib from . import XMLParser from .item import Stub, ManualTest, WebDriverSpecTest, RefTestNode, TestharnessTest, SupportFile, ConformanceCheckerTest, VisualTest -from .utils import rel_path_to_url, ContextManagerBytesIO, cached_property +from .utils import ContextManagerBytesIO, cached_property wd_pattern = "*.py" js_meta_re = re.compile(b"//\s*META:\s*(\w*)=(.*)$") @@ -233,8 +234,13 @@ class SourceFile(object): return os.path.join(self.tests_root, self.rel_path) @cached_property + def rel_url(self): + assert not os.path.isabs(self.rel_path), self.rel_path + return self.rel_path.replace(os.sep, "/") + + @cached_property def url(self): - return rel_path_to_url(self.rel_path, self.url_base) + return urljoin(self.url_base, self.rel_url) @cached_property def hash(self): @@ -335,7 +341,7 @@ class SourceFile(object): def name_is_reference(self): """Check if the file name matches the conditions for the file to be a reference file (not a reftest)""" - return "/reference/" in self.url or "/reftest/" in self.url or bool(reference_file_re.search(self.name)) + return "/reference/" in self.url or bool(reference_file_re.search(self.name)) @property def markup_type(self): @@ -448,6 +454,79 @@ class SourceFile(object): return self.dpi_nodes[0].attrib.get("content", None) @cached_property + def fuzzy_nodes(self): + """List of ElementTree Elements corresponding to nodes in a test that + specify reftest fuzziness""" + return self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='fuzzy']") + + @cached_property + def fuzzy(self): + rv = {} + if self.root is None: + return rv + + if not self.fuzzy_nodes: + return rv + + args = ["maxDifference", "totalPixels"] + + for node in self.fuzzy_nodes: + item = node.attrib.get("content", "") + + parts = item.rsplit(":", 1) + if len(parts) == 1: + key = None + value = parts[0] + else: + key = urljoin(self.url, parts[0]) + reftype = None + for ref in self.references: + if ref[0] == key: + reftype = ref[1] + break + if reftype not in ("==", "!="): + raise ValueError("Fuzzy key %s doesn't correspond to a references" % key) + key = (self.url, key, reftype) + value = parts[1] + ranges = value.split(";") + if len(ranges) != 2: + raise ValueError("Malformed fuzzy value %s" % item) + arg_values = {None: deque()} + for range_str_value in ranges: + if "=" in range_str_value: + name, range_str_value = [part.strip() + for part in range_str_value.split("=", 1)] + if name not in args: + raise ValueError("%s is not a valid fuzzy property" % name) + if arg_values.get(name): + raise ValueError("Got multiple values for argument %s" % name) + else: + name = None + if "-" in range_str_value: + range_min, range_max = range_str_value.split("-") + else: + range_min = range_str_value + range_max = range_str_value + try: + range_value = [int(x.strip()) for x in (range_min, range_max)] + except ValueError: + raise ValueError("Fuzzy value %s must be a range of integers" % + range_str_value) + if name is None: + arg_values[None].append(range_value) + else: + arg_values[name] = range_value + rv[key] = [] + for arg_name in args: + if arg_values.get(arg_name): + value = arg_values.pop(arg_name) + else: + value = arg_values[None].popleft() + rv[key].append(value) + assert list(arg_values.keys()) == [None] and len(arg_values[None]) == 0 + return rv + + @cached_property def testharness_nodes(self): """List of ElementTree Elements corresponding to nodes representing a testharness.js script""" @@ -599,22 +678,54 @@ class SourceFile(object): return self.items_cache if self.name_is_non_test: - rv = "support", [SupportFile(self)] + rv = "support", [ + SupportFile( + self.tests_root, + self.rel_path + )] elif self.name_is_stub: - rv = Stub.item_type, [Stub(self, self.url)] + rv = Stub.item_type, [ + Stub( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] elif self.name_is_manual: - rv = ManualTest.item_type, [ManualTest(self, self.url)] + rv = ManualTest.item_type, [ + ManualTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] elif self.name_is_conformance: - rv = ConformanceCheckerTest.item_type, [ConformanceCheckerTest(self, self.url)] + rv = ConformanceCheckerTest.item_type, [ + ConformanceCheckerTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] elif self.name_is_conformance_support: - rv = "support", [SupportFile(self)] + rv = "support", [ + SupportFile( + self.tests_root, + self.rel_path + )] elif self.name_is_visual: - rv = VisualTest.item_type, [VisualTest(self, self.url)] + rv = VisualTest.item_type, [ + VisualTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] elif self.name_is_multi_global: globals = b"" @@ -624,53 +735,115 @@ class SourceFile(object): break tests = [ - TestharnessTest(self, global_variant_url(self.url, suffix) + variant, timeout=self.timeout, - jsshell=jsshell) + TestharnessTest( + self.tests_root, + self.rel_path, + self.url_base, + global_variant_url(self.rel_url, suffix) + variant, + timeout=self.timeout, + jsshell=jsshell, + script_metadata=self.script_metadata + ) for (suffix, jsshell) in sorted(global_suffixes(globals)) for variant in self.test_variants ] rv = TestharnessTest.item_type, tests elif self.name_is_worker: - test_url = replace_end(self.url, ".worker.js", ".worker.html") + test_url = replace_end(self.rel_url, ".worker.js", ".worker.html") tests = [ - TestharnessTest(self, test_url + variant, timeout=self.timeout) + TestharnessTest( + self.tests_root, + self.rel_path, + self.url_base, + test_url + variant, + timeout=self.timeout, + script_metadata=self.script_metadata + ) for variant in self.test_variants ] rv = TestharnessTest.item_type, tests elif self.name_is_window: - test_url = replace_end(self.url, ".window.js", ".window.html") + test_url = replace_end(self.rel_url, ".window.js", ".window.html") tests = [ - TestharnessTest(self, test_url + variant, timeout=self.timeout) + TestharnessTest( + self.tests_root, + self.rel_path, + self.url_base, + test_url + variant, + timeout=self.timeout, + script_metadata=self.script_metadata + ) for variant in self.test_variants ] rv = TestharnessTest.item_type, tests elif self.name_is_webdriver: - rv = WebDriverSpecTest.item_type, [WebDriverSpecTest(self, self.url, - timeout=self.timeout)] + rv = WebDriverSpecTest.item_type, [ + WebDriverSpecTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url, + timeout=self.timeout + )] elif self.content_is_css_manual and not self.name_is_reference: - rv = ManualTest.item_type, [ManualTest(self, self.url)] + rv = ManualTest.item_type, [ + ManualTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] elif self.content_is_testharness: rv = TestharnessTest.item_type, [] testdriver = self.has_testdriver for variant in self.test_variants: - url = self.url + variant - rv[1].append(TestharnessTest(self, url, timeout=self.timeout, testdriver=testdriver)) + url = self.rel_url + variant + rv[1].append(TestharnessTest( + self.tests_root, + self.rel_path, + self.url_base, + url, + timeout=self.timeout, + testdriver=testdriver, + script_metadata=self.script_metadata + )) elif self.content_is_ref_node: - rv = (RefTestNode.item_type, - [RefTestNode(self, self.url, self.references, timeout=self.timeout, - viewport_size=self.viewport_size, dpi=self.dpi)]) + rv = RefTestNode.item_type, [ + RefTestNode( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url, + references=self.references, + timeout=self.timeout, + viewport_size=self.viewport_size, + dpi=self.dpi, + fuzzy=self.fuzzy + )] elif self.content_is_css_visual and not self.name_is_reference: - rv = VisualTest.item_type, [VisualTest(self, self.url)] + rv = VisualTest.item_type, [ + VisualTest( + self.tests_root, + self.rel_path, + self.url_base, + self.rel_url + )] else: - rv = "support", [SupportFile(self)] + rv = "support", [ + SupportFile( + self.tests_root, + self.rel_path + )] + + assert len(rv[1]) == len(set(rv[1])) self.items_cache = rv diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py index 217d767cece..321cfebe2a6 100755 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/update.py @@ -33,7 +33,7 @@ def update_from_cli(**kwargs): path = kwargs["path"] assert tests_root is not None - if kwargs["download"]: + if not kwargs["rebuild"] and kwargs["download"]: download_from_github(path, tests_root) manifest.load_and_update(tests_root, diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py index 9da79f6deb4..a097ad5090e 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/utils.py @@ -14,7 +14,10 @@ def rel_path_to_url(rel_path, url_base="/"): def from_os_path(path): assert os.path.sep == "/" or platform.system() == "Windows" - rv = path.replace(os.path.sep, "/") + if "/" == os.path.sep: + rv = path + else: + rv = path.replace(os.path.sep, "/") if "\\" in rv: raise ValueError("path contains \\ when separator is %s" % os.path.sep) return rv @@ -24,6 +27,8 @@ def to_os_path(path): assert os.path.sep == "/" or platform.system() == "Windows" if "\\" in path: raise ValueError("normalised path contains \\") + if "/" == os.path.sep: + return path return path.replace("/", os.path.sep) diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py index 69eb9ab8ecb..676e53f6785 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/manifest/vcs.py @@ -50,8 +50,8 @@ class Git(object): return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT) except Exception as e: if platform.uname()[0] == "Windows" and isinstance(e, WindowsError): - full_cmd[0] = "git.bat" - return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT) + full_cmd[0] = "git.bat" + return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT) else: raise return git @@ -60,31 +60,32 @@ class Git(object): def for_path(cls, path, url_base, cache_path, manifest_path=None, rebuild=False): git = Git.get_func(path) try: - return cls(git("rev-parse", "--show-toplevel").rstrip(), url_base, cache_path, - manifest_path=manifest_path, rebuild=rebuild) + # this needs to be a command that fails if we aren't in a git repo + git("rev-parse", "--show-toplevel") except (subprocess.CalledProcessError, OSError): return None + else: + return cls(path, url_base, cache_path, + manifest_path=manifest_path, rebuild=rebuild) def _local_changes(self): - changes = {} + """get a set of files which have changed between HEAD and working copy""" + changes = set() + cmd = ["status", "-z", "--ignore-submodules=all"] data = self.git(*cmd) - if data == "": - return changes - - rename_data = None - for entry in data.split("\0")[:-1]: - if rename_data is not None: - status, rel_path = entry.split(" ") - if status[0] == "R": - rename_data = (rel_path, status) - else: - changes[rel_path] = (status, None) + in_rename = False + for line in data.split(b"\0")[:-1]: + if in_rename: + changes.add(line) + in_rename = False else: - rel_path = entry - changes[rel_path] = rename_data - rename_data = None + status = line[:2] + if b"R" in status or b"C" in status: + in_rename = True + changes.add(line[3:]) + return changes def _show_file(self, path): @@ -95,18 +96,17 @@ class Git(object): cmd = ["ls-tree", "-r", "-z", "HEAD"] local_changes = self._local_changes() for result in self.git(*cmd).split("\0")[:-1]: - rel_path = result.split("\t")[-1] - hash = result.split()[2] - if not os.path.isdir(os.path.join(self.root, rel_path)): - if rel_path in local_changes: - contents = self._show_file(rel_path) - else: - contents = None - yield SourceFile(self.root, - rel_path, - self.url_base, - hash, - contents=contents), True + data, rel_path = result.rsplit("\t", 1) + hash = data.split(" ", 3)[2] + if rel_path in local_changes: + contents = self._show_file(rel_path) + else: + contents = None + yield SourceFile(self.root, + rel_path, + self.url_base, + hash, + contents=contents), True def dump_caches(self): pass diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py index 4eee732443b..53f81457be1 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/serve/serve.py @@ -16,6 +16,7 @@ import traceback from six.moves import urllib import uuid from collections import defaultdict, OrderedDict +from itertools import chain, product from multiprocessing import Process, Event from localpaths import repo_root @@ -720,6 +721,9 @@ def build_config(override_path=None, **kwargs): return rv +def _make_subdomains_product(s, depth=2): + return set(u".".join(x) for x in chain(*(product(s, repeat=i) for i in range(1, depth+1)))) + _subdomains = {u"www", u"www1", u"www2", @@ -728,6 +732,10 @@ _subdomains = {u"www", _not_subdomains = {u"nonexistent"} +_subdomains = _make_subdomains_product(_subdomains) + +_not_subdomains = _make_subdomains_product(_not_subdomains) + class ConfigBuilder(config.ConfigBuilder): """serve config diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/client.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/client.py index 29be09bb3c9..52ebc95e84a 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/client.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/client.py @@ -356,7 +356,7 @@ class UserPrompt(object): @text.setter @command def text(self, value): - body = {"value": list(value)} + body = {"text": value} self.session.send_session_command("POST", "alert/text", body=body) diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/transport.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/transport.py index 881002ad117..c6786b9ed05 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/transport.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/webdriver/transport.py @@ -16,9 +16,10 @@ class Response(object): body has been read and parsed as appropriate. """ - def __init__(self, status, body): + def __init__(self, status, body, headers): self.status = status self.body = body + self.headers = headers def __repr__(self): cls_name = self.__class__.__name__ @@ -39,11 +40,12 @@ class Response(object): def from_http(cls, http_response, decoder=json.JSONDecoder, **kwargs): try: body = json.load(http_response, cls=decoder, **kwargs) + headers = dict(http_response.getheaders()) except ValueError: raise ValueError("Failed to decode response body as JSON:\n" + http_response.read()) - return cls(http_response.status, body) + return cls(http_response.status, body, headers) class HTTPWireProtocol(object): diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py index 71d1e61918a..52157bca4e7 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/browser.py @@ -5,10 +5,13 @@ import shutil import stat import subprocess import tempfile +import urlparse from abc import ABCMeta, abstractmethod from datetime import datetime, timedelta from distutils.spawn import find_executable +import requests + from utils import call, get, untar, unzip uname = platform.uname() @@ -26,7 +29,7 @@ class Browser(object): return NotImplemented @abstractmethod - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): """Install the WebDriver implementation for this browser.""" return NotImplemented @@ -93,30 +96,28 @@ class Firefox(Browser): def install(self, dest=None, channel="nightly"): """Install Firefox.""" - branch = { - "nightly": "mozilla-central", - "beta": "mozilla-beta", - "stable": "mozilla-stable" - } - scraper = { - "nightly": "daily", - "beta": "release", - "stable": "release" + import mozinstall + + product = { + "nightly": "firefox-nightly-latest-ssl", + "beta": "firefox-beta-latest-ssl", + "stable": "firefox-latest-ssl" } - version = { - "stable": "latest", - "beta": "latest-beta", - "nightly": "latest" + + os_builds = { + ("linux", "x86"): "linux", + ("linux", "x86_64"): "linux64", + ("win", "x86"): "win", + ("win", "x86_64"): "win64", + ("macos", "x86_64"): "osx", } + os_key = (self.platform, uname[4]) - if channel not in branch: + if channel not in product: raise ValueError("Unrecognised release channel: %s" % channel) - from mozdownload import FactoryScraper - import mozinstall - - if self.platform is None: - raise ValueError("Unable to construct a valid Firefox package name for current platform") + if os_key not in os_builds: + raise ValueError("Unsupported platform: %s %s" % os_key) if dest is None: # os.getcwd() doesn't include the venv path @@ -124,17 +125,35 @@ class Firefox(Browser): dest = os.path.join(dest, "browsers", channel) - scraper = FactoryScraper(scraper[channel], - branch=branch[channel], - version=version[channel], - destination=dest) + if not os.path.exists(dest): + os.makedirs(dest) + + url = "https://download.mozilla.org/?product=%s&os=%s&lang=en-US" % (product[channel], + os_builds[os_key]) + self.logger.info("Downloading Firefox from %s" % url) + resp = requests.get(url) + + filename = None + + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + filenames = re.findall("filename=(.+)", content_disposition) + if filenames: + filename = filenames[0] + + if not filename: + filename = urlparse.urlsplit(resp.url).path.rsplit("/", 1)[1] + + if not filename: + filename = "firefox.tar.bz2" - self.logger.info("Downloading Firefox from %s" % scraper.url) + installer_path = os.path.join(dest, filename) - filename = scraper.download() + with open(installer_path, "w") as f: + f.write(resp.content) try: - mozinstall.install(filename, dest) + mozinstall.install(installer_path, dest) except mozinstall.mozinstall.InstallError: if self.platform == "macos" and os.path.exists(os.path.join(dest, self.application_name.get(channel, "Firefox Nightly.app"))): # mozinstall will fail if nightly is already installed in the venv because @@ -144,14 +163,14 @@ class Firefox(Browser): else: raise - os.remove(filename) + os.remove(installer_path) return self.find_binary_path(dest) - def find_binary_path(self,path=None, channel="nightly"): + def find_binary_path(self, path=None, channel="nightly"): """Looks for the firefox binary in the virtual environment""" if path is None: - #os.getcwd() doesn't include the venv path + # os.getcwd() doesn't include the venv path path = os.path.join(os.getcwd(), "_venv", "browsers", channel) binary = None @@ -226,13 +245,10 @@ class Firefox(Browser): tag = "tip" else: repo = "https://hg.mozilla.org/mozilla-central" - if channel == "beta": - tag = "FIREFOX_%s_BETA" % version.split(".", 1)[0] - else: - # Always use tip as the tag for nightly; this isn't quite right - # but to do better we need the actual build revision, which we - # can get if we have an application.ini file - tag = "tip" + # Always use tip as the tag for nightly; this isn't quite right + # but to do better we need the actual build revision, which we + # can get if we have an application.ini file + tag = "tip" return "%s/archive/%s.zip/testing/profiles/" % (repo, tag) @@ -251,7 +267,7 @@ class Firefox(Browser): if version: dest = os.path.join(dest, version) have_cache = False - if os.path.exists(dest): + if os.path.exists(dest) and len(os.listdir(dest)) > 0: if channel != "nightly": have_cache = True else: @@ -299,7 +315,7 @@ class Firefox(Browser): assert latest_release != 0 return "v%s.%s.%s" % tuple(str(item) for item in latest_release) - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): """Install latest Geckodriver.""" if dest is None: dest = os.getcwd() @@ -376,7 +392,7 @@ class Fennec(Browser): def find_webdriver(self, channel=None): raise NotImplementedError - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): @@ -424,24 +440,66 @@ class Chrome(Browser): return "%s%s" % (platform, bits) + def chromium_platform_string(self): + platform = { + "Linux": "Linux", + "Windows": "Win", + "Darwin": "Mac" + }.get(uname[0]) + + if platform is None: + raise ValueError("Unable to construct a valid Chromium package name for current platform") + + if (platform == "Linux" or platform == "Win") and uname[4] == "x86_64": + platform += "_x64" + + return platform + def find_binary(self, venv_path=None, channel=None): raise NotImplementedError def find_webdriver(self, channel=None): return find_executable("chromedriver") - def install_webdriver(self, dest=None, channel=None): + def _latest_chromedriver_url(self, browser_binary=None): + latest = None + chrome_version = self.version(browser_binary) + if chrome_version is not None: + parts = chrome_version.split(".") + if len(parts) == 4: + latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s.%s.%s" % ( + parts[0], parts[1], parts[2]) + try: + latest = get(latest_url).text.strip() + except requests.RequestException: + latest_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_%s" % parts[0] + try: + latest = get(latest_url).text.strip() + except requests.RequestException: + pass + if latest is None: + # Fall back to the tip-of-tree *Chromium* build. + latest_url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/LAST_CHANGE" % ( + self.chromium_platform_string()) + latest = get(latest_url).text.strip() + url = "https://storage.googleapis.com/chromium-browser-snapshots/%s/%s/chromedriver_%s.zip" % ( + self.chromium_platform_string(), latest, self.platform_string()) + else: + url = "https://chromedriver.storage.googleapis.com/%s/chromedriver_%s.zip" % ( + latest, self.platform_string()) + return url + + def install_webdriver(self, dest=None, channel=None, browser_binary=None): if dest is None: dest = os.pwd - latest = get("http://chromedriver.storage.googleapis.com/LATEST_RELEASE").text.strip() - url = "http://chromedriver.storage.googleapis.com/%s/chromedriver_%s.zip" % (latest, - self.platform_string()) + url = self._latest_chromedriver_url(browser_binary) + self.logger.info("Downloading ChromeDriver from %s" % url) unzip(get(url).raw, dest) - - path = find_executable("chromedriver", dest) - st = os.stat(path) - os.chmod(path, st.st_mode | stat.S_IEXEC) - return path + chromedriver_dir = os.path.join(dest, 'chromedriver_%s' % self.platform_string()) + if os.path.isfile(os.path.join(chromedriver_dir, "chromedriver")): + shutil.move(os.path.join(chromedriver_dir, "chromedriver"), dest) + shutil.rmtree(chromedriver_dir) + return find_executable("chromedriver", dest) def version(self, binary=None, webdriver_binary=None): binary = binary or self.binary @@ -449,11 +507,11 @@ class Chrome(Browser): try: version_string = call(binary, "--version").strip() except subprocess.CalledProcessError: - self.logger.warning("Failed to call %s", binary) + self.logger.warning("Failed to call %s" % binary) return None - m = re.match(r"Google Chrome (.*)", version_string) + m = re.match(r"(?:Google Chrome|Chromium) (.*)", version_string) if not m: - self.logger.warning("Failed to extract version from: s%", version_string) + self.logger.warning("Failed to extract version from: %s" % version_string) return None return m.group(1) self.logger.warning("Unable to extract version from binary on Windows.") @@ -478,13 +536,14 @@ class ChromeAndroid(Browser): def find_webdriver(self, channel=None): return find_executable("chromedriver") - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): chrome = Chrome() return chrome.install_webdriver(dest, channel) def version(self, binary=None, webdriver_binary=None): return None + class Opera(Browser): """Opera-specific interface. @@ -530,7 +589,7 @@ class Opera(Browser): def find_webdriver(self, channel=None): return find_executable("operadriver") - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): if dest is None: dest = os.pwd latest = get("https://api.github.com/repos/operasoftware/operachromiumdriver/releases/latest").json()["tag_name"] @@ -553,7 +612,7 @@ class Opera(Browser): try: output = call(binary, "--version") except subprocess.CalledProcessError: - self.logger.warning("Failed to call %s", binary) + self.logger.warning("Failed to call %s" % binary) return None m = re.search(r"[0-9\.]+( [a-z]+)?$", output.strip()) if m: @@ -575,11 +634,16 @@ class Edge(Browser): def find_webdriver(self, channel=None): return find_executable("MicrosoftWebDriver") - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): - return None + command = "(Get-AppxPackage Microsoft.MicrosoftEdge).Version" + try: + return call("powershell.exe", command).strip() + except (subprocess.CalledProcessError, OSError): + self.logger.warning("Failed to call %s in PowerShell" % command) + return None class EdgeWebDriver(Edge): @@ -601,7 +665,7 @@ class InternetExplorer(Browser): def find_webdriver(self, channel=None): return find_executable("IEDriverServer.exe") - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): @@ -629,7 +693,7 @@ class Safari(Browser): path = "/Applications/Safari Technology Preview.app/Contents/MacOS" return find_executable("safaridriver", path) - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): @@ -643,11 +707,11 @@ class Safari(Browser): try: version_string = call(webdriver_binary, "--version").strip() except subprocess.CalledProcessError: - self.logger.warning("Failed to call %s --version", webdriver_binary) + self.logger.warning("Failed to call %s --version" % webdriver_binary) return None m = re.match(r"Included with Safari (.*)", version_string) if not m: - self.logger.warning("Failed to extract version from: s%", version_string) + self.logger.warning("Failed to extract version from: %s" % version_string) return None return m.group(1) @@ -701,7 +765,7 @@ class Servo(Browser): def find_webdriver(self, channel=None): return None - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): @@ -731,7 +795,7 @@ class Sauce(Browser): def find_webdriver(self, channel=None): raise NotImplementedError - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): @@ -753,8 +817,37 @@ class WebKit(Browser): def find_webdriver(self, channel=None): return None - def install_webdriver(self, dest=None, channel=None): + def install_webdriver(self, dest=None, channel=None, browser_binary=None): + raise NotImplementedError + + def version(self, binary=None, webdriver_binary=None): + return None + + +class Epiphany(Browser): + """Epiphany-specific interface.""" + + product = "epiphany" + requirements = "requirements_epiphany.txt" + + def install(self, dest=None, channel=None): + raise NotImplementedError + + def find_binary(self, venv_path=None, channel=None): + return find_executable("epiphany") + + def find_webdriver(self, channel=None): + return find_executable("WebKitWebDriver") + + def install_webdriver(self, dest=None, channel=None, browser_binary=None): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): + if binary is None: + return None + output = call(binary, "--version") + if output: + # Stable release output looks like: "Web 3.30.2" + # Tech Preview output looks like "Web 3.31.3-88-g97db4f40f" + return output.split()[1] return None diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py index 44cf3137d41..ffdd8efa946 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/run.py @@ -106,7 +106,7 @@ otherwise install OpenSSL and ensure that it's on your $PATH.""") def check_environ(product): - if product not in ("firefox", "servo"): + if product not in ("chrome", "firefox", "servo"): config_builder = serve.build_config(os.path.join(wpt_root, "config.json")) # Override the ports to avoid looking for free ports config_builder.ssl = {"type": "none"} @@ -171,7 +171,8 @@ class BrowserSetup(object): return self.browser.install(venv.path, channel) def install_requirements(self): - self.venv.install_requirements(os.path.join(wpt_root, "tools", "wptrunner", self.browser.requirements)) + if not self.venv.skip_virtualenv_setup: + self.venv.install_requirements(os.path.join(wpt_root, "tools", "wptrunner", self.browser.requirements)) def setup(self, kwargs): self.setup_kwargs(kwargs) @@ -265,7 +266,7 @@ class Chrome(BrowserSetup): if install: logger.info("Downloading chromedriver") - webdriver_binary = self.browser.install_webdriver(dest=self.venv.bin_path) + webdriver_binary = self.browser.install_webdriver(dest=self.venv.bin_path, browser_binary=kwargs["binary"]) else: logger.info("Using webdriver binary %s" % webdriver_binary) @@ -276,14 +277,6 @@ class Chrome(BrowserSetup): if kwargs["browser_channel"] == "dev": logger.info("Automatically turning on experimental features for Chrome Dev") kwargs["binary_args"].append("--enable-experimental-web-platform-features") - # TODO(foolip): remove after unified plan is enabled on Chrome stable - kwargs["binary_args"].append("--enable-features=RTCUnifiedPlanByDefault") - - # Allow audio autoplay without a user gesture. - kwargs["binary_args"].append("--autoplay-policy=no-user-gesture-required") - - # Allow WebRTC tests to call getUserMedia. - kwargs["binary_args"] += ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream"] class ChromeAndroid(BrowserSetup): @@ -422,7 +415,7 @@ class Servo(BrowserSetup): binary = self.browser.find_binary(self.venv.path, None) if binary is None: - raise WptrunError("Unable to find servo binary on the PATH") + raise WptrunError("Unable to find servo binary in PATH") kwargs["binary"] = binary @@ -442,6 +435,29 @@ class WebKit(BrowserSetup): pass +class Epiphany(BrowserSetup): + name = "epiphany" + browser_cls = browser.Epiphany + + def install(self, venv, channel=None): + raise NotImplementedError + + def setup_kwargs(self, kwargs): + if kwargs["binary"] is None: + binary = self.browser.find_binary() + + if binary is None: + raise WptrunError("Unable to find epiphany in PATH") + kwargs["binary"] = binary + + if kwargs["webdriver_binary"] is None: + webdriver_binary = self.browser.find_webdriver() + + if webdriver_binary is None: + raise WptrunError("Unable to find WebKitWebDriver in PATH") + kwargs["webdriver_binary"] = webdriver_binary + + product_setup = { "fennec": Fennec, "firefox": Firefox, @@ -456,6 +472,7 @@ product_setup = { "sauce": Sauce, "opera": Opera, "webkit": WebKit, + "epiphany": Epiphany, } @@ -539,10 +556,15 @@ def setup_wptrunner(venv, prompt=True, install_browser=False, **kwargs): wptrunner_path = os.path.join(wpt_root, "tools", "wptrunner") - venv.install_requirements(os.path.join(wptrunner_path, "requirements.txt")) + if not venv.skip_virtualenv_setup: + venv.install_requirements(os.path.join(wptrunner_path, "requirements.txt")) + + # Only update browser_version if it was not given as a command line + # argument, so that it can be overridden on the command line. + if not kwargs["browser_version"]: + kwargs["browser_version"] = setup_cls.browser.version(binary=kwargs.get("binary"), + webdriver_binary=kwargs.get("webdriver_binary")) - kwargs['browser_version'] = setup_cls.browser.version(binary=kwargs.get("binary"), - webdriver_binary=kwargs.get("webdriver_binary")) return kwargs diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py index 70e695aa010..006e4a22d0b 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/testfiles.py @@ -34,15 +34,15 @@ def display_branch_point(): def branch_point(): git = get_git_cmd(wpt_root) - if (os.environ.get("TRAVIS_PULL_REQUEST", "false") == "false" and - os.environ.get("TRAVIS_BRANCH") == "master"): + if (os.environ.get("GITHUB_PULL_REQUEST", "false") == "false" and + os.environ.get("GITHUB_BRANCH") == "master"): # For builds on the master branch just return the HEAD commit return git("rev-parse", "HEAD") - elif os.environ.get("TRAVIS_PULL_REQUEST", "false") != "false": - # This is a PR, so the base branch is in TRAVIS_BRANCH - travis_branch = os.environ.get("TRAVIS_BRANCH") - assert travis_branch, "TRAVIS_BRANCH environment variable is defined" - branch_point = git("merge-base", "HEAD", travis_branch) + elif os.environ.get("GITHUB_PULL_REQUEST", "false") != "false": + # This is a PR, so the base branch is in GITHUB_BRANCH + base_branch = os.environ.get("GITHUB_BRANCH") + assert base_branch, "GITHUB_BRANCH environment variable is defined" + branch_point = git("merge-base", "HEAD", base_branch) else: # Otherwise we aren't on a PR, so we try to find commits that are only in the # current branch c.f. diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py index fa60230389d..f98687feb51 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/utils.py @@ -5,6 +5,11 @@ import tarfile import zipfile from io import BytesIO +try: + from typing import Any +except ImportError: + pass + logger = logging.getLogger(__name__) diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py index 0ce7054ae22..b99d15d1f6a 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/virtualenv.py @@ -4,16 +4,21 @@ import sys import logging from distutils.spawn import find_executable +import pkg_resources + from tools.wpt.utils import call logger = logging.getLogger(__name__) class Virtualenv(object): - def __init__(self, path): + def __init__(self, path, skip_virtualenv_setup): self.path = path - self.virtualenv = find_executable("virtualenv") - if not self.virtualenv: - raise ValueError("virtualenv must be installed and on the PATH") + self.skip_virtualenv_setup = skip_virtualenv_setup + if not skip_virtualenv_setup: + self.virtualenv = find_executable("virtualenv") + if not self.virtualenv: + raise ValueError("virtualenv must be installed and on the PATH") + self._working_set = None @property def exists(self): @@ -22,6 +27,7 @@ class Virtualenv(object): def create(self): if os.path.exists(self.path): shutil.rmtree(self.path) + self._working_set = None call(self.virtualenv, self.path, "-p", sys.executable) @property @@ -37,6 +43,36 @@ class Virtualenv(object): raise ValueError("pip not found") return path + @property + def lib_path(self): + base = self.path + + # this block is literally taken from virtualenv 16.4.3 + IS_PYPY = hasattr(sys, "pypy_version_info") + IS_JYTHON = sys.platform.startswith("java") + if IS_JYTHON: + site_packages = os.path.join(base, "Lib", "site-packages") + elif IS_PYPY: + site_packages = os.path.join(base, "site-packages") + else: + IS_WIN = sys.platform == "win32" + if IS_WIN: + site_packages = os.path.join(base, "Lib", "site-packages") + else: + site_packages = os.path.join(base, "lib", "python{}".format(sys.version[:3]), "site-packages") + + return site_packages + + @property + def working_set(self): + if not self.exists: + raise ValueError("trying to read working_set when venv doesn't exist") + + if self._working_set is None: + self._working_set = pkg_resources.WorkingSet((self.lib_path,)) + + return self._working_set + def activate(self): path = os.path.join(self.bin_path, "activate_this.py") execfile(path, {"__file__": path}) # noqa: F821 @@ -47,7 +83,28 @@ class Virtualenv(object): self.activate() def install(self, *requirements): - call(self.pip_path, "install", *requirements) + try: + self.working_set.require(*requirements) + except pkg_resources.ResolutionError: + pass + else: + return + + # `--prefer-binary` guards against race conditions when installation + # occurs while packages are in the process of being published. + call(self.pip_path, "install", "--prefer-binary", *requirements) def install_requirements(self, requirements_path): - call(self.pip_path, "install", "-r", requirements_path) + with open(requirements_path) as f: + try: + self.working_set.require(f.read()) + except pkg_resources.ResolutionError: + pass + else: + return + + # `--prefer-binary` guards against race conditions when installation + # occurs while packages are in the process of being published. + call( + self.pip_path, "install", "--prefer-binary", "-r", requirements_path + ) diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py index 55802461553..909d435b648 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wpt/wpt.py @@ -43,6 +43,9 @@ def load_commands(): def parse_args(argv, commands): parser = argparse.ArgumentParser() parser.add_argument("--venv", action="store", help="Path to an existing virtualenv to use") + parser.add_argument("--skip-venv-setup", action="store_true", + dest="skip_venv_setup", + help="Whether to use the virtualenv as-is. Must set --venv as well") parser.add_argument("--debug", action="store_true", help="Run the debugger in case of an exception") subparsers = parser.add_subparsers(dest="command") for command, props in iteritems(commands): @@ -77,15 +80,19 @@ def import_command(prog, command, props): return script, parser -def setup_virtualenv(path, props): +def setup_virtualenv(path, skip_venv_setup, props): + if skip_venv_setup and path is None: + raise ValueError("Must set --venv when --skip-venv-setup is used") + should_skip_setup = path is not None and skip_venv_setup if path is None: path = os.path.join(wpt_root, "_venv") - venv = virtualenv.Virtualenv(path) - venv.start() - for name in props["install"]: - venv.install(name) - for path in props["requirements"]: - venv.install_requirements(path) + venv = virtualenv.Virtualenv(path, should_skip_setup) + if not should_skip_setup: + venv.start() + for name in props["install"]: + venv.install(name) + for path in props["requirements"]: + venv.install_requirements(path) return venv @@ -105,7 +112,7 @@ def main(prog=None, argv=None): props = commands[command] venv = None if props["virtualenv"]: - venv = setup_virtualenv(main_args.venv, props) + venv = setup_virtualenv(main_args.venv, main_args.skip_venv_setup, props) script, parser = import_command(prog, command, props) if parser: if props["parse_known"]: diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py index aadcbb9b5a6..e11cae137b9 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/config.py @@ -75,7 +75,9 @@ def json_types(obj): return {key: json_types(value) for key, value in iteritems(obj)} if (isinstance(obj, string_types) or isinstance(obj, integer_types) or - isinstance(obj, float)): + isinstance(obj, float) or + isinstance(obj, bool) or + obj is None): return obj if isinstance(obj, list) or hasattr(obj, "__iter__"): return [json_types(value) for value in obj] diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/handlers.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/handlers.py index 2dd01e29338..e846ff807a6 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/handlers.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/handlers.py @@ -159,8 +159,8 @@ class FileHandler(object): rv = (self.load_headers(request, os.path.join(os.path.split(path)[0], "__dir__")) + self.load_headers(request, path)) - if not any(key.lower() == "content-type" for (key, _) in rv): - rv.insert(0, ("Content-Type", guess_content_type(path))) + if not any(key.lower() == b"content-type" for (key, _) in rv): + rv.insert(0, (b"Content-Type", guess_content_type(path).encode("ascii"))) return rv @@ -173,14 +173,14 @@ class FileHandler(object): use_sub = False try: - with open(headers_path) as headers_file: + with open(headers_path, "rb") as headers_file: data = headers_file.read() except IOError: return [] else: if use_sub: data = template(request, data, escape_type="none") - return [tuple(item.strip() for item in line.split(":", 1)) + return [tuple(item.strip() for item in line.split(b":", 1)) for line in data.splitlines() if line] def get_data(self, response, path, byte_ranges): diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py index aa6306a533e..1b1061ba7b7 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/request.py @@ -593,9 +593,7 @@ class MultiDict(dict): values = [values] for value in values: - if value.filename: - value = value - else: + if not value.filename: value = value.value self.add(key, value) return self diff --git a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py index 00a609b2950..efc65ef1801 100644 --- a/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py +++ b/chromium/third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/wptserve/wptserve/response.py @@ -8,7 +8,7 @@ from .constants import response_codes, h2_headers from .logger import get_logger from io import BytesIO -from six import binary_type, text_type, itervalues +from six import binary_type, text_type, integer_types, itervalues, PY3 from hyperframe.frame import HeadersFrame, DataFrame, ContinuationFrame from hpack.struct import HeaderTuple @@ -157,6 +157,8 @@ class Response(object): cookies = self.headers.get("Set-Cookie") parser = BaseCookie() for cookie in cookies: + if PY3: + cookie = cookie.decode("iso-8859-1") parser.load(cookie) if name in parser.keys(): @@ -237,18 +239,18 @@ class MultipartContent(object): def __init__(self, boundary=None, default_content_type=None): self.items = [] if boundary is None: - boundary = str(uuid.uuid4()) + boundary = text_type(uuid.uuid4()) self.boundary = boundary self.default_content_type = default_content_type def __call__(self): - boundary = "--" + self.boundary - rv = ["", boundary] + boundary = b"--" + self.boundary.encode("ascii") + rv = [b"", boundary] for item in self.items: - rv.append(str(item)) + rv.append(item.to_bytes()) rv.append(boundary) - rv[-1] += "--" - return "\r\n".join(rv) + rv[-1] += b"--" + return b"\r\n".join(rv) def append_part(self, data, content_type=None, headers=None): if content_type is None: @@ -265,6 +267,7 @@ class MultipartContent(object): class MultipartPart(object): def __init__(self, data, content_type=None, headers=None): + assert isinstance(data, binary_type), data self.headers = ResponseHeaders() if content_type is not None: @@ -272,7 +275,7 @@ class MultipartPart(object): if headers is not None: for name, value in headers: - if name.lower() == "content-type": + if name.lower() == b"content-type": func = self.headers.set else: func = self.headers.append @@ -280,13 +283,36 @@ class MultipartPart(object): self.data = data - def __str__(self): + def to_bytes(self): rv = [] - for item in self.headers: - rv.append("%s: %s" % item) - rv.append("") + for key, value in self.headers: + assert isinstance(key, binary_type) + assert isinstance(value, binary_type) + rv.append(b"%s: %s" % (key, value)) + rv.append(b"") rv.append(self.data) - return "\r\n".join(rv) + return b"\r\n".join(rv) + + +def _maybe_encode(s): + """Encodes a text-type string into binary data using iso-8859-1. + + Returns `str` in Python 2 and `bytes` in Python 3. The function is a no-op + if the argument already has a binary type. + """ + if isinstance(s, binary_type): + return s + + # Python 3 assumes iso-8859-1 when parsing headers, which will garble text + # with non ASCII characters. We try to encode the text back to binary. + # https://github.com/python/cpython/blob/273fc220b25933e443c82af6888eb1871d032fb8/Lib/http/client.py#L213 + if isinstance(s, text_type): + return s.encode("iso-8859-1") + + if isinstance(s, integer_types): + return b"%i" % (s,) + + raise TypeError("Unexpected value in ResponseHeaders: %r" % s) class ResponseHeaders(object): @@ -301,6 +327,8 @@ class ResponseHeaders(object): :param key: Name of the header to set :param value: Value to set the header to """ + key = _maybe_encode(key) + value = _maybe_encode(value) self.data[key.lower()] = (key, [value]) def append(self, key, value): @@ -310,6 +338,8 @@ class ResponseHeaders(object): :param key: Name of the header to add :param value: Value to set for the header """ + key = _maybe_encode(key) + value = _maybe_encode(value) if key.lower() in self.data: self.data[key.lower()][1].append(value) else: @@ -317,6 +347,7 @@ class ResponseHeaders(object): def get(self, key, default=missing): """Get the set values for a particular header.""" + key = _maybe_encode(key) try: return self[key] except KeyError: @@ -328,12 +359,15 @@ class ResponseHeaders(object): """Get a list of values for a particular header """ + key = _maybe_encode(key) return self.data[key.lower()][1] def __delitem__(self, key): + key = _maybe_encode(key) del self.data[key.lower()] def __contains__(self, key): + key = _maybe_encode(key) return key.lower() in self.data def __setitem__(self, key, value): @@ -623,6 +657,9 @@ class ResponseWriter(object): self.file_chunk_size = 32 * 1024 self.default_status = 200 + def _seen_header(self, name): + return self.encode(name.lower()) in self._headers_seen + def write_status(self, code, message=None): """Write out the status line of a response. @@ -648,19 +685,25 @@ class ResponseWriter(object): """ if not self._status_written: self.write_status(self.default_status) - self._headers_seen.add(name.lower()) - self.write("%s: %s\r\n" % (name, value)) + self._headers_seen.add(self.encode(name.lower())) + self.write(name) + self.write(b": ") + if isinstance(value, int): + self.write(text_type(value)) + else: + self.write(value) + self.write(b"\r\n") if not self._response.explicit_flush: self.flush() def write_default_headers(self): for name, f in [("Server", self._handler.version_string), ("Date", self._handler.date_time_string)]: - if name.lower() not in self._headers_seen: + if not self._seen_header(name): self.write_header(name, f()) if (isinstance(self._response.content, (binary_type, text_type)) and - "content-length" not in self._headers_seen): + not self._seen_header("content-length")): #Would be nice to avoid double-encoding here self.write_header("Content-Length", len(self.encode(self._response.content))) @@ -675,7 +718,7 @@ class ResponseWriter(object): self.write_default_headers() self.write("\r\n") - if "content-length" not in self._headers_seen: + if not self._seen_header("content-length"): self._response.close_connection = True if not self._response.explicit_flush: self.flush() @@ -735,7 +778,7 @@ class ResponseWriter(object): elif isinstance(data, text_type): return data.encode(self._response.encoding) else: - raise ValueError + raise ValueError("data %r should be text or binary, but is %s" % (data, type(data))) def flush(self): """Flush the output. Returns False if the flush failed due to diff --git a/chromium/third_party/blink/tools/blinkpy/w3c/test_importer.py b/chromium/third_party/blink/tools/blinkpy/w3c/test_importer.py index e4676e4d0ec..de02c959e26 100644 --- a/chromium/third_party/blink/tools/blinkpy/w3c/test_importer.py +++ b/chromium/third_party/blink/tools/blinkpy/w3c/test_importer.py @@ -21,6 +21,7 @@ from blinkpy.common.net.buildbot import current_build_link from blinkpy.common.net.git_cl import GitCL from blinkpy.common.net.network_transaction import NetworkTimeout from blinkpy.common.path_finder import PathFinder +from blinkpy.common.system.executive import ScriptError from blinkpy.common.system.log_utils import configure_logging from blinkpy.w3c.chromium_exportable_commits import exportable_commits_over_last_n_commits from blinkpy.w3c.common import read_credentials, is_testharness_baseline, is_file_exportable, WPT_GH_URL @@ -262,7 +263,14 @@ class TestImporter(object): return True _log.error('Cannot submit CL; aborting.') - self.git_cl.run(['set-close']) + try: + self.git_cl.run(['set-close']) + except ScriptError as e: + if e.output and 'Conflict: change is merged' in e.output: + _log.error('CL is already merged; treating as success.') + return True + else: + raise e return False def blink_try_bots(self): diff --git a/chromium/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/chromium/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py index 31647c56477..59279294328 100644 --- a/chromium/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py @@ -188,6 +188,45 @@ class TestImporterTest(LoggingTestCase): importer.git_cl.calls, [['git', 'cl', 'try'], ['git', 'cl', 'set-close']]) + def test_submit_cl_timeout_and_already_merged(self): + # Here we simulate a case where we timeout waiting for the CQ to submit a + # CL because we miss the notification that it was merged. We then get an + # error when trying to close the CL because it's already been merged. + host = MockHost() + host.filesystem.write_text_file(MOCK_WEB_TESTS + 'W3CImportExpectations', '') + importer = TestImporter(host) + # Define some error text that looks like a typical ScriptError. + git_error_text = ( + 'This is a git Script Error\n' + '...there is usually a stack trace here with some calls\n' + '...and maybe other calls\n' + 'And finally, there is the exception:\n' + 'GerritError: Conflict: change is merged\n' + ) + importer.git_cl = MockGitCL( + host, status='lgtm', git_error_output={'set-close': git_error_text}, + # Only the latest job for each builder is counted. + try_job_results={ + Build('cq-builder-a', 120): TryJobStatus('COMPLETED', 'FAILURE'), + Build('cq-builder-a', 123): TryJobStatus('COMPLETED', 'SUCCESS')}) + importer.git_cl.wait_for_closed_status = lambda: False + success = importer.run_commit_queue_for_cl() + # Since the CL is already merged, we absorb the error and treat it as success. + self.assertTrue(success) + self.assertLog([ + 'INFO: Triggering CQ try jobs.\n', + 'INFO: All jobs finished.\n', + 'INFO: CQ appears to have passed; trying to commit.\n', + 'ERROR: Cannot submit CL; aborting.\n', + 'ERROR: CL is already merged; treating as success.\n', + ]) + self.assertEqual(importer.git_cl.calls, [ + ['git', 'cl', 'try'], + ['git', 'cl', 'upload', '-f', '--send-mail'], + ['git', 'cl', 'set-commit'], + ['git', 'cl', 'set-close'], + ]) + def test_apply_exportable_commits_locally(self): # TODO(robertma): Consider using MockLocalWPT. host = MockHost() @@ -460,6 +499,7 @@ class TestImporterTest(LoggingTestCase): '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt', 'manifest', '--work', + '--no-download', '--tests-root', MOCK_WEB_TESTS + 'external/wpt', ] diff --git a/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py b/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py index 7e18f3b3712..4dc48e7dd23 100644 --- a/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py +++ b/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py @@ -7,6 +7,12 @@ The MANIFEST.json file contains metadata about files in web-platform-tests, such as what tests exist, and extra information about each test, including test type, options, URLs to use, and reference file paths if applicable. + +Naming conventions: +* A (file) path is a relative file system path from the root of WPT. +* A (test) URL is the path (with an optional query string) to the test on + wptserve relative to url_base. +Neither has a leading slash. """ import json @@ -17,14 +23,16 @@ from blinkpy.common.path_finder import PathFinder _log = logging.getLogger(__file__) - +# The default filename of manifest expected by `wpt`. +MANIFEST_NAME = 'MANIFEST.json' # The filename used for the base manifest includes the version as a # workaround for trouble landing huge changes to the base manifest when # the version changes. See https://crbug.com/876717. -BASE_MANIFEST_NAME = 'WPT_BASE_MANIFEST_5.json' +BASE_MANIFEST_NAME = 'WPT_BASE_MANIFEST_6.json' # TODO(robertma): Use the official wpt.manifest module. + class WPTManifest(object): """A simple abstraction of WPT MANIFEST.json. @@ -53,8 +61,6 @@ class WPTManifest(object): [[reference_url1, "=="], [reference_url2, "!="], ...] """ - - def __init__(self, json_content): self.raw_dict = json.loads(json_content) self.test_types = ('manual', 'reftest', 'testharness') @@ -63,15 +69,15 @@ class WPTManifest(object): """Finds manifest items for the given WPT path. Args: - path_in_wpt: A file path relative to the root of WPT. Note that this - is different from a WPT URL; a file path does not have a leading - slash or a query string. + path_in_wpt: A file path relative to the root of WPT. Returns: A list of manifest items, or None if not found. """ items = self.raw_dict['items'] for test_type in self.test_types: + if test_type not in items: + continue if path_in_wpt in items[test_type]: return items[test_type][path_in_wpt] return None @@ -80,7 +86,7 @@ class WPTManifest(object): """Finds the manifest item for the given WPT URL. Args: - url: A WPT URL (with the leading slash). + url: A WPT URL. Returns: A manifest item, or None if not found. @@ -112,8 +118,11 @@ class WPTManifest(object): url_items = {} if 'items' not in self.raw_dict: return url_items + items = self.raw_dict['items'] for test_type in self.test_types: - for records in self.raw_dict['items'][test_type].itervalues(): + if test_type not in items: + continue + for records in items[test_type].itervalues(): for item in filter(self._is_not_jsshell, records): url_items[self._get_url_from_item(item)] = item return url_items @@ -124,29 +133,20 @@ class WPTManifest(object): return frozenset(self.all_url_items().keys()) def is_test_file(self, path_in_wpt): + """Checks if path_in_wpt is a test file according to the manifest.""" + assert not path_in_wpt.startswith('/') return self._items_for_file_path(path_in_wpt) is not None def is_test_url(self, url): - """Checks if url is a valid test in the manifest. - - The url must be the WPT test name with a leading slash (/). - """ - if url[0] != '/': - raise Exception('Test url missing leading /: %s' % url) + """Checks if url is a valid test in the manifest.""" + assert not url.startswith('/') return url in self.all_urls() - def file_path_to_url_paths(self, path_in_wpt): - manifest_items = self._items_for_file_path(path_in_wpt) - assert manifest_items is not None - # Remove the leading slashes when returning. - return [self._get_url_from_item(item)[1:] - for item in manifest_items if self._is_not_jsshell(item)] - def is_slow_test(self, url): """Checks if a WPT is slow (long timeout) according to the manifest. Args: - url: A WPT URL (with the leading slash). + url: A WPT URL. Returns: True if the test is found and is slow, False otherwise. @@ -165,38 +165,50 @@ class WPTManifest(object): The return value is a list of (match/not-match, reference path in wpt) pairs, like: - [("==", "foo/bar/baz-match.html"), - ("!=", "foo/bar/baz-mismatch.html")] + [("==", "/foo/bar/baz-match.html"), + ("!=", "/foo/bar/baz-mismatch.html")] """ - all_items = self.raw_dict['items'] - if path_in_wpt not in all_items['reftest']: + items = self.raw_dict['items'] + if path_in_wpt not in items.get('reftest', {}): return [] reftest_list = [] - for item in all_items['reftest'][path_in_wpt]: + for item in items['reftest'][path_in_wpt]: for ref_path_in_wpt, expectation in item[1]: + # Ref URLs in MANIFEST should be absolute, but we double check + # just in case. + if not ref_path_in_wpt.startswith('/'): + ref_path_in_wpt = '/' + ref_path_in_wpt reftest_list.append((expectation, ref_path_in_wpt)) return reftest_list @staticmethod - def ensure_manifest(host): - """Updates the MANIFEST.json file, or generates if it does not exist.""" - finder = PathFinder(host.filesystem) - manifest_path = finder.path_from_web_tests('external', 'wpt', 'MANIFEST.json') - base_manifest_path = finder.path_from_web_tests('external', BASE_MANIFEST_NAME) + def ensure_manifest(host, path=None): + """Updates the MANIFEST.json file, or generates if it does not exist. - if not host.filesystem.exists(base_manifest_path): - _log.error('Manifest base not found at "%s".', base_manifest_path) - host.filesystem.write_text_file(base_manifest_path, '{}') + Args: + path: The path to a WPT root (relative to web_tests, optional). + """ + if path is None: + path = host.filesystem.join('external', 'wpt') + finder = PathFinder(host.filesystem) + wpt_path = finder.path_from_web_tests(path) + manifest_path = host.filesystem.join(wpt_path, MANIFEST_NAME) - # Unconditionally replace MANIFEST.json with the base manifest even if - # the former exists, to avoid regenerating the manifest from scratch - # when the manifest version changes. Remove the destination first as - # copyfile will fail if the two files are hardlinked or symlinked. + # Unconditionally delete local MANIFEST.json to avoid regenerating the + # manifest from scratch (when version is bumped) or invalid/out-of-date + # local manifest breaking the runner. if host.filesystem.exists(manifest_path): host.filesystem.remove(manifest_path) - host.filesystem.copyfile(base_manifest_path, manifest_path) - wpt_path = finder.path_from_web_tests('external', 'wpt') + # TODO(crbug.com/853815): perhaps also cache the manifest for wpt_internal. + if 'external' in path: + base_manifest_path = finder.path_from_web_tests('external', BASE_MANIFEST_NAME) + if not host.filesystem.exists(base_manifest_path): + _log.error('Manifest base not found at "%s".', base_manifest_path) + host.filesystem.write_text_file(base_manifest_path, '{}') + + host.filesystem.copyfile(base_manifest_path, manifest_path) + WPTManifest.generate_manifest(host, wpt_path) _log.debug('Manifest generation completed.') @@ -206,7 +218,7 @@ class WPTManifest(object): """Generates MANIFEST.json on the specified directory.""" finder = PathFinder(host.filesystem) wpt_exec_path = finder.path_from_blink_tools('blinkpy', 'third_party', 'wpt', 'wpt', 'wpt') - cmd = ['python', wpt_exec_path, 'manifest', '--work', '--tests-root', dest_path] + cmd = ['python', wpt_exec_path, 'manifest', '--work', '--no-download', '--tests-root', dest_path] # ScriptError will be raised if the command fails. host.executive.run_command( diff --git a/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py b/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py index ae3155bd9f3..eb9a5be65e5 100644 --- a/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py @@ -32,6 +32,7 @@ class WPTManifestUnitTest(unittest.TestCase): '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt', 'manifest', '--work', + '--no-download', '--tests-root', MOCK_WEB_TESTS + 'external/wpt', ] @@ -57,6 +58,7 @@ class WPTManifestUnitTest(unittest.TestCase): '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt', 'manifest', '--work', + '--no-download', '--tests-root', MOCK_WEB_TESTS + 'external/wpt', ] @@ -70,26 +72,43 @@ class WPTManifestUnitTest(unittest.TestCase): with self.assertRaises(ScriptError): WPTManifest.ensure_manifest(host) - def test_all_url_items_skips_jsshell_tests(self): + def test_ensure_manifest_takes_optional_dest(self): + host = MockHost() + WPTManifest.ensure_manifest(host, 'wpt_internal') + self.assertEqual( + host.executive.calls, + [ + [ + 'python', + '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt', + 'manifest', + '--work', + '--no-download', + '--tests-root', + MOCK_WEB_TESTS + 'wpt_internal', + ] + ] + ) + + def test_does_not_throw_when_missing_some_test_types(self): manifest_json = ''' { "items": { - "manual": {}, - "reftest": {}, "testharness": { "test.any.js": [ - ["/test.any.html", {}], - ["/test.any.js", {"jsshell": true}] + ["/test.any.html", {}] ] } } } ''' manifest = WPTManifest(manifest_json) + self.assertTrue(manifest.is_test_file('test.any.js')) self.assertEqual(manifest.all_url_items(), {u'/test.any.html': [u'/test.any.html', {}]}) + self.assertEqual(manifest.extract_reference_list('/foo/bar.html'), []) - def test_file_path_to_url_paths(self): + def test_all_url_items_skips_jsshell_tests(self): manifest_json = ''' { "items": { @@ -105,7 +124,5 @@ class WPTManifestUnitTest(unittest.TestCase): } ''' manifest = WPTManifest(manifest_json) - # Leading slashes should be stripped; and jsshell tests shouldn't be - # included. - self.assertEqual(manifest.file_path_to_url_paths('test.any.js'), - [u'test.any.html']) + self.assertEqual(manifest.all_url_items(), + {u'/test.any.html': [u'/test.any.html', {}]}) diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py b/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py index 661ccdbdce7..5b5eb403769 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/manager.py @@ -99,10 +99,17 @@ class Manager(object): self._printer.write_update('Collecting tests ...') running_all_tests = False - if self._options.manifest_update and (not args or any('external' in path for path in args)): - self._printer.write_update('Generating MANIFEST.json for web-platform-tests ...') - WPTManifest.ensure_manifest(self._port.host) - self._printer.write_update('Completed generating manifest.') + if self._options.manifest_update: + # TODO(robertma): Consolidate the two cases to `for wpt_path in + # WPT_DIRS` when external/wpt is moved to wpt. + if not args or any('external' in path for path in args): + self._printer.write_update('Generating MANIFEST.json for external/wpt...') + WPTManifest.ensure_manifest(self._port.host) + self._printer.write_update('Completed generating manifest.') + if not args or any('wpt_internal' in path for path in args): + self._printer.write_update('Generating MANIFEST.json for wpt_internal...') + WPTManifest.ensure_manifest(self._port.host, 'wpt_internal') + self._printer.write_update('Completed generating manifest.') self._printer.write_update('Collecting tests ...') try: diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py b/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py index fd4774ce253..76e5ff6065c 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/controllers/single_test_runner.py @@ -461,6 +461,10 @@ class SingleTestRunner(object): def _compare_image(self, expected_driver_output, driver_output): if not expected_driver_output.image or not expected_driver_output.image_hash: return [] + # The presence of an expected image, but a lack of an outputted image + # does not signify an error. content::BlinkTestController checks the + # image_hash, and upon a match simply skips recording the outputted + # image. This even occurs when results_directory is set. if not driver_output.image or not driver_output.image_hash: return [] diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py b/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py index cd64e3e4e07..200a5964370 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py @@ -117,7 +117,7 @@ class TestExpectationParser(object): line_number = 0 for line in expectations_string.split('\n'): line_number += 1 - test_expectation = TestExpectationLine.tokenize_line(filename, line, line_number) + test_expectation = TestExpectationLine.tokenize_line(filename, line, line_number, self._port) self._parse_line(test_expectation) expectation_lines.append(test_expectation) @@ -326,7 +326,7 @@ class TestExpectationLine(object): [('TEXT', 'Failure'), ('IMAGE', 'Failure'), ('IMAGE+TEXT', 'Failure'), ('AUDIO', 'Failure')]) @classmethod - def tokenize_line(cls, filename, expectation_string, line_number): + def tokenize_line(cls, filename, expectation_string, line_number, port): """Tokenizes a line from TestExpectations and returns an unparsed TestExpectationLine instance using the old format. The new format for a test expectation line is: @@ -447,6 +447,11 @@ class TestExpectationLine(object): if 'NeverFixTests' in filename and expectations != ['WONTFIX', 'SKIP']: warnings.append('Only WONTFIX expectations are allowed in NeverFixTests') + if 'SlowTests' in filename and port.is_wpt_test(name): + warnings.append( + 'WPT should not be added to SlowTests; they should be marked as ' + 'slow inside the test (see https://web-platform-tests.org/writing-tests/testharness-api.html#harness-timeout)') + if 'SlowTests' in filename and expectations != ['SLOW']: warnings.append('Only SLOW expectations are allowed in SlowTests') @@ -714,7 +719,7 @@ class TestExpectationsModel(object): def get_expectations_string(self, test): """Returns the expectations for the given test as an uppercase string. - If there are no expectations for the test, then "PASS" is returned. + If there are no expectations for the test, KeyError is raised. """ if self.get_expectation_line(test).is_extra_skipped_test: return 'NOTRUN' diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py b/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py index 8331deaa53f..9409dd63c9f 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/models/test_expectations_unittest.py @@ -354,7 +354,9 @@ class ExpectationSyntaxTests(Base): expectations = expectations or [] warnings = warnings or [] line_number = '1' - expectation_line = TestExpectationLine.tokenize_line(filename, line, line_number) + host = MockHost() + expectation_line = TestExpectationLine.tokenize_line( + filename, line, line_number, host.port_factory.get('test-win-win7', None)) self.assertEqual(expectation_line.warnings, warnings) self.assertEqual(expectation_line.name, name) self.assertEqual(expectation_line.filename, filename) @@ -386,6 +388,8 @@ class ExpectationSyntaxTests(Base): self.assert_tokenize_exp('foo.html [ Slow ]', specifiers=[], expectations=['SLOW'], filename='SlowTests') self.assert_tokenize_exp('foo.html [ Timeout Slow ]', specifiers=[], expectations=['SKIP', 'TIMEOUT'], warnings=[ 'Only SLOW expectations are allowed in SlowTests'], filename='SlowTests') + self.assert_tokenize_exp('external/wpt/foo.html [ Slow ]', name='external/wpt/foo.html', specifiers=[], expectations=['SLOW'], warnings=[ + 'WPT should not be added to SlowTests; they should be marked as slow inside the test (see https://web-platform-tests.org/writing-tests/testharness-api.html#harness-timeout)'], filename='SlowTests') def test_wontfix(self): self.assert_tokenize_exp( @@ -778,7 +782,8 @@ class TestExpectationSerializationTests(unittest.TestCase): unittest.TestCase.__init__(self, testFunc) def _tokenize(self, line): - return TestExpectationLine.tokenize_line('path', line, 0) + host = MockHost() + return TestExpectationLine.tokenize_line('path', line, 0, host.port_factory.get('test-win-win7', None)) def assert_round_trip(self, in_string, expected_string=None): expectation = self._tokenize(in_string) diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/base.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/base.py index d890cefeaa8..d5c57ac31c7 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/base.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/base.py @@ -46,7 +46,7 @@ from blinkpy.common import read_checksum_from_png from blinkpy.common.memoized import memoized from blinkpy.common.path_finder import PathFinder from blinkpy.common.system.path import abspath_to_uri -from blinkpy.w3c.wpt_manifest import WPTManifest +from blinkpy.w3c.wpt_manifest import WPTManifest, MANIFEST_NAME from blinkpy.web_tests.layout_package.bot_test_expectations import BotTestExpectationsFactory from blinkpy.web_tests.models.test_configuration import TestConfiguration from blinkpy.web_tests.models.test_expectations import TestExpectationParser @@ -102,6 +102,7 @@ SXG_FINGERPRINT = '55qC1nKu2A88ESbFmk5sTPQS/ScG+8DD7P+2bgFA9iM=' # And one for external/wpt/signed-exchange/resources/127.0.0.1.sxg.pem SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk=' + class Port(object): """Abstract class for Port-specific hooks for the web_test package.""" @@ -173,6 +174,22 @@ class Port(object): FLAG_EXPECTATIONS_PREFIX = 'FlagExpectations' + # The following is used for concetenating WebDriver test names. + WEBDRIVER_SUBTEST_SEPARATOR = '>>' + + # The following two constants must match. When adding a new WPT root, also + # remember to add an alias rule to third_party/wpt/wpt.config.json. + # WPT_DIRS maps WPT roots on the file system to URL prefixes on wptserve. + # The order matters: '/' MUST be the last URL prefix. + WPT_DIRS = collections.OrderedDict([ + ('wpt_internal', '/wpt_internal/'), + ('external/wpt', '/'), + ]) + # WPT_REGEX captures: 1. the root directory of WPT relative to web_tests + # (without a trailing slash), 2. the path of the test within WPT (without a + # leading slash). + WPT_REGEX = re.compile(r'^(?:virtual/[^/]+/)?(external/wpt|wpt_internal)/(.*)$') + # Because this is an abstract base class, arguments to functions may be # unused in this class - pylint: disable=unused-argument @@ -399,8 +416,8 @@ class Port(object): _log.error('httpd seems broken. Cannot run http tests.') return False return True - except OSError: - pass + except OSError as e: + _log.error('httpd launch error: ' + repr(e)) _log.error('No httpd found. Cannot run http tests.') return False @@ -437,6 +454,11 @@ class Port(object): executable = self._path_to_image_diff() # Although we are handed 'old', 'new', image_diff wants 'new', 'old'. command = [executable, '--diff', actual_filename, expected_filename, diff_filename] + # Notifies image_diff to allow a tolerance when calculating the pixel + # diff. To account for variances when the tests are ran on an actual + # GPU. + if self.get_option('fuzzy_diff'): + command.append('--fuzzy-diff') result = None err_str = None @@ -687,15 +709,13 @@ class Port(object): return reftest_list # Try to extract information from MANIFEST.json. - match = re.match(r'virtual/[^/]+/', test_name) - if match: - test_name = test_name[match.end(0):] - match = re.match(r'external/wpt/(.*)', test_name) + match = self.WPT_REGEX.match(test_name) if not match: return [] - path_in_wpt = match.group(1) - for expectation, ref_path_in_wpt in self._wpt_manifest().extract_reference_list(path_in_wpt): - ref_absolute_path = self._filesystem.join(self.web_tests_dir(), 'external/wpt' + ref_path_in_wpt) + wpt_path = match.group(1) + path_in_wpt = match.group(2) + for expectation, ref_path_in_wpt in self._wpt_manifest(wpt_path).extract_reference_list(path_in_wpt): + ref_absolute_path = self._filesystem.join(self.web_tests_dir(), wpt_path + ref_path_in_wpt) reftest_list.append((expectation, ref_absolute_path)) return reftest_list @@ -717,26 +737,29 @@ class Port(object): suites = self.virtual_test_suites() if paths: tests.extend(self._virtual_tests_matching_paths(paths, suites)) - if any('external' in path for path in paths): + if (any(wpt_path in path for wpt_path in self.WPT_DIRS for path in paths) + # TODO(robertma): Remove this special case when external/wpt is moved to wpt. + or any('external' in path for path in paths)): tests.extend(self._wpt_test_urls_matching_paths(paths)) else: tests.extend(self._all_virtual_tests(suites)) - tests.extend(['external/wpt' + test for test in self._wpt_manifest().all_urls()]) + tests.extend([wpt_path + self.TEST_PATH_SEPARATOR + test for wpt_path in self.WPT_DIRS + for test in self._wpt_manifest(wpt_path).all_urls()]) return tests def real_tests(self, paths): + """Find all real tests in paths except WPT.""" # When collecting test cases, skip these directories. - skipped_directories = set([ - 'platform', 'resources', 'support', 'script-tests', - 'reference', 'reftest', 'external' - ]) - is_non_wpt_real_test_file = lambda fs, dirname, filename: ( - self.is_test_file(fs, dirname, filename) - and not re.search(r'[/\\]external[/\\]wpt([/\\].*)?$', dirname) - ) + skipped_directories = set( + ['platform', 'resources', 'support', 'script-tests', + 'reference', 'reftest']) + # Also ignore all WPT directories. Note that this is only an + # optimization; is_non_wpt_test_file should skip WPT regardless. + skipped_directories |= set(self.WPT_DIRS) files = find_files.find(self._filesystem, self.web_tests_dir(), paths, skipped_directories, - is_non_wpt_real_test_file, self.test_key) + lambda _, dirname, filename: self.is_non_wpt_test_file(dirname, filename), + self.test_key) return [self.relative_test_filename(f) for f in files] @staticmethod @@ -758,56 +781,43 @@ class Port(object): '.htm', '.php', '.svg', '.mht', '.pdf', ]) - @staticmethod - def _has_supported_extension(filesystem, filename): + def _has_supported_extension(self, filename): """Returns True if filename is one of the file extensions we want to run a test on.""" - extension = filesystem.splitext(filename)[1] - return extension in Port.supported_file_extensions - - def is_test_file(self, filesystem, dirname, filename): - match = re.search(r'[/\\]external[/\\]wpt([/\\].*)?$', dirname) - if match: - if match.group(1): - path_in_wpt = match.group(1)[1:].replace('\\', '/') + '/' + filename - else: - path_in_wpt = filename - return self._wpt_manifest().is_test_file(path_in_wpt) - extension = filesystem.splitext(filename)[1] + extension = self._filesystem.splitext(filename)[1] + return extension in self.supported_file_extensions + + def is_non_wpt_test_file(self, dirname, filename): + # Convert dirname to a relative path to web_tests with slashes + # normalized and ensure it has a trailing slash. + normalized_test_dir = self.relative_test_filename(dirname) + self.TEST_PATH_SEPARATOR + if any(normalized_test_dir.startswith(d + self.TEST_PATH_SEPARATOR) for d in self.WPT_DIRS): + return False + extension = self._filesystem.splitext(filename)[1] if 'inspector-protocol' in dirname and extension == '.js': return True if 'devtools' in dirname and extension == '.js': return True - return Port._has_supported_extension( - filesystem, filename) and not Port.is_reference_html_file(filesystem, dirname, filename) - - def _convert_wpt_file_path_to_url_paths(self, file_path): - tests = [] - # Path separators are normalized by relative_test_filename(). - match = re.search(r'external/wpt/(.*)$', file_path) - if not match: - return [file_path] - urls = self._wpt_manifest().file_path_to_url_paths(match.group(1)) - for url in urls: - tests.append(file_path[0:match.start(1)] + url) - return tests + return (self._has_supported_extension(filename) and + not Port.is_reference_html_file(self._filesystem, dirname, filename)) @memoized - def _wpt_manifest(self): - manifest_path = self._filesystem.join(self.web_tests_dir(), 'external', 'wpt', 'MANIFEST.json') + def _wpt_manifest(self, path): + assert path in self.WPT_DIRS + # Convert '/' to the platform-specific separator. + path = self._filesystem.normpath(path) + manifest_path = self._filesystem.join(self.web_tests_dir(), path, MANIFEST_NAME) if not self._filesystem.exists(manifest_path): _log.error('Manifest not found at %s. Remove the --no-manifest-update argument to generate it.', manifest_path) return WPTManifest('{}') return WPTManifest(self._filesystem.read_text_file(manifest_path)) def is_slow_wpt_test(self, test_file): - match = re.match(r'virtual/[^/]+/', test_file) - if match: - test_file = test_file[match.end(0):] - # WPTManifest.is_slow_test() takes a WPT URL with the leading slash. - match = re.match(r'external/wpt(.*)', test_file) + match = self.WPT_REGEX.match(test_file) if not match: return False - return self._wpt_manifest().is_slow_test(match.group(1)) + wpt_path = match.group(1) + path_in_wpt = match.group(2) + return self._wpt_manifest(wpt_path).is_slow_test(path_in_wpt) def test_key(self, test_name): """Turns a test name into a pair of sublists: the natural sort key of the @@ -1179,7 +1189,7 @@ class Port(object): @staticmethod def is_wpt_test(test): """Whether a test is considered a web-platform-tests test.""" - return re.match(r'(virtual/[^/]+/)?external/wpt/', test) + return Port.WPT_REGEX.match(test) @staticmethod def should_use_wptserve(test): @@ -1209,6 +1219,7 @@ class Port(object): cmd = [httpd_path, '-t', '-f', self.path_to_apache_config_file(), + '-C', 'ServerRoot "%s"' % self.apache_server_root(), '-C', 'HttpProtocolOptions Unsafe', '-C', intentional_syntax_error] env = self.setup_environ_for_server() @@ -1400,6 +1411,10 @@ class Port(object): def path_to_generic_test_expectations_file(self): return self._filesystem.join(self.web_tests_dir(), 'TestExpectations') + @memoized + def path_to_webdriver_expectations_file(self): + return self._filesystem.join(self.web_tests_dir(), 'WebDriverExpectations') + def repository_path(self): """Returns the repository path for the chromium code base.""" return self._path_from_chromium_base('build') @@ -1419,6 +1434,14 @@ class Port(object): """ raise NotImplementedError('Port.path_to_apache') + def apache_server_root(self): + """Returns the root that the apache binary is installed to. + + This is used for the ServerRoot directive. + """ + executable = self.path_to_apache() + return self._filesystem.dirname(self._filesystem.dirname(executable)) + def path_to_apache_config_file(self): """Returns the full path to the apache configuration file. @@ -1603,39 +1626,47 @@ class Port(object): return '*' in path def _wpt_test_urls_matching_paths(self, paths): - tests = [] + # This is to make sure "external[\\/]?" can also match to external/wpt. + # TODO(robertma): Remove this special case when external/wpt is moved to wpt. + paths = [self._filesystem.join(path, 'wpt') if path.rstrip('\\/').endswith('external') + else path for path in paths] # '/' is used throughout this function instead of filesystem.sep as the WPT manifest always # uses '/' for paths (it is not OS dependent). if self._filesystem.sep != '/': paths = [path.replace(self._filesystem.sep, '/') for path in paths] - for test_url_path in self._wpt_manifest().all_urls(): - if test_url_path[0] == '/': - test_url_path = test_url_path[1:] + tests = [] + for wpt_path in self.WPT_DIRS: + tests += self._wpt_test_urls(wpt_path, paths) + return tests - full_test_url_path = 'external/wpt/' + test_url_path + def _wpt_test_urls(self, wpt_path, paths): + tests = [] + for test_url_path in self._wpt_manifest(wpt_path).all_urls(): + assert not test_url_path.startswith('/') + full_test_url_path = wpt_path + '/' + test_url_path for path in paths: - if 'external' not in path: + if not path.startswith(wpt_path): continue - wpt_path = path.replace('external/wpt/', '') + # Also remove the slash after wpt_path, if any. + path_in_wpt = path[len(wpt_path) + 1:] - # When `test_url_path` is test.any.html or test.any.worker.html and path is test.any.js + # When `test_url_path` is test.any.html etc., and `path_in_wpt` is test.any.js: matches_any_js_test = ( - self._wpt_manifest().is_test_file(wpt_path) - and test_url_path.startswith(re.sub(r'\.js$', '', wpt_path)) + self._wpt_manifest(wpt_path).is_test_file(path_in_wpt) + and test_url_path.startswith(re.sub(r'\.js$', '', path_in_wpt)) ) - # Get a list of directories for both paths, filter empty strings + # For all other path matches within WPT: + # Get a list of directories for both paths, filter empty strings. full_test_url_directories = filter(None, full_test_url_path.split('/')) path_directories = filter(None, path.split('/')) + test_is_in_path = path_directories == full_test_url_directories[0:len(path_directories)] - # For all other path matches within WPT - if matches_any_js_test or path_directories == full_test_url_directories[0:len(path_directories)]: - wpt_file_paths = self._convert_wpt_file_path_to_url_paths(test_url_path) - tests.extend('external/wpt/' + wpt_file_path for wpt_file_path in wpt_file_paths) - + if matches_any_js_test or test_is_in_path: + tests.append(full_test_url_path) return tests def _populate_virtual_suite(self, suite): @@ -1748,6 +1779,8 @@ class Port(object): raise TestRunException(exit_codes.SYS_DEPS_EXIT_STATUS, message) return result + def add_webdriver_subtest_suffix(self, test_name, subtest_name): + return test_name + self.WEBDRIVER_SUBTEST_SEPARATOR + subtest_name class VirtualTestSuite(object): diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py index 2bec6d06f26..ef04a8390cc 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py @@ -26,7 +26,6 @@ # (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 functools import json import optparse import unittest @@ -55,6 +54,25 @@ class PortTest(LoggingTestCase): return TestPort(host, **kwargs) return Port(host, port_name or 'baseport', **kwargs) + def test_validate_wpt_dirs(self): + # Keys should not have trailing slashes. + for wpt_path in Port.WPT_DIRS.keys(): + self.assertFalse(wpt_path.endswith('/')) + # Values should not be empty (except the last one). + for url_prefix in Port.WPT_DIRS.values()[:-1]: + self.assertNotEqual(url_prefix, '/') + self.assertEqual(Port.WPT_DIRS.values()[-1], '/') + + def test_validate_wpt_regex(self): + self.assertEquals(Port.WPT_REGEX.match('external/wpt/foo/bar.html').groups(), + ('external/wpt', 'foo/bar.html')) + self.assertEquals(Port.WPT_REGEX.match('virtual/test/external/wpt/foo/bar.html').groups(), + ('external/wpt', 'foo/bar.html')) + self.assertEquals(Port.WPT_REGEX.match('wpt_internal/foo/bar.html').groups(), + ('wpt_internal', 'foo/bar.html')) + self.assertEquals(Port.WPT_REGEX.match('virtual/test/wpt_internal/foo/bar.html').groups(), + ('wpt_internal', 'foo/bar.html')) + def test_setup_test_run(self): port = self.make_port() # This routine is a no-op. We just test it for coverage. @@ -467,25 +485,25 @@ class PortTest(LoggingTestCase): 'items': { 'testharness': { 'dom/ranges/Range-attributes.html': [ - ['/dom/ranges/Range-attributes.html', {}] + ['dom/ranges/Range-attributes.html', {}] ], 'dom/ranges/Range-attributes-slow.html': [ - ['/dom/ranges/Range-attributes-slow.html', {'timeout': 'long'}] + ['dom/ranges/Range-attributes-slow.html', {'timeout': 'long'}] ], 'console/console-is-a-namespace.any.js': [ - ['/console/console-is-a-namespace.any.html', {}], - ['/console/console-is-a-namespace.any.worker.html', {'timeout': 'long'}], + ['console/console-is-a-namespace.any.html', {}], + ['console/console-is-a-namespace.any.worker.html', {'timeout': 'long'}], ], 'html/parse.html': [ - ['/html/parse.html?run_type=uri', {}], - ['/html/parse.html?run_type=write', {'timeout': 'long'}], + ['html/parse.html?run_type=uri', {}], + ['html/parse.html?run_type=write', {'timeout': 'long'}], ], }, 'manual': {}, 'reftest': { 'html/dom/elements/global-attributes/dir_auto-EN-L.html': [ [ - '/html/dom/elements/global-attributes/dir_auto-EN-L.html', + 'html/dom/elements/global-attributes/dir_auto-EN-L.html', [ [ '/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html', @@ -498,9 +516,20 @@ class PortTest(LoggingTestCase): }, }})) filesystem.write_text_file(WEB_TEST_DIR + '/external/wpt/dom/ranges/Range-attributes.html', '') + filesystem.write_text_file(WEB_TEST_DIR + '/external/wpt/dom/ranges/Range-attributes-slow.html', '') filesystem.write_text_file(WEB_TEST_DIR + '/external/wpt/console/console-is-a-namespace.any.js', '') filesystem.write_text_file(WEB_TEST_DIR + '/external/wpt/common/blank.html', 'foo') + filesystem.write_text_file(WEB_TEST_DIR + '/wpt_internal/MANIFEST.json', json.dumps({ + 'items': { + 'testharness': { + 'dom/bar.html': [ + ['dom/bar.html', {}] + ] + } + }})) + filesystem.write_text_file(WEB_TEST_DIR + '/wpt_internal/dom/bar.html', 'baz') + def test_find_none_if_not_in_manifest(self): port = self.make_port(with_tests=True) PortTest._add_manifest_to_mock_file_system(port.host.filesystem) @@ -533,26 +562,29 @@ class PortTest(LoggingTestCase): self.assertEqual(port.tests(['external/csswg-test']), []) self.assertEqual(sorted(port.tests(['external/wpt'])), all_wpt) self.assertEqual(sorted(port.tests(['external/wpt/'])), all_wpt) - self.assertEqual(port.tests(['external/wpt/console']), - ['external/wpt/console/console-is-a-namespace.any.worker.html', - 'external/wpt/console/console-is-a-namespace.any.html']) - self.assertEqual(port.tests(['external/wpt/console/']), - ['external/wpt/console/console-is-a-namespace.any.worker.html', - 'external/wpt/console/console-is-a-namespace.any.html']) - self.assertEqual(port.tests(['external/wpt/console/console-is-a-namespace.any.js']), - ['external/wpt/console/console-is-a-namespace.any.worker.html', - 'external/wpt/console/console-is-a-namespace.any.html']) + self.assertEqual(sorted(port.tests(['external/wpt/console'])), + ['external/wpt/console/console-is-a-namespace.any.html', + 'external/wpt/console/console-is-a-namespace.any.worker.html']) + self.assertEqual(sorted(port.tests(['external/wpt/console/'])), + ['external/wpt/console/console-is-a-namespace.any.html', + 'external/wpt/console/console-is-a-namespace.any.worker.html']) + self.assertEqual(sorted(port.tests(['external/wpt/console/console-is-a-namespace.any.js'])), + ['external/wpt/console/console-is-a-namespace.any.html', + 'external/wpt/console/console-is-a-namespace.any.worker.html']) self.assertEqual(port.tests(['external/wpt/console/console-is-a-namespace.any.html']), ['external/wpt/console/console-is-a-namespace.any.html']) - self.assertEqual(port.tests(['external/wpt/dom']), + self.assertEqual(sorted(port.tests(['external/wpt/dom'])), ['external/wpt/dom/ranges/Range-attributes-slow.html', 'external/wpt/dom/ranges/Range-attributes.html']) - self.assertEqual(port.tests(['external/wpt/dom/']), + self.assertEqual(sorted(port.tests(['external/wpt/dom/'])), ['external/wpt/dom/ranges/Range-attributes-slow.html', 'external/wpt/dom/ranges/Range-attributes.html']) self.assertEqual(port.tests(['external/wpt/dom/ranges/Range-attributes.html']), ['external/wpt/dom/ranges/Range-attributes.html']) + # wpt_internal should work the same. + self.assertEqual(port.tests(['wpt_internal']), ['wpt_internal/dom/bar.html']) + def test_virtual_wpt_tests_paths(self): port = self.make_port(with_tests=True) PortTest._add_manifest_to_mock_file_system(port.host.filesystem) @@ -581,43 +613,37 @@ class PortTest(LoggingTestCase): self.assertEqual(port.tests(['virtual/virtual_wpt_dom/external/wpt/dom/ranges/Range-attributes.html']), ['virtual/virtual_wpt_dom/external/wpt/dom/ranges/Range-attributes.html']) - def test_is_test_file(self): - port = self.make_port(with_tests=True) - is_test_file = functools.partial(Port.is_test_file, port, port.host.filesystem) - self.assertTrue(is_test_file('', 'foo.html')) - self.assertTrue(is_test_file('', 'foo.svg')) - self.assertTrue(is_test_file('', 'test-ref-test.html')) - self.assertTrue(is_test_file('devtools', 'a.js')) - self.assertFalse(is_test_file('', 'foo.png')) - self.assertFalse(is_test_file('', 'foo-expected.html')) - self.assertFalse(is_test_file('', 'foo-expected.svg')) - self.assertFalse(is_test_file('', 'foo-expected.xht')) - self.assertFalse(is_test_file('', 'foo-expected-mismatch.html')) - self.assertFalse(is_test_file('', 'foo-expected-mismatch.svg')) - self.assertFalse(is_test_file('', 'foo-expected-mismatch.xhtml')) - self.assertFalse(is_test_file('', 'foo-ref.html')) - self.assertFalse(is_test_file('', 'foo-notref.html')) - self.assertFalse(is_test_file('', 'foo-notref.xht')) - self.assertFalse(is_test_file('', 'foo-ref.xhtml')) - self.assertFalse(is_test_file('', 'ref-foo.html')) - self.assertFalse(is_test_file('', 'notref-foo.xhr')) - - def test_is_test_file_in_wpt(self): - port = self.make_port(with_tests=True) - filesystem = port.host.filesystem - PortTest._add_manifest_to_mock_file_system(filesystem) + # wpt_internal should work the same. + self.assertEqual(port.tests(['virtual/virtual_wpt_dom/wpt_internal']), + ['virtual/virtual_wpt_dom/wpt_internal/dom/bar.html']) + self.assertEqual(port.tests(['virtual/virtual_wpt_dom/']), + dom_wpt + ['virtual/virtual_wpt_dom/wpt_internal/dom/bar.html']) - # A file not in MANIFEST.json is not a test even if it has .html suffix. - self.assertFalse(port.is_test_file(filesystem, WEB_TEST_DIR + '/external/wpt/common', 'blank.html')) - - # .js is not a test in general, but it is if MANIFEST.json contains an - # entry for it. - self.assertTrue(port.is_test_file(filesystem, WEB_TEST_DIR + '/external/wpt/console', 'console-is-a-namespace.any.js')) - - # A file in external/wpt, not a sub directory. - self.assertFalse(port.is_test_file(filesystem, WEB_TEST_DIR + '/external/wpt', 'testharness_runner.html')) - # A file in external/wpt_automation. - self.assertTrue(port.is_test_file(filesystem, WEB_TEST_DIR + '/external/wpt_automation', 'foo.html')) + def test_is_non_wpt_test_file(self): + port = self.make_port(with_tests=True) + self.assertTrue(port.is_non_wpt_test_file('', 'foo.html')) + self.assertTrue(port.is_non_wpt_test_file('', 'foo.svg')) + self.assertTrue(port.is_non_wpt_test_file('', 'test-ref-test.html')) + self.assertTrue(port.is_non_wpt_test_file('devtools', 'a.js')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo.png')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected.html')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected.svg')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected.xht')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected-mismatch.html')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected-mismatch.svg')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-expected-mismatch.xhtml')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-ref.html')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-notref.html')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-notref.xht')) + self.assertFalse(port.is_non_wpt_test_file('', 'foo-ref.xhtml')) + self.assertFalse(port.is_non_wpt_test_file('', 'ref-foo.html')) + self.assertFalse(port.is_non_wpt_test_file('', 'notref-foo.xhr')) + + self.assertFalse(port.is_non_wpt_test_file(WEB_TEST_DIR + '/external/wpt/common', 'blank.html')) + self.assertFalse(port.is_non_wpt_test_file(WEB_TEST_DIR + '/external/wpt/console', 'console-is-a-namespace.any.js')) + self.assertFalse(port.is_non_wpt_test_file(WEB_TEST_DIR + '/external/wpt', 'testharness_runner.html')) + self.assertTrue(port.is_non_wpt_test_file(WEB_TEST_DIR + '/external/wpt_automation', 'foo.html')) + self.assertFalse(port.is_non_wpt_test_file(WEB_TEST_DIR + '/wpt_internal/console', 'console-is-a-namespace.any.js')) def test_is_wpt_test(self): self.assertTrue(Port.is_wpt_test('external/wpt/dom/ranges/Range-attributes.html')) @@ -912,6 +938,15 @@ class PortTest(LoggingTestCase): self.assertTrue(port.skips_test('failures/expected/image.html')) + def test_add_webdriver_subtest_suffix(self): + port = self.make_port() + wb_test_name = "abd" + sub_test_name = "bar" + + full_webdriver_name = port.add_webdriver_subtest_suffix(wb_test_name, sub_test_name) + + self.assertEqual(full_webdriver_name, "abd>>bar") + class NaturalCompareTest(unittest.TestCase): def setUp(self): diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver.py index d966ec8cf9b..f7f0d68791d 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver.py @@ -30,7 +30,6 @@ import base64 import logging import re import shlex -import sys import time from blinkpy.common.system import path @@ -156,6 +155,7 @@ class Driver(object): port - reference back to the port object. worker_number - identifier for a particular worker/driver instance """ + self.WPT_DIRS = port.WPT_DIRS self._port = port self._worker_number = worker_number self._no_timeout = no_timeout @@ -176,6 +176,7 @@ class Driver(object): # "#LEAK". This leak detection is enabled only when the flag # --enable-leak-detection is passed to content_shell. self._leaked = False + self._leak_log = None # stderr reading is scoped on a per-test (not per-block) basis, so we store the accumulated # stderr output, as well as if we've seen #EOF on this driver instance. @@ -274,6 +275,7 @@ class Driver(object): pid=pid) def _get_crash_log(self, stdout, stderr, newer_than): + # pylint: disable=protected-access return self._port._get_crash_log(self._crashed_process_name, self._crashed_pid, stdout, stderr, newer_than) # FIXME: Seems this could just be inlined into callers. @@ -289,7 +291,6 @@ class Driver(object): HTTP_DIR = 'http/tests/' HTTP_LOCAL_DIR = 'http/tests/local/' HTTP_HOST_AND_PORTS = ('127.0.0.1', 8000, 8443) - WPT_DIR = 'external/wpt/' WPT_HOST_AND_PORTS = ('web-platform.test', 8001, 8444) def is_http_test(self, test_name): @@ -311,17 +312,29 @@ class Driver(object): return path.abspath_to_uri(self._port.host.platform, self._port.abspath_for_test(test_name)) if using_wptserve: - test_dir_prefix = self.WPT_DIR + for wpt_path, url_prefix in self.WPT_DIRS.items(): + # The keys of WPT_DIRS do not have trailing slashes. + wpt_path += '/' + if test_name.startswith(wpt_path): + test_dir_prefix = wpt_path + test_url_prefix = url_prefix + break + else: + # We really shouldn't reach here, but in case we do, fail gracefully. + _log.error('Unrecognized WPT test name: %s', test_name) + test_dir_prefix = 'external/wpt/' + test_url_prefix = '/' hostname, insecure_port, secure_port = self.WPT_HOST_AND_PORTS else: test_dir_prefix = self.HTTP_DIR + test_url_prefix = '/' hostname, insecure_port, secure_port = self.HTTP_HOST_AND_PORTS relative_path = test_name[len(test_dir_prefix):] if '/https/' in test_name or '.https.' in test_name or '.serviceworker.' in test_name: - return 'https://%s:%d/%s' % (hostname, secure_port, relative_path) - return 'http://%s:%d/%s' % (hostname, insecure_port, relative_path) + return 'https://%s:%d%s%s' % (hostname, secure_port, test_url_prefix, relative_path) + return 'http://%s:%d%s%s' % (hostname, insecure_port, test_url_prefix, relative_path) def _get_uri_prefixes(self, hostname, insecure_port, secure_port): """Returns the HTTP and HTTPS URI prefix for a hostname.""" @@ -347,7 +360,10 @@ class Driver(object): return self.HTTP_DIR + uri[len(prefix):] for prefix in self._get_uri_prefixes(*self.WPT_HOST_AND_PORTS): if uri.startswith(prefix): - return self.WPT_DIR + uri[len(prefix):] + url_path = '/' + uri[len(prefix):] + for wpt_path, url_prefix in self.WPT_DIRS.items(): + if url_path.startswith(url_prefix): + return wpt_path + '/' + url_path[len(url_prefix):] raise NotImplementedError('unknown url type: %s' % uri) def has_crashed(self): @@ -381,7 +397,6 @@ class Driver(object): self._crashed_process_name = None self._crashed_pid = None self._leaked = False - self._leak_log = None cmd_line = self.cmd_line(per_test_args) self._server_process = self._port.server_process_constructor( self._port, server_name, cmd_line, environment, more_logging=self._port.get_option('driver_logging')) @@ -579,6 +594,7 @@ class Driver(object): if out_line[-1] != '\n': _log.error( 'Last character read from DRT stdout line was not a newline! This indicates either a NRWT or DRT bug.') + # pylint: disable=protected-access content_length_before_header_check = block._content_length self._process_stdout_line(block, out_line) # FIXME: Unlike HTTP, DRT dumps the content right after printing a Content-Length header. diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver_unittest.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver_unittest.py index 45fe2ecda67..9e5d1bfcf83 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/driver_unittest.py @@ -38,6 +38,8 @@ from blinkpy.web_tests.port.server_process_mock import MockServerProcess class DriverTest(unittest.TestCase): + # pylint: disable=protected-access + def make_port(self): return Port(MockSystemHost(), 'test', optparse.Values({'configuration': 'Release'})) @@ -62,9 +64,18 @@ class DriverTest(unittest.TestCase): self.assertEqual(driver.test_to_uri('http/tests/https/bar.html'), 'https://127.0.0.1:8443/https/bar.html') self.assertEqual(driver.test_to_uri('http/tests/bar.https.html'), 'https://127.0.0.1:8443/bar.https.html') self.assertEqual(driver.test_to_uri('http/tests/barhttps.html'), 'http://127.0.0.1:8000/barhttps.html') - self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.html'), 'http://web-platform.test:8001/foo/bar.html') - self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.https.html'), 'https://web-platform.test:8444/foo/bar.https.html') - self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.serviceworker.html'), 'https://web-platform.test:8444/foo/bar.serviceworker.html') + self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.html'), + 'http://web-platform.test:8001/foo/bar.html') + self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.https.html'), + 'https://web-platform.test:8444/foo/bar.https.html') + self.assertEqual(driver.test_to_uri('external/wpt/foo/bar.serviceworker.html'), + 'https://web-platform.test:8444/foo/bar.serviceworker.html') + self.assertEqual(driver.test_to_uri('wpt_internal/foo/bar.html'), + 'http://web-platform.test:8001/wpt_internal/foo/bar.html') + self.assertEqual(driver.test_to_uri('wpt_internal/foo/bar.https.html'), + 'https://web-platform.test:8444/wpt_internal/foo/bar.https.html') + self.assertEqual(driver.test_to_uri('wpt_internal/foo/bar.serviceworker.html'), + 'https://web-platform.test:8444/wpt_internal/foo/bar.serviceworker.html') def test_uri_to_test(self): port = self.make_port() @@ -73,9 +84,18 @@ class DriverTest(unittest.TestCase): self.assertEqual(driver.uri_to_test('http://127.0.0.1:8000/foo.html'), 'http/tests/foo.html') self.assertEqual(driver.uri_to_test('https://127.0.0.1:8443/https/bar.html'), 'http/tests/https/bar.html') self.assertEqual(driver.uri_to_test('https://127.0.0.1:8443/bar.https.html'), 'http/tests/bar.https.html') - self.assertEqual(driver.uri_to_test('http://web-platform.test:8001/foo/bar.html'), 'external/wpt/foo/bar.html') - self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/foo/bar.https.html'), 'external/wpt/foo/bar.https.html') - self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/foo/bar.serviceworker.html'), 'external/wpt/foo/bar.serviceworker.html') + self.assertEqual(driver.uri_to_test('http://web-platform.test:8001/foo/bar.html'), + 'external/wpt/foo/bar.html') + self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/foo/bar.https.html'), + 'external/wpt/foo/bar.https.html') + self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/foo/bar.serviceworker.html'), + 'external/wpt/foo/bar.serviceworker.html') + self.assertEqual(driver.uri_to_test('http://web-platform.test:8001/wpt_internal/foo/bar.html'), + 'wpt_internal/foo/bar.html') + self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/wpt_internal/foo/bar.https.html'), + 'wpt_internal/foo/bar.https.html') + self.assertEqual(driver.uri_to_test('https://web-platform.test:8444/wpt_internal/foo/bar.serviceworker.html'), + 'wpt_internal/foo/bar.serviceworker.html') def test_read_block(self): port = self.make_port() @@ -272,22 +292,22 @@ class CoalesceRepeatedSwitchesTest(unittest.TestCase): def test_multiple_enable_features(self): self._assert_coalesced_switches( - [ '--A', '--enable-features=Z,X', - '--enable-features=Y', '--X', '--enable-features=X,Y', - '--enable-features=X', '--enable-features=X,X'], + ['--A', '--enable-features=Z,X', + '--enable-features=Y', '--X', '--enable-features=X,Y', + '--enable-features=X', '--enable-features=X,X'], ['--A', '--X', '--enable-features=X,Y,Z']) def test_multiple_disable_features(self): self._assert_coalesced_switches( - [ '--A', '--disable-features=Z,X', - '--disable-features=Y', '--X', '--disable-features=X,Y', - '--disable-features=X', '--disable-features=X,X'], + ['--A', '--disable-features=Z,X', + '--disable-features=Y', '--X', '--disable-features=X,Y', + '--disable-features=X', '--disable-features=X,X'], ['--A', '--X', '--disable-features=X,Y,Z']) def test_enable_and_disable_features(self): # The coalescing of --enable-features and --disable-features is # independent (may both enable and disable the same feature). self._assert_coalesced_switches( - [ '--A', '--disable-features=Z','--disable-features=E,X', - '--enable-features=Y', '--X', '--enable-features=X,Y'], + ['--A', '--disable-features=Z', '--disable-features=E,X', + '--enable-features=Y', '--X', '--enable-features=X,Y'], ['--A', '--X', '--enable-features=X,Y', '--disable-features=E,X,Z']) diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/factory.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/factory.py index 0aeaa03e405..fb58b9e1de9 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/factory.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/factory.py @@ -126,6 +126,8 @@ def configuration_options(): help='Specify the target build subdirectory under src/out/'), optparse.make_option('--release', action='store_const', const='Release', dest='configuration', help='Set the configuration to Release'), + optparse.make_option('--no-xvfb', action='store_false', dest='use_xvfb', default=True, + help='Do not run tests with Xvfb'), ] @@ -147,7 +149,7 @@ def _check_configuration_and_target(host, options): expected_configuration = getattr(options, 'configuration') if expected_configuration not in (None, gn_configuration): raise ValueError('Configuration does not match the GN build args. ' - 'Expected "%s" but got "%s".' % (gn_configuration, expected_configuration)) + 'Expected "%s" but got "%s".' % (expected_configuration, gn_configuration)) options.configuration = gn_configuration return diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py index 5233f4dc508..dd5b05397b8 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py @@ -146,24 +146,9 @@ class _TargetHost(object): ssh_args=forwarding_flags, stderr=subprocess.PIPE) - # Copy content_shell package to the device. - device_package_path = \ - os.path.join('/data', os.path.basename(CONTENT_SHELL_PACKAGE_PATH)) - self._target.PutFile( - os.path.join(build_path, CONTENT_SHELL_PACKAGE_PATH), - device_package_path) - - pm_install = self._target.RunCommandPiped( - ['pm', 'install', device_package_path], - stderr=subprocess.PIPE) - output = pm_install.stderr.readlines() - pm_install.wait() - - if pm_install.returncode != 0: - # Don't error out if the package already exists on the device. - if len(output) != 1 or 'ErrAlreadyExists' not in output[0]: - raise Exception('Failed to install content_shell: %s' % \ - '\n'.join(output)) + package_path = os.path.join(build_path, CONTENT_SHELL_PACKAGE_PATH) + self._target.InstallPackage(package_path, "content_shell", + package_deps=[]) # Process will be forked for each worker, which may make QemuTarget # unusable (e.g. waitpid() for qemu process returns ECHILD after diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/linux.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/linux.py index 29a3d196a88..9e39ed7b632 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/linux.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/linux.py @@ -110,13 +110,15 @@ class LinuxPort(base.Port): def setup_test_run(self): super(LinuxPort, self).setup_test_run() - if not self._start_xvfb(): - return SYS_DEPS_EXIT_STATUS + if self.get_option('use_xvfb'): + if not self._start_xvfb(): + return SYS_DEPS_EXIT_STATUS self._setup_dummy_home_dir() def clean_up_test_run(self): super(LinuxPort, self).clean_up_test_run() - self._stop_xvfb(save_logs=False) + if self.get_option('use_xvfb'): + self._stop_xvfb(save_logs=False) self._clean_up_dummy_home_dir() # diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac.py index 149bbca69da..3817fba26e4 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac.py @@ -91,13 +91,12 @@ class MacPort(base.Port): # def path_to_apache(self): - return '/usr/sbin/httpd' + return self._path_from_chromium_base( + 'third_party', 'apache-mac', 'bin', 'httpd') def path_to_apache_config_file(self): - config_file_basename = 'apache2-httpd-' + self._apache_version() - if self.host.platform.os_version in ['mac10.13', 'mac10.14']: - config_file_basename += '-php7' - return self._filesystem.join(self.apache_config_directory(), config_file_basename + '.conf') + config_file_basename = 'apache2-httpd-%s-php7.conf' % (self._apache_version(),) + return self._filesystem.join(self.apache_config_directory(), config_file_basename) def _path_to_driver(self, target=None): return self._build_path_with_target(target, self.driver_name() + '.app', 'Contents', 'MacOS', self.driver_name()) diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac_unittest.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac_unittest.py index 7cf62d3ba76..670dbfe6e40 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac_unittest.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/mac_unittest.py @@ -56,13 +56,6 @@ class MacPortTest(port_testcase.PortTestCase): def test_path_to_apache_config_file(self): port = self.make_port() - port._apache_version = lambda: '2.2' # pylint: disable=protected-access - self.assertEqual( - port.path_to_apache_config_file(), - '/mock-checkout/third_party/blink/tools/apache_config/apache2-httpd-2.2.conf') - - def test_path_to_apache_config_file_on_10_13(self): - port = self.make_port(os_version='mac10.13', port_name='mac') port._apache_version = lambda: '2.4' # pylint: disable=protected-access self.assertEqual( port.path_to_apache_config_file(), diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py index 11378b8d31a..e8ae31a6976 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py @@ -66,7 +66,7 @@ class PortTestCase(LoggingTestCase): def make_port(self, host=None, port_name=None, options=None, os_name=None, os_version=None, **kwargs): host = host or MockSystemHost(os_name=(os_name or self.os_name), os_version=(os_version or self.os_version)) - options = options or optparse.Values({'configuration': 'Release'}) + options = options or optparse.Values({'configuration': 'Release', 'use_xvfb': True}) port_name = port_name or self.port_name port_name = self.port_maker.determine_full_port_name(host, options, port_name) return self.port_maker(host, port_name, options=options, **kwargs) diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/port/test.py b/chromium/third_party/blink/tools/blinkpy/web_tests/port/test.py index f7b6b6de907..0d777d77c43 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/port/test.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/port/test.py @@ -31,7 +31,6 @@ import time from blinkpy.common import exit_codes from blinkpy.common.system.crash_logs import CrashLogs -from blinkpy.web_tests.models import test_run_results from blinkpy.web_tests.models.test_configuration import TestConfiguration from blinkpy.web_tests.port.base import Port, VirtualTestSuite from blinkpy.web_tests.port.driver import DeviceFailure, Driver, DriverOutput @@ -513,6 +512,7 @@ class TestPort(Port): args=['--virtual-arg'], references_use_default_args=True), VirtualTestSuite(prefix='virtual_wpt', base='external/wpt', args=['--virtual-arg']), VirtualTestSuite(prefix='virtual_wpt_dom', base='external/wpt/dom', args=['--virtual-arg']), + VirtualTestSuite(prefix='virtual_wpt_dom', base='wpt_internal/dom', args=['--virtual-arg']), ] diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py b/chromium/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py index 00a33b66d18..65dd5348fc7 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/run_web_tests.py @@ -279,6 +279,14 @@ def parse_args(args): default=None, help='Exit after the first N failures instead of running all tests'), optparse.make_option( + '--fuzzy-diff', + action='store_true', + default=False, + help=('When running tests on an actual GPU, variance in pixel ' + 'output can leads image differences causing failed expectations. ' + 'Instead a fuzzy diff is used to account for this variance. ' + 'See tools/imagediff/image_diff.cc')), + optparse.make_option( '--ignore-builder-category', action='store', help=('The category of builders to use with the --ignore-flaky-tests option ' diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py b/chromium/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py index eb3e93c2eb3..0a532501870 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/servers/apache_http.py @@ -53,7 +53,6 @@ class ApacheHTTP(server_base.ServerBase): self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name) executable = self._port_obj.path_to_apache() - server_root = self._filesystem.dirname(self._filesystem.dirname(executable)) test_dir = self._port_obj.web_tests_dir() document_root = self._filesystem.join(test_dir, 'http', 'tests') @@ -75,7 +74,7 @@ class ApacheHTTP(server_base.ServerBase): start_cmd = [ executable, '-f', '%s' % self._port_obj.path_to_apache_config_file(), - '-C', 'ServerRoot "%s"' % server_root, + '-C', 'ServerRoot "%s"' % self._port_obj.apache_server_root(), '-C', 'DocumentRoot "%s"' % document_root, '-c', 'Alias /js-test-resources "%s/resources"' % test_dir, '-c', 'Alias /geolocation-api/js-test-resources "%s/geolocation-api/resources"' % test_dir, @@ -180,6 +179,7 @@ class ApacheHTTP(server_base.ServerBase): proc = self._executive.popen([self._port_obj.path_to_apache(), '-f', self._port_obj.path_to_apache_config_file(), + '-C', 'ServerRoot "%s"' % self._port_obj.apache_server_root(), '-c', 'PidFile "%s"' % self._pid_file, '-k', 'stop']) _, err = proc.communicate() diff --git a/chromium/third_party/blink/tools/blinkpy/web_tests/try_flag.py b/chromium/third_party/blink/tools/blinkpy/web_tests/try_flag.py index 81b4c88cfc1..9910eec2f31 100644 --- a/chromium/third_party/blink/tools/blinkpy/web_tests/try_flag.py +++ b/chromium/third_party/blink/tools/blinkpy/web_tests/try_flag.py @@ -70,7 +70,8 @@ class TryFlag(object): result = set() path = self._flag_expectations_path() for line in self._filesystem.read_text_file(path).split('\n'): - expectation_line = TestExpectationLine.tokenize_line(path, line, 0) + expectation_line = TestExpectationLine.tokenize_line( + path, line, 0, self._host.port_factory.get()) test_name = expectation_line.name if test_name: result.add(test_name) |