| # Copyright (C) 2011 Google Inc. All rights reserved. |
| # Copyright (C) 2012-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 logging |
| import os |
| import re |
| |
| from webkitpy.common.memoized import memoized |
| from webkitpy.common.system.executive import ScriptError |
| from webkitpy.common.version import Version |
| from webkitpy.common.version_name_map import PUBLIC_TABLE, INTERNAL_TABLE |
| from webkitpy.common.version_name_map import VersionNameMap |
| from webkitpy.port.config import apple_additions, Config |
| from webkitpy.port.darwin import DarwinPort |
| |
| _log = logging.getLogger(__name__) |
| |
| |
| class MacPort(DarwinPort): |
| port_name = "mac" |
| |
| CURRENT_VERSION = Version(10, 15) |
| |
| SDK = 'macosx' |
| |
| ARCHITECTURES = ['x86_64', 'x86'] |
| |
| DEFAULT_ARCHITECTURE = 'x86_64' |
| |
| def __init__(self, host, port_name, **kwargs): |
| DarwinPort.__init__(self, host, port_name, **kwargs) |
| version_name_map = VersionNameMap.map(host.platform) |
| self._os_version = None |
| split_port_name = port_name.split('-') |
| if len(split_port_name) > 1 and split_port_name[1] != 'wk2': |
| self._os_version = version_name_map.from_name(split_port_name[1])[1] |
| elif self.host.platform.is_mac() and apple_additions(): |
| self._os_version = self.host.platform.os_version |
| if not self._os_version: |
| self._os_version = MacPort.CURRENT_VERSION |
| assert self._os_version.major == 10 |
| |
| def _build_driver_flags(self): |
| return ['ARCHS=i386'] if self.architecture() == 'x86' else [] |
| |
| def default_baseline_search_path(self, **kwargs): |
| versions_to_fallback = [] |
| version_name_map = VersionNameMap.map(self.host.platform) |
| |
| if self._os_version == self.CURRENT_VERSION: |
| versions_to_fallback = [self.CURRENT_VERSION] |
| else: |
| temp_version = Version(self._os_version.major, self._os_version.minor) |
| while temp_version != self.CURRENT_VERSION: |
| versions_to_fallback.append(Version.from_iterable(temp_version)) |
| if temp_version < self.CURRENT_VERSION: |
| temp_version.minor += 1 |
| else: |
| temp_version.minor -= 1 |
| wk_string = 'wk1' |
| if self.get_option('webkit_test_runner'): |
| wk_string = 'wk2' |
| |
| expectations = [] |
| for version in versions_to_fallback: |
| version_name = version_name_map.to_name(version, platform=self.port_name) |
| if version_name: |
| standardized_version_name = version_name.lower().replace(' ', '') |
| apple_name = None |
| if apple_additions(): |
| apple_name = version_name_map.to_name(version, platform=self.port_name, table=INTERNAL_TABLE) |
| |
| if apple_name: |
| expectations.append(self._apple_baseline_path('mac-{}-{}'.format(apple_name.lower().replace(' ', ''), wk_string))) |
| if version_name: |
| expectations.append(self._webkit_baseline_path('mac-{}-{}'.format(standardized_version_name, wk_string))) |
| if apple_name: |
| expectations.append(self._apple_baseline_path('mac-{}'.format(apple_name.lower().replace(' ', '')))) |
| if version_name: |
| expectations.append(self._webkit_baseline_path('mac-{}'.format(standardized_version_name))) |
| |
| if apple_additions(): |
| expectations.append(self._apple_baseline_path('{}-{}'.format(self.port_name, wk_string))) |
| expectations.append(self._webkit_baseline_path('{}-{}'.format(self.port_name, wk_string))) |
| if apple_additions(): |
| expectations.append(self._apple_baseline_path('{}'.format(self.port_name))) |
| expectations.append(self._webkit_baseline_path(self.port_name)) |
| |
| if self.get_option('webkit_test_runner'): |
| expectations.append(self._webkit_baseline_path('wk2')) |
| return expectations |
| |
| @memoized |
| def configuration_specifier_macros(self): |
| config_map = {} |
| version_name_map = VersionNameMap.map(self.host.platform) |
| for version in self._allowed_versions(): |
| version_names = [] |
| for newer in self._allowed_versions()[self._allowed_versions().index(version):]: |
| version_name = version_name_map.to_name(newer, platform=self.port_name) |
| if not version_name: |
| version_name = version_name_map.to_name(newer, platform=self.port_name, table=INTERNAL_TABLE) |
| version_names.append(version_name.lower().replace(' ', '')) |
| for table in [PUBLIC_TABLE, INTERNAL_TABLE]: |
| version_name = version_name_map.to_name(version, platform=self.port_name, table=table) |
| if not version_name: |
| continue |
| config_map[version_name.lower().replace(' ', '') + '+'] = version_names |
| return config_map |
| |
| def environment_for_api_tests(self): |
| result = super(MacPort, self).environment_for_api_tests() |
| if self.get_option('guard_malloc'): |
| result['DYLD_INSERT_LIBRARIES'] = '/usr/lib/libgmalloc.dylib' |
| result['__XPC_DYLD_INSERT_LIBRARIES'] = '/usr/lib/libgmalloc.dylib' |
| return result |
| |
| def setup_environ_for_server(self, server_name=None): |
| env = super(MacPort, self).setup_environ_for_server(server_name) |
| if server_name == self.driver_name(): |
| if self.get_option('leaks'): |
| env['MallocStackLogging'] = '1' |
| env['__XPC_MallocStackLogging'] = '1' |
| env['MallocScribble'] = '1' |
| env['__XPC_MallocScribble'] = '1' |
| if self.get_option('guard_malloc'): |
| self._append_value_colon_separated(env, 'DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib') |
| self._append_value_colon_separated(env, '__XPC_DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib') |
| self._append_value_colon_separated(env, 'DYLD_INSERT_LIBRARIES', self._build_path("libWebCoreTestShim.dylib")) |
| env['XML_CATALOG_FILES'] = '' # work around missing /etc/catalog <rdar://problem/4292995> |
| return env |
| |
| def _clear_global_caches_and_temporary_files(self): |
| self._filesystem.rmtree(os.path.expanduser('~/Library/' + self.driver_name())) |
| self._filesystem.rmtree(os.path.expanduser('~/Library/Application Support/' + self.driver_name())) |
| self._filesystem.rmtree(os.path.expanduser('~/Library/Caches/' + self.driver_name())) |
| self._filesystem.rmtree(os.path.expanduser('~/Library/WebKit/' + self.driver_name())) |
| |
| def _path_to_user_cache_directory(self, suffix=None): |
| DIRHELPER_USER_DIR_SUFFIX = 'DIRHELPER_USER_DIR_SUFFIX' |
| CS_DARWIN_USER_CACHE_DIR = 65538 |
| |
| # The environment variable DIRHELPER_USER_DIR_SUFFIX is only honored on systems with |
| # System Integrity Protection disabled or with an Apple-Internal OS. To make this code |
| # work for all system configurations we compute the path with respect to the suffix |
| # by hand and temporarily unset the environment variable DIRHELPER_USER_DIR_SUFFIX (if set) |
| # to avoid it influencing confstr() on systems that honor DIRHELPER_USER_DIR_SUFFIX. |
| saved_suffix = None |
| if DIRHELPER_USER_DIR_SUFFIX in os.environ: |
| saved_suffix = os.environ[DIRHELPER_USER_DIR_SUFFIX] |
| del os.environ[DIRHELPER_USER_DIR_SUFFIX] |
| result = os.path.join(os.confstr(CS_DARWIN_USER_CACHE_DIR), suffix or '') |
| if saved_suffix is not None: |
| os.environ[DIRHELPER_USER_DIR_SUFFIX] = saved_suffix |
| return result |
| |
| def operating_system(self): |
| return 'mac' |
| |
| def default_child_processes(self, **kwargs): |
| default_count = super(MacPort, self).default_child_processes() |
| |
| # FIXME: https://bugs.webkit.org/show_bug.cgi?id=95906 With too many WebProcess WK2 tests get stuck in resource contention. |
| # To alleviate the issue reduce the number of running processes |
| # Anecdotal evidence suggests that a 4 core/8 core logical machine may run into this, but that a 2 core/4 core logical machine does not. |
| should_throttle_for_wk2 = self.get_option('webkit_test_runner') and default_count > 4 |
| # We also want to throttle for leaks bots. |
| if should_throttle_for_wk2 or self.get_option('leaks'): |
| default_count = int(.75 * default_count) |
| |
| if should_throttle_for_wk2 and self.get_option('guard_malloc'): |
| # Some 12 core Macs get a lot of tests time out when running 18 WebKitTestRunner processes (it's not clear what this depends on). |
| # <rdar://problem/25750302> |
| default_count = min(default_count, 12) |
| |
| # Make sure we have enough ram to support that many instances: |
| total_memory = self.host.platform.total_bytes_memory() |
| if total_memory: |
| bytes_per_drt = 256 * 1024 * 1024 # Assume each DRT needs 256MB to run. |
| overhead = 2048 * 1024 * 1024 # Assume we need 2GB free for the O/S |
| supportable_instances = max((total_memory - overhead) / bytes_per_drt, 1) # Always use one process, even if we don't have space for it. |
| if supportable_instances < default_count: |
| _log.warning("This machine could support %s child processes, but only has enough memory for %s." % (default_count, supportable_instances)) |
| else: |
| _log.warning("Cannot determine available memory for child processes, using default child process count of %s." % default_count) |
| supportable_instances = default_count |
| return min(supportable_instances, default_count) |
| |
| def start_helper(self, pixel_tests=False, prefer_integrated_gpu=False): |
| helper_path = self._path_to_helper() |
| if not helper_path: |
| _log.error("No path to LayoutTestHelper binary") |
| return False |
| _log.debug("Starting layout helper %s" % helper_path) |
| arguments = [helper_path, '--install-color-profile'] |
| if prefer_integrated_gpu: |
| arguments.append('--prefer-integrated-gpu') |
| self._helper = self._executive.popen(arguments, |
| stdin=self._executive.PIPE, stdout=self._executive.PIPE, stderr=None) |
| is_ready = self._helper.stdout.readline() |
| if not is_ready.startswith(b'ready'): |
| _log.error("LayoutTestHelper could not start") |
| return False |
| return True |
| |
| def reset_preferences(self): |
| _log.debug("Resetting persistent preferences") |
| |
| for domain in ["DumpRenderTree", "WebKitTestRunner"]: |
| try: |
| self._executive.run_command(["defaults", "delete", domain]) |
| except ScriptError as e: |
| # 'defaults' returns 1 if the domain did not exist |
| if e.exit_code != 1: |
| raise e |
| |
| def stop_helper(self): |
| if self._helper: |
| _log.debug("Stopping LayoutTestHelper") |
| try: |
| self._helper.stdin.write(b"x\n") |
| self._helper.stdin.close() |
| self._helper.wait() |
| except IOError as e: |
| _log.debug("IOError raised while stopping helper: %s" % str(e)) |
| self._helper = None |
| |
| def logging_patterns_to_strip(self): |
| logging_patterns = [] |
| |
| # FIXME: Remove this after <rdar://problem/35954459> is fixed. |
| logging_patterns.append(('AVDCreateGPUAccelerator: Error loading GPU renderer\n', '')) |
| |
| # FIXME: Remove this after <rdar://problem/51191120> is fixed. |
| logging_patterns.append((re.compile('GVA warning: getFreeDRMInstanceCount, maxDRMInstanceCount: .*\n'), '')) |
| |
| # FIXME: Remove this after <rdar://problem/52897406> is fixed. |
| logging_patterns.append((re.compile('VPA info:.*\n'), '')) |
| |
| return logging_patterns |
| |
| def stderr_patterns_to_strip(self): |
| worthless_patterns = [] |
| worthless_patterns.append((re.compile('.*(Fig|fig|itemasync|vt|mv_|PullParamSetSPS|ccrp_|client).* signalled err=.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<<< FigFilePlayer >>>>.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<<< FigFile >>>>.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<<< FAQ >>>>.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<<< MediaValidator >>>>.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<<< VMC >>>>.*\n'), '')) |
| worthless_patterns.append((re.compile('.*<<< FFR_Common >>>.*\n'), '')) |
| return worthless_patterns |
| |
| def configuration_for_upload(self, host=None): |
| host = host or self.host |
| configuration = super(MacPort, self).configuration_for_upload(host=host) |
| |
| output = host.executive.run_command(['/usr/sbin/sysctl', 'hw.model']).rstrip() |
| match = re.match(r'hw.model: (?P<model>.*)', output) |
| if match: |
| configuration['model'] = match.group('model') |
| |
| return configuration |
| |
| |
| class MacCatalystPort(MacPort): |
| port_name = "maccatalyst" |
| |
| def __init__(self, *args, **kwargs): |
| super(MacCatalystPort, self).__init__(*args, **kwargs) |
| self._config = Config(self._executive, self._filesystem, MacCatalystPort.port_name) |
| |
| def _build_driver_flags(self): |
| return ['SDK_VARIANT=iosmac'] + super(MacCatalystPort, self)._build_driver_flags() |
| |
| def configuration_for_upload(self, host=None): |
| configuration = super(MacCatalystPort, self).configuration_for_upload(host=host) |
| configuration['platform'] = self.port_name |
| return configuration |