542 lines
27 KiB
Python
542 lines
27 KiB
Python
# Copyright (C) 2010 Google Inc. All rights reserved.
|
|
# Copyright (C) 2013-2019 Apple Inc. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are
|
|
# met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above
|
|
# copyright notice, this list of conditions and the following disclaimer
|
|
# in the documentation and/or other materials provided with the
|
|
# distribution.
|
|
# * Neither the Google name nor the names of its
|
|
# contributors may be used to endorse or promote products derived from
|
|
# this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import atexit
|
|
import glob
|
|
import logging
|
|
import os
|
|
import re
|
|
import sys
|
|
import time
|
|
|
|
from webkitcorepy import Version
|
|
|
|
from webkitpy.common.system.crashlogs import CrashLogs
|
|
from webkitpy.common.system.systemhost import SystemHost
|
|
from webkitpy.common.system.path import abspath_to_uri, cygpath
|
|
from webkitpy.common.version_name_map import VersionNameMap
|
|
from webkitpy.port.apple import ApplePort
|
|
from webkitpy.port.config import apple_additions
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
|
|
try:
|
|
import _winreg
|
|
import win32com.client
|
|
except ImportError:
|
|
try:
|
|
import winreg as _winreg
|
|
import win32com.client
|
|
except ImportError:
|
|
_log.debug("Not running on native Windows.")
|
|
|
|
|
|
class WinPort(ApplePort):
|
|
port_name = "win"
|
|
|
|
VERSION_MIN = Version(5, 1)
|
|
VERSION_MAX = Version(10)
|
|
|
|
ARCHITECTURES = ['x86', 'x86_64']
|
|
|
|
DEFAULT_ARCHITECTURE = 'x86_64'
|
|
|
|
CRASH_LOG_PREFIX = "CrashLog"
|
|
|
|
if sys.platform.startswith('win'):
|
|
POST_MORTEM_DEBUGGER_KEY = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug'
|
|
WOW64_POST_MORTEM_DEBUGGER_KEY = r'SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug'
|
|
WINDOWS_ERROR_REPORTING_KEY = r'SOFTWARE\Microsoft\Windows\Windows Error Reporting'
|
|
WOW64_WINDOWS_ERROR_REPORTING_KEY = r'SOFTWARE\Wow6432Node\Microsoft\Windows\Windows Error Reporting'
|
|
_HKLM = _winreg.HKEY_LOCAL_MACHINE
|
|
_HKCU = _winreg.HKEY_CURRENT_USER
|
|
_REG_DWORD = _winreg.REG_DWORD
|
|
_REG_SZ = _winreg.REG_SZ
|
|
else:
|
|
POST_MORTEM_DEBUGGER_KEY = "/%s/SOFTWARE/Microsoft/Windows NT/CurrentVersion/AeDebug/%s"
|
|
WOW64_POST_MORTEM_DEBUGGER_KEY = "/%s/SOFTWARE/Wow6432Node/Microsoft/Windows NT/CurrentVersion/AeDebug/%s"
|
|
WINDOWS_ERROR_REPORTING_KEY = "/%s/SOFTWARE/Microsoft/Windows/Windows Error Reporting/%s"
|
|
WOW64_WINDOWS_ERROR_REPORTING_KEY = "/%s/SOFTWARE/Wow6432Node/Microsoft/Windows/Windows Error Reporting/%s"
|
|
_HKLM = "HKLM"
|
|
_HKCU = "HKCU"
|
|
_REG_DWORD = "-d"
|
|
_REG_SZ = "-s"
|
|
|
|
previous_debugger_values = {}
|
|
previous_wow64_debugger_values = {}
|
|
|
|
previous_error_reporting_values = {}
|
|
previous_wow64_error_reporting_values = {}
|
|
|
|
def __init__(self, host, port_name, **kwargs):
|
|
ApplePort.__init__(self, host, port_name, **kwargs)
|
|
if len(port_name.split('-')) > 1:
|
|
self._os_version = VersionNameMap.map(host.platform).from_name(port_name.split('-')[1])[1]
|
|
else:
|
|
self._os_version = self.host.platform.os_version
|
|
|
|
def do_text_results_differ(self, expected_text, actual_text):
|
|
# Sanity was restored in WebKitTestRunner, so we don't need this hack there.
|
|
if not self.get_option('webkit_test_runner'):
|
|
# Windows does not have an EDITING DELEGATE, so strip those messages to make more tests pass.
|
|
# It's possible other ports might want this, and if so, this could move down into WebKitPort.
|
|
delegate_regexp = re.compile("^EDITING DELEGATE: .*?\n", re.MULTILINE)
|
|
expected_text = delegate_regexp.sub("", expected_text)
|
|
actual_text = delegate_regexp.sub("", actual_text)
|
|
return ApplePort.do_text_results_differ(self, expected_text, actual_text)
|
|
|
|
def default_baseline_search_path(self, **kwargs):
|
|
version_name_map = VersionNameMap.map(self.host.platform)
|
|
if self._os_version < self.VERSION_MIN or self._os_version > self.VERSION_MAX:
|
|
fallback_versions = [self._os_version] if self._os_version else []
|
|
else:
|
|
sorted_versions = sorted(version_name_map.mapping_for_platform(platform=self.port_name).values())
|
|
fallback_versions = sorted_versions[sorted_versions.index(self._os_version):]
|
|
|
|
fallback_names = ['win-' + version_name_map.to_name(version, platform=self.port_name).lower().replace(' ', '') for version in fallback_versions]
|
|
fallback_names.append('win')
|
|
|
|
# FIXME: The AppleWin port falls back to AppleMac for some results. Eventually we'll have a shared 'apple' port.
|
|
if self.get_option('webkit_test_runner'):
|
|
fallback_names.insert(0, 'win-wk2')
|
|
fallback_names.append('mac-wk2')
|
|
# Note we do not add 'wk2' here, even though it's included in _skipped_search_paths().
|
|
# FIXME: Perhaps we should get this list from MacPort?
|
|
fallback_names.append('mac')
|
|
result = list(map(self._webkit_baseline_path, fallback_names))
|
|
if apple_additions() and getattr(apple_additions(), "layout_tests_path", None):
|
|
result.insert(0, self._filesystem.join(apple_additions().layout_tests_path(), self.port_name))
|
|
return result
|
|
|
|
def setup_environ_for_server(self, server_name=None):
|
|
env = super(WinPort, self).setup_environ_for_server(server_name)
|
|
env['XML_CATALOG_FILES'] = '' # work around missing /etc/catalog <rdar://problem/4292995>
|
|
return env
|
|
|
|
def environment_for_api_tests(self):
|
|
env = super(WinPort, self).environment_for_api_tests()
|
|
for variable in ['SYSTEMROOT', 'WEBKIT_LIBRARIES']:
|
|
self._copy_value_from_environ_if_set(env, variable)
|
|
return env
|
|
|
|
def operating_system(self):
|
|
return 'win'
|
|
|
|
def _port_flag_for_scripts(self):
|
|
if self.architecture() == 'x86_64':
|
|
return '--64-bit'
|
|
return None
|
|
|
|
def show_results_html_file(self, results_filename):
|
|
self._run_script('run-safari', [abspath_to_uri(SystemHost().platform, results_filename)])
|
|
|
|
def _build_path(self, *comps):
|
|
"""Returns the full path to the test driver (DumpRenderTree)."""
|
|
root_directory = self.get_option('_cached_root') or self.get_option('root')
|
|
if not root_directory:
|
|
ApplePort._build_path(self, *comps) # Sets option _cached_root
|
|
binary_directory = 'bin32'
|
|
if self.architecture() == 'x86_64':
|
|
binary_directory = 'bin64'
|
|
root_directory = self._filesystem.join(self.get_option('_cached_root'), binary_directory)
|
|
self.set_option('_cached_root', root_directory)
|
|
|
|
return self._filesystem.join(root_directory, *comps)
|
|
|
|
def is_cygwin(self):
|
|
"""Return whether current platform is Cygwin or not"""
|
|
return self.host.platform.is_cygwin()
|
|
|
|
# Note: These are based on the stock XAMPP locations for these files.
|
|
def _uses_apache(self):
|
|
return True
|
|
|
|
def _path_to_apache(self):
|
|
root = os.environ.get('XAMPP_ROOT', 'C:\\xampp')
|
|
path = self._filesystem.join(root, 'apache', 'bin', 'httpd.exe')
|
|
if self._filesystem.exists(path):
|
|
return path
|
|
_log.error('Could not find apache in the expected location. (path=%s)' % path)
|
|
return None
|
|
|
|
def _path_to_lighttpd(self):
|
|
return "/usr/sbin/lighttpd"
|
|
|
|
def _path_to_lighttpd_modules(self):
|
|
return "/usr/lib/lighttpd"
|
|
|
|
def _path_to_lighttpd_php(self):
|
|
return "/usr/bin/php-cgi"
|
|
|
|
def _path_to_default_image_diff(self):
|
|
return self._build_path('ImageDiff.exe')
|
|
|
|
API_TEST_BINARY_NAMES = ['TestWTF.exe', 'TestWebCore.exe', 'TestWebKitLegacy.exe']
|
|
|
|
def path_to_api_test_binaries(self):
|
|
return {binary.split('.')[0]: self._build_path(binary) for binary in self.API_TEST_BINARY_NAMES}
|
|
|
|
def test_search_path(self, **kwargs):
|
|
test_fallback_names = [path for path in self.baseline_search_path() if not path.startswith(self._webkit_baseline_path('mac'))]
|
|
return list(map(self._webkit_baseline_path, test_fallback_names))
|
|
|
|
def _ntsd_location(self):
|
|
if 'PROGRAMFILES' not in os.environ:
|
|
return None
|
|
possible_paths = [self._filesystem.join(os.environ['PROGRAMFILES'], "Windows Kits", "10", "Debuggers", "x64", "ntsd.exe"),
|
|
self._filesystem.join(os.environ['PROGRAMFILES'], "Windows Kits", "8.1", "Debuggers", "x64", "ntsd.exe"),
|
|
self._filesystem.join(os.environ['PROGRAMFILES'], "Windows Kits", "8.0", "Debuggers", "x64", "ntsd.exe")]
|
|
if self.architecture() == 'x86_64':
|
|
possible_paths.append(self._filesystem.join("{0} (x86)".format(os.environ['PROGRAMFILES']), "Windows Kits", "10", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join("{0} (x86)".format(os.environ['PROGRAMFILES']), "Windows Kits", "8.1", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join("{0} (x86)".format(os.environ['PROGRAMFILES']), "Windows Kits", "8.0", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join("{0} (x86)".format(os.environ['PROGRAMFILES']), "Debugging Tools for Windows (x64)", "ntsd.exe"))
|
|
else:
|
|
possible_paths.append(self._filesystem.join(os.environ['PROGRAMFILES'], "Debugging Tools for Windows (x86)", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join(os.environ['SYSTEMROOT'], "system32", "ntsd.exe"))
|
|
if 'ProgramW6432' in os.environ:
|
|
possible_paths.append(self._filesystem.join(os.environ['ProgramW6432'], "Windows Kits", "10", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join(os.environ['ProgramW6432'], "Windows Kits", "8.1", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join(os.environ['ProgramW6432'], "Windows Kits", "8.0", "Debuggers", "x64", "ntsd.exe"))
|
|
possible_paths.append(self._filesystem.join(os.environ['ProgramW6432'], "Debugging Tools for Windows (x64)", "ntsd.exe"))
|
|
for path in possible_paths:
|
|
expanded_path = self._filesystem.expanduser(path)
|
|
_log.debug("Considering '%s'" % expanded_path)
|
|
if self._filesystem.exists(expanded_path):
|
|
_log.debug("Using ntsd located in '%s'" % path)
|
|
return expanded_path
|
|
return None
|
|
|
|
def create_debugger_command_file(self):
|
|
debugger_temp_directory = str(self._filesystem.mkdtemp())
|
|
command_file = self._filesystem.join(debugger_temp_directory, "debugger-commands.txt")
|
|
commands = ''.join(['.logopen /t "%s\\%s.txt"\n' % (cygpath(self.results_directory()), self.CRASH_LOG_PREFIX),
|
|
'.srcpath "%s"\n' % cygpath(self._webkit_finder.webkit_base()),
|
|
'!analyze -vv\n',
|
|
'~*kpn\n',
|
|
'q\n'])
|
|
self._filesystem.write_text_file(command_file, commands)
|
|
return command_file
|
|
|
|
def read_registry_value(self, reg_path, arch, root, key):
|
|
if sys.platform.startswith('win'):
|
|
_log.debug("Trying to read %s\\%s" % (reg_path, key))
|
|
try:
|
|
registry_key = _winreg.OpenKey(root, reg_path)
|
|
value = _winreg.QueryValueEx(registry_key, key)
|
|
_winreg.CloseKey(registry_key)
|
|
except WindowsError as ex:
|
|
_log.debug("Unable to read %s\\%s: %s" % (reg_path, key, str(ex)))
|
|
return ['', self._REG_SZ]
|
|
else:
|
|
registry_key = reg_path % (root, key)
|
|
_log.debug("Reading %s" % (registry_key))
|
|
read_registry_command = ["regtool", arch, "get", registry_key]
|
|
int_value = self._executive.run_command(read_registry_command, ignore_errors=True)
|
|
# regtool doesn't return the type of the entry, so need this ugly hack:
|
|
if reg_path in (self.WINDOWS_ERROR_REPORTING_KEY, self.WOW64_WINDOWS_ERROR_REPORTING_KEY):
|
|
_log.debug("I got {0}".format(int_value))
|
|
try:
|
|
value = [int(int_value), self._REG_DWORD]
|
|
except:
|
|
value = [0, self._REG_DWORD]
|
|
else:
|
|
value = [int_value.rstrip(), self._REG_SZ]
|
|
|
|
_log.debug("I got back ({0}) of type ({1})".format(value[0], value[1]))
|
|
return value
|
|
|
|
def write_registry_value(self, reg_path, arch, root, key, regType, value):
|
|
if sys.platform.startswith('win'):
|
|
_log.debug("Trying to write %s\\%s = %s" % (reg_path, key, value))
|
|
try:
|
|
registry_key = _winreg.OpenKey(root, reg_path, 0, _winreg.KEY_WRITE)
|
|
except WindowsError:
|
|
try:
|
|
_log.debug("Key doesn't exist -- must create it.")
|
|
registry_key = _winreg.CreateKeyEx(root, reg_path, 0, _winreg.KEY_WRITE)
|
|
except WindowsError as ex:
|
|
_log.error(r"Error setting (%s) %s\key: %s to value: %s. Error=%s." % (arch, root, key, value, str(ex)))
|
|
_log.error("You many need to adjust permissions on the %s\\%s key." % (reg_path, key))
|
|
return False
|
|
|
|
_log.debug("Writing {0} of type {1} to {2}\\{3}".format(value, regType, registry_key, key))
|
|
_winreg.SetValueEx(registry_key, key, 0, regType, value)
|
|
_winreg.CloseKey(registry_key)
|
|
else:
|
|
registry_key = reg_path % (root, key)
|
|
_log.debug("Writing to %s" % registry_key)
|
|
|
|
set_reg_value_command = ["regtool", arch, "set", regType, str(registry_key), str(value)]
|
|
rc = self._executive.run_command(set_reg_value_command, return_exit_code=True)
|
|
if rc == 2:
|
|
add_reg_value_command = ["regtool", arch, "add", regType, str(registry_key)]
|
|
rc = self._executive.run_command(add_reg_value_command, return_exit_code=True)
|
|
if rc == 0:
|
|
rc = self._executive.run_command(set_reg_value_command, return_exit_code=True)
|
|
if rc:
|
|
_log.warn(r"Error setting (%s) %s\key: %s to value: %s. Error=%s." % (arch, root, key, value, str(rc)))
|
|
_log.warn("You many need to adjust permissions on the %s key." % registry_key)
|
|
return False
|
|
|
|
# On Windows Vista/7 with UAC enabled, regtool will fail to modify the registry, but will still
|
|
# return a successful exit code. So we double-check here that the value we tried to write to the
|
|
# registry was really written.
|
|
check_value = self.read_registry_value(reg_path, arch, root, key)
|
|
if check_value[0] != value or check_value[1] != regType:
|
|
_log.warn("Reg update reported success, but value of key %s did not change." % key)
|
|
_log.warn("Wanted to set it to ({0}, {1}), but got {2})".format(value, regType, check_value))
|
|
_log.warn("You many need to adjust permissions on the %s\\%s key." % (reg_path, key))
|
|
return False
|
|
|
|
return True
|
|
|
|
def setup_crash_log_saving(self):
|
|
if '_NT_SYMBOL_PATH' not in os.environ:
|
|
_log.warning("The _NT_SYMBOL_PATH environment variable is not set. Using Microsoft Symbol Server.")
|
|
os.environ['_NT_SYMBOL_PATH'] = 'SRV*http://msdl.microsoft.com/download/symbols'
|
|
|
|
# Add build path to symbol path
|
|
os.environ['_NT_SYMBOL_PATH'] += ";" + str(self._build_path())
|
|
|
|
ntsd_path = self._ntsd_location()
|
|
if not ntsd_path:
|
|
_log.warning("Can't find ntsd.exe. Crash logs will not be saved.")
|
|
return None
|
|
# If we used -c (instead of -cf) we could pass the commands directly on the command line. But
|
|
# when the commands include multiple quoted paths (e.g., for .logopen and .srcpath), Windows
|
|
# fails to invoke the post-mortem debugger at all (perhaps due to a bug in Windows's command
|
|
# line parsing). So we save the commands to a file instead and tell the debugger to execute them
|
|
# using -cf.
|
|
command_file = self.create_debugger_command_file()
|
|
if not command_file:
|
|
return None
|
|
debugger_options = '"{0}" -p %ld -e %ld -g -noio -lines -cf "{1}"'.format(cygpath(ntsd_path), cygpath(command_file))
|
|
registry_settings = {'Debugger': [debugger_options, self._REG_SZ], 'Auto': ["1", self._REG_SZ]}
|
|
for key, value in registry_settings.items():
|
|
for arch in ["--wow32", "--wow64"]:
|
|
self.previous_debugger_values[(arch, self._HKLM, key)] = self.read_registry_value(self.POST_MORTEM_DEBUGGER_KEY, arch, self._HKLM, key)
|
|
self.previous_wow64_debugger_values[(arch, self._HKLM, key)] = self.read_registry_value(self.WOW64_POST_MORTEM_DEBUGGER_KEY, arch, self._HKLM, key)
|
|
self.write_registry_value(self.POST_MORTEM_DEBUGGER_KEY, arch, self._HKLM, key, value[1], value[0])
|
|
self.write_registry_value(self.WOW64_POST_MORTEM_DEBUGGER_KEY, arch, self._HKLM, key, value[1], value[0])
|
|
|
|
def restore_crash_log_saving(self):
|
|
for key, value in self.previous_debugger_values.items():
|
|
self.write_registry_value(self.POST_MORTEM_DEBUGGER_KEY, key[0], key[1], key[2], value[1], value[0])
|
|
for key, value in self.previous_wow64_debugger_values.items():
|
|
self.write_registry_value(self.WOW64_POST_MORTEM_DEBUGGER_KEY, key[0], key[1], key[2], value[1], value[0])
|
|
|
|
def prevent_error_dialogs(self):
|
|
registry_settings = {'DontShowUI': [1, self._REG_DWORD], 'Disabled': [1, self._REG_DWORD]}
|
|
for key, value in registry_settings.items():
|
|
for root in [self._HKLM, self._HKCU]:
|
|
for arch in ["--wow32", "--wow64"]:
|
|
self.previous_error_reporting_values[(arch, root, key)] = self.read_registry_value(self.WINDOWS_ERROR_REPORTING_KEY, arch, root, key)
|
|
self.previous_wow64_error_reporting_values[(arch, root, key)] = self.read_registry_value(self.WOW64_WINDOWS_ERROR_REPORTING_KEY, arch, root, key)
|
|
self.write_registry_value(self.WINDOWS_ERROR_REPORTING_KEY, arch, root, key, value[1], value[0])
|
|
self.write_registry_value(self.WOW64_WINDOWS_ERROR_REPORTING_KEY, arch, root, key, value[1], value[0])
|
|
|
|
def allow_error_dialogs(self):
|
|
for key, value in self.previous_error_reporting_values.items():
|
|
self.write_registry_value(self.WINDOWS_ERROR_REPORTING_KEY, key[0], key[1], key[2], value[1], value[0])
|
|
for key, value in self.previous_wow64_error_reporting_values.items():
|
|
self.write_registry_value(self.WOW64_WINDOWS_ERROR_REPORTING_KEY, key[0], key[1], key[2], value[1], value[0])
|
|
|
|
def delete_sem_locks(self):
|
|
if self.is_cygwin():
|
|
os.system("rm -rf /dev/shm/sem.*")
|
|
|
|
def delete_preference_files(self):
|
|
try:
|
|
preferences_files = self._filesystem.join(os.environ['APPDATA'], "Apple Computer/Preferences", "com.apple.DumpRenderTree*")
|
|
filelist = glob.glob(preferences_files)
|
|
for file in filelist:
|
|
self._filesystem.remove(file)
|
|
except:
|
|
_log.warn("Failed to delete preference files.")
|
|
|
|
def setup_test_run(self, device_type=None):
|
|
atexit.register(self.restore_crash_log_saving)
|
|
self.setup_crash_log_saving()
|
|
self.prevent_error_dialogs()
|
|
self.delete_sem_locks()
|
|
self.delete_preference_files()
|
|
super(WinPort, self).setup_test_run(device_type)
|
|
|
|
def clean_up_test_run(self):
|
|
self.allow_error_dialogs()
|
|
self.restore_crash_log_saving()
|
|
super(WinPort, self).clean_up_test_run()
|
|
|
|
def path_to_crash_logs(self):
|
|
return self.results_directory()
|
|
|
|
def _get_crash_log(self, name, pid, stdout, stderr, newer_than, time_fn=None, sleep_fn=None, wait_for_log=True, target_host=None):
|
|
# Note that we do slow-spin here and wait, since it appears the time
|
|
# ReportCrash takes to actually write and flush the file varies when there are
|
|
# lots of simultaneous crashes going on.
|
|
# FIXME: Should most of this be moved into CrashLogs()?
|
|
time_fn = time_fn or time.time
|
|
sleep_fn = sleep_fn or time.sleep
|
|
crash_log = ''
|
|
crash_logs = CrashLogs(target_host or self.host, self.path_to_crash_logs(), crash_logs_to_skip=self._crash_logs_to_skip_for_host.get(target_host or self.host, []))
|
|
now = time_fn()
|
|
# FIXME: delete this after we're sure this code is working ...
|
|
_log.debug('looking for crash log for %s:%s' % (name, str(pid)))
|
|
deadline = now + 5 * int(self.get_option('child_processes', 1))
|
|
while not crash_log and now <= deadline:
|
|
crash_log = crash_logs.find_newest_log(name, pid, include_errors=True, newer_than=newer_than)
|
|
if not wait_for_log:
|
|
break
|
|
if not crash_log or not [line for line in crash_log.splitlines() if line.startswith('quit:')]:
|
|
sleep_fn(0.1)
|
|
now = time_fn()
|
|
|
|
if not crash_log:
|
|
return (stderr, None)
|
|
return (stderr, crash_log)
|
|
|
|
def look_for_new_crash_logs(self, crashed_processes, start_time):
|
|
"""Since crash logs can take a long time to be written out if the system is
|
|
under stress do a second pass at the end of the test run.
|
|
|
|
crashes: test_name -> pid, process_name tuple of crashed process
|
|
start_time: time the tests started at. We're looking for crash
|
|
logs after that time.
|
|
"""
|
|
crash_logs = {}
|
|
for (test_name, process_name, pid) in crashed_processes:
|
|
# Passing None for output. This is a second pass after the test finished so
|
|
# if the output had any logging we would have already collected it.
|
|
crash_log = self._get_crash_log(process_name, pid, None, None, start_time, wait_for_log=False)[1]
|
|
if crash_log:
|
|
crash_logs[test_name] = crash_log
|
|
return crash_logs
|
|
|
|
def check_httpd(self):
|
|
if not super(WinPort, self).check_httpd():
|
|
return False
|
|
|
|
path = self._path_to_apache()
|
|
if not path:
|
|
return False
|
|
|
|
# To launch Apache as a daemon, service installation is required.
|
|
exit_code = self._executive.run_command([path, '-k', 'install', '-T'], return_exit_code=True)
|
|
# 0=success, 2=already installed, 720005=permission error, etc.
|
|
if exit_code not in (0, 2):
|
|
_log.error('Could not install httpd as a service. Perhaps you forgot to run as adminstrator? (exit code={})'.format(exit_code))
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class WinCairoPort(WinPort):
|
|
port_name = "wincairo"
|
|
|
|
DEFAULT_ARCHITECTURE = 'x86_64'
|
|
|
|
def default_baseline_search_path(self, **kwargs):
|
|
return list(map(self._webkit_baseline_path, self._search_paths()))
|
|
|
|
def _port_specific_expectations_files(self, **kwargs):
|
|
return list(map(lambda x: self._filesystem.join(self._webkit_baseline_path(x), 'TestExpectations'), reversed(self._search_paths())))
|
|
|
|
def _search_paths(self):
|
|
paths = []
|
|
version_name_map = VersionNameMap.map(self.host.platform)
|
|
if self._os_version < self.VERSION_MIN or self._os_version > self.VERSION_MAX:
|
|
versions = [self._os_version] if self._os_version else []
|
|
else:
|
|
sorted_versions = sorted(version_name_map.mapping_for_platform(platform=self.port_name).values())
|
|
versions = sorted_versions[sorted_versions.index(self._os_version):]
|
|
|
|
normalize = lambda version: version.lower().replace(' ', '')
|
|
to_name = lambda version: version_name_map.to_name(version, platform=self.port_name)
|
|
|
|
wk_version = 'wk2' if self.get_option('webkit_test_runner') else 'wk1'
|
|
|
|
for version in versions:
|
|
name = self.port_name + '-' + normalize(to_name(version))
|
|
paths.append(name + '-' + wk_version)
|
|
paths.append(name)
|
|
|
|
paths.append(self.port_name + '-' + wk_version)
|
|
paths.append(self.port_name)
|
|
if self.get_option('webkit_test_runner'):
|
|
paths.append('wk2')
|
|
paths.extend(self.get_option("additional_platform_directory", []))
|
|
|
|
return paths
|
|
|
|
def configuration_for_upload(self, host=None):
|
|
configuration = super(WinCairoPort, self).configuration_for_upload(host=host)
|
|
configuration['platform'] = self.port_name
|
|
return configuration
|
|
|
|
|
|
class FTWPort(WinPort):
|
|
port_name = "ftw"
|
|
|
|
DEFAULT_ARCHITECTURE = 'x86_64'
|
|
|
|
def default_baseline_search_path(self, **kwargs):
|
|
return list(map(self._webkit_baseline_path, self._search_paths()))
|
|
|
|
def _port_specific_expectations_files(self, **kwargs):
|
|
return list(map(lambda x: self._filesystem.join(self._webkit_baseline_path(x), 'TestExpectations'), reversed(self._search_paths())))
|
|
|
|
def _search_paths(self):
|
|
paths = []
|
|
version_name_map = VersionNameMap.map(self.host.platform)
|
|
if self._os_version < self.VERSION_MIN or self._os_version > self.VERSION_MAX:
|
|
versions = [self._os_version]
|
|
else:
|
|
sorted_versions = sorted(version_name_map.mapping_for_platform(platform=self.port_name).values())
|
|
versions = sorted_versions[sorted_versions.index(self._os_version):]
|
|
|
|
normalize = lambda version: version.lower().replace(' ', '')
|
|
to_name = lambda version: version_name_map.to_name(version, platform=self.port_name)
|
|
|
|
wk_version = 'wk2' if self.get_option('webkit_test_runner') else 'wk1'
|
|
|
|
for version in versions:
|
|
name = self.port_name + '-' + normalize(to_name(version))
|
|
paths.append(name + '-' + wk_version)
|
|
paths.append(name)
|
|
|
|
paths.append(self.port_name + '-' + wk_version)
|
|
paths.append(self.port_name)
|
|
if self.get_option('webkit_test_runner'):
|
|
paths.append('wk2')
|
|
paths.extend(self.get_option("additional_platform_directory", []))
|
|
|
|
return paths
|