blob: 57b066c584654540b9be26f50945f59020bcbc57 [file] [log] [blame]
# Copyright (C) 2010 Google Inc. All rights reserved.
# Copyright (C) 2013 Samsung Electronics. All rights reserved.
# Copyright (C) 2017 Igalia S.L. 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 os
import uuid
import logging
import shlex
import webkitpy
from webkitpy.common.system import path
from webkitpy.common.memoized import memoized
from webkitpy.layout_tests.models.test_configuration import TestConfiguration
from webkitpy.port.base import Port
from webkitpy.port.xvfbdriver import XvfbDriver
from webkitpy.port.westondriver import WestonDriver
from webkitpy.port.xorgdriver import XorgDriver
from webkitpy.port.waylanddriver import WaylandDriver
from webkitpy.port.linux_get_crash_log import GDBCrashLogGenerator
from webkitpy.port.leakdetector_valgrind import LeakDetectorValgrind
from webkitcorepy import decorators
_log = logging.getLogger(__name__)
class GtkPort(Port):
port_name = "gtk"
def __init__(self, *args, **kwargs):
super(GtkPort, self).__init__(*args, **kwargs)
self._display_server = self.get_option("display_server")
if self.get_option("leaks"):
self._leakdetector = LeakDetectorValgrind(self._executive, self._filesystem, self.results_directory())
if not self.get_option("wrapper"):
raise ValueError('use --wrapper=\"valgrind\" for memory leak detection on GTK')
if self._should_use_jhbuild():
self._jhbuild_wrapper = [self.path_from_webkit_base('Tools', 'jhbuild', 'jhbuild-wrapper'), '--gtk', 'run']
if self.get_option('wrapper'):
self.set_option('wrapper', ' '.join(self._jhbuild_wrapper) + ' ' + self.get_option('wrapper'))
else:
self.set_option_default('wrapper', ' '.join(self._jhbuild_wrapper))
def _built_executables_path(self, *path):
return self._build_path(*(('bin',) + path))
def _built_libraries_path(self, *path):
return self._build_path(*(('lib',) + path))
def _port_flag_for_scripts(self):
return "--gtk"
@memoized
def _driver_class(self):
if self._display_server == "weston":
return WestonDriver
if self._display_server == "wayland":
return WaylandDriver
if self._display_server == "xorg":
return XorgDriver
return XvfbDriver
def default_timeout_ms(self):
default_timeout = 15000
# Starting an application under Valgrind takes a lot longer than normal
# so increase the timeout (empirically 10x is enough to avoid timeouts).
multiplier = 10 if self.get_option("leaks") else 1
# Debug builds are slower (no compiler optimizations are used).
if self.get_option('configuration') == 'Debug':
multiplier *= 2
return multiplier * default_timeout
def driver_stop_timeout(self):
if self.get_option("leaks"):
# Wait the default timeout time before killing the process in driver.stop().
return self.default_timeout_ms()
return super(GtkPort, self).driver_stop_timeout()
def setup_test_run(self, device_type=None):
super(GtkPort, self).setup_test_run(device_type)
if self.get_option("leaks"):
self._leakdetector.clean_leaks_files_from_results_directory()
def setup_environ_for_server(self, server_name=None):
environment = super(GtkPort, self).setup_environ_for_server(server_name)
environment['G_DEBUG'] = 'fatal-criticals'
environment['GSETTINGS_BACKEND'] = 'memory'
environment['LIBOVERLAY_SCROLLBAR'] = '0'
environment['TEST_RUNNER_INJECTED_BUNDLE_FILENAME'] = self._build_path('lib', 'libTestRunnerInjectedBundle.so')
environment['TEST_RUNNER_TEST_PLUGIN_PATH'] = self._build_path('lib', 'plugins')
self._copy_value_from_environ_if_set(environment, 'WEBKIT_OUTPUTDIR')
self._copy_value_from_environ_if_set(environment, 'WEBKIT_JHBUILD')
self._copy_value_from_environ_if_set(environment, 'WEBKIT_TOP_LEVEL')
self._copy_value_from_environ_if_set(environment, 'WEBKIT_DEBUG')
self._copy_value_from_environ_if_set(environment, 'WEBKIT_GST_USE_PLAYBIN3')
for gst_variable in ('DEBUG', 'DEBUG_DUMP_DOT_DIR', 'DEBUG_FILE', 'DEBUG_NO_COLOR',
'PLUGIN_SCANNER', 'PLUGIN_PATH', 'PLUGIN_SYSTEM_PATH', 'REGISTRY',
'PLUGIN_PATH_1_0'):
self._copy_value_from_environ_if_set(environment, 'GST_%s' % gst_variable)
gst_feature_rank_override = environment.get('GST_PLUGIN_FEATURE_RANK')
environment['GST_PLUGIN_FEATURE_RANK'] = 'fakeaudiosink:max'
if gst_feature_rank_override:
environment['GST_PLUGIN_FEATURE_RANK'] += ',%s' % gst_feature_rank_override
# Configure the software libgl renderer if jhbuild ready and we test inside a virtualized window system
if self._driver_class() in [XvfbDriver, WestonDriver] and (self._should_use_jhbuild() or self._is_flatpak()):
if self._should_use_jhbuild():
llvmpipe_libgl_path = self.host.executive.run_command(self._jhbuild_wrapper + ['printenv', 'LLVMPIPE_LIBGL_PATH'],
ignore_errors=True).strip()
dri_libgl_path = os.path.join(llvmpipe_libgl_path, "dri")
else: # in flatpak
llvmpipe_libgl_path = "/usr/lib/x86_64-linux-gnu/"
dri_libgl_path = os.path.join(llvmpipe_libgl_path, "GL", "lib", "dri")
if os.path.exists(os.path.join(llvmpipe_libgl_path, "libGL.so")) and os.path.exists(os.path.join(dri_libgl_path, "swrast_dri.so")):
# Make sure va-api support gets disabled because it's incompatible with Mesa's softGL driver.
environment['LIBVA_DRIVER_NAME'] = "null"
# Force the Gallium llvmpipe software rasterizer
environment['LIBGL_ALWAYS_SOFTWARE'] = "1"
environment['LIBGL_DRIVERS_PATH'] = dri_libgl_path
else:
_log.warning("Can't find Gallium llvmpipe driver. Try to run update-webkitgtk-libs or update-webkit-flatpak")
if self.get_option("leaks"):
# Turn off GLib memory optimisations https://wiki.gnome.org/Valgrind.
environment['G_SLICE'] = 'always-malloc'
environment['G_DEBUG'] += ',gc-friendly'
# Turn off bmalloc when running under Valgrind, see https://bugs.webkit.org/show_bug.cgi?id=177745
environment['Malloc'] = '1'
xmlfilename = "".join(("drt-%p-", uuid.uuid1().hex, "-leaks.xml"))
xmlfile = os.path.join(self.results_directory(), xmlfilename)
suppressionsfile = self.path_from_webkit_base('Tools', 'Scripts', 'valgrind', 'suppressions.txt')
environment['VALGRIND_OPTS'] = \
"--tool=memcheck " \
"--num-callers=40 " \
"--demangle=no " \
"--trace-children=no " \
"--smc-check=all-non-file " \
"--leak-check=yes " \
"--leak-resolution=high " \
"--show-possibly-lost=no " \
"--show-reachable=no " \
"--leak-check=full " \
"--undef-value-errors=no " \
"--gen-suppressions=all " \
"--xml=yes " \
"--xml-file=%s " \
"--suppressions=%s" % (xmlfile, suppressionsfile)
return environment
def _generate_all_test_configurations(self):
configurations = []
for build_type in self.ALL_BUILD_TYPES:
configurations.append(TestConfiguration(version=self.version_name(), architecture='x86', build_type=build_type))
return configurations
def _path_to_driver(self):
return self._built_executables_path(self.driver_name())
@decorators.Memoize()
def _path_to_image_diff(self):
return self._built_executables_path('ImageDiff')
def _path_to_default_image_diff(self):
return self._path_to_image_diff()
def _path_to_webcore_library(self):
gtk_library_names = [
"libwebkitgtk-1.0.so",
"libwebkitgtk-3.0.so",
"libwebkit2gtk-1.0.so",
]
for library in gtk_library_names:
full_library = self._built_libraries_path(library)
if self._filesystem.isfile(full_library):
return full_library
return None
def _search_paths(self):
search_paths = []
if self._is_gtk4_build():
search_paths.append(self.port_name + "4")
if self._driver_class() in [WaylandDriver, WestonDriver]:
search_paths.append(self.port_name + "-wayland")
search_paths.append(self.port_name)
search_paths.append('glib')
search_paths.append('wk2')
search_paths.extend(self.get_option("additional_platform_directory", []))
return search_paths
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 [self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in reversed(self._search_paths())]
def print_leaks_summary(self):
if not self.get_option('leaks'):
return
# FIXME: This is a hack, but we don't have a better way to get this information from the workers yet
# because we're in the manager process.
leaks_files = self._leakdetector.leaks_files_in_results_directory()
if not leaks_files:
return
self._leakdetector.parse_and_print_leaks_detail(leaks_files)
def show_results_html_file(self, results_filename):
self.run_minibrowser([path.abspath_to_uri(self.host.platform, results_filename)])
def check_sys_deps(self):
return super(GtkPort, self).check_sys_deps() and self._driver_class().check_driver(self)
def _get_crash_log(self, name, pid, stdout, stderr, newer_than, target_host=None):
return GDBCrashLogGenerator(self._executive, name, pid, newer_than,
self._filesystem, self._path_to_driver, self.port_name, self.get_option('configuration')).generate_crash_log(stdout, stderr)
def test_expectations_file_position(self):
# GTK port baseline search path is gtk -> glib -> wk2 -> generic (as gtk-wk2 and gtk baselines are merged), so port test expectations file is at third to last position.
return 3
def build_webkit_command(self, build_style=None):
command = super(GtkPort, self).build_webkit_command(build_style)
command.extend(["--gtk", "--update-gtk"])
command.append(super(GtkPort, self).make_args())
return command
def run_webkit_tests_command(self):
command = super(GtkPort, self).run_webkit_tests_command()
command.append("--gtk")
return command
def configuration_for_upload(self, host=None):
configuration = super(GtkPort, self).configuration_for_upload(host=host)
configuration['platform'] = 'GTK'
configuration['version_name'] = self._display_server.capitalize() if self._display_server else 'Xvfb'
return configuration
def run_minibrowser(self, args):
miniBrowser = self._build_path('bin', 'MiniBrowser')
if not self._filesystem.isfile(miniBrowser):
print("%s not found... Did you run build-webkit?" % miniBrowser)
return 1
command = [miniBrowser]
if os.environ.get("WEBKIT_MINI_BROWSER_PREFIX"):
command = shlex.split(os.environ["WEBKIT_MINI_BROWSER_PREFIX"]) + command
if self._should_use_jhbuild():
command = self._jhbuild_wrapper + command
return self._executive.run_command(command + args, cwd=self.webkit_base(), stdout=None, return_stderr=False, decode_output=False)
@memoized
def _is_gtk4_build(self):
try:
libdir = self._build_path('lib')
candidates = self._filesystem.glob(os.path.join(libdir, 'libwebkit2gtk-*.so'))
if not candidates:
return False
if len(candidates) > 1:
_log.warning("Multiple WebKit2GTK libraries found. Skipping GTK4 detection.")
return False
return os.path.basename(candidates[0]) == 'libwebkit2gtk-5.0.so'
except (webkitpy.common.system.executive.ScriptError, IOError, OSError):
return False
return False