diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 2bed1745a5f3..c6bd65e9bf43 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -880,6 +880,22 @@ def join_ensure_dir(dir1, dir2): dest="enable_webrender", help="Enable the WebRender compositor in Gecko.", ) +@CommandArgumentGroup("filter sets") +@CommandArgument( + "--filter-set", + default=None, + dest="filter_set", + type=str, + group="filter sets", + help="Use a predefined gtest filter (this overrides the gtest_filter).", +) +@CommandArgument( + "--list-filter-sets", + action="store_true", + dest="list_filter_sets", + group="filter sets", + help="List available predefined gtest filters.", +) @CommandArgumentGroup("Android") @CommandArgument( "--package", @@ -946,6 +962,8 @@ def gtest( list_tests, tbpl_parser, enable_webrender, + filter_set, + list_filter_sets, package, adb_path, device_serial, @@ -983,6 +1001,10 @@ def gtest( if conditions.is_android(command_context): if jobs != 1: print("--jobs is not supported on Android and will be ignored") + if filter_set: + print("--filter-set is not supported on Android and will be ignored") + if list_filter_sets: + print("--list-filter-sets is not supported on Android and will be ignored") if debug or debugger or debugger_args: print("--debug options are not supported on Android and will be ignored") from mozrunner.devices.android_device import InstallIntent @@ -1055,6 +1077,23 @@ def gtest( else: gtest_env["MOZ_WEBRENDER"] = "0" + if filter_set or list_filter_sets: + filter_sets_mod_path = os.path.join("testing", "gtest", "gtest_filter_sets.py") + load_source("gtest_filter_sets", filter_sets_mod_path) + + import gtest_filter_sets + + if filter_set: + gtest_filter_for_filter_set = gtest_filter_sets.get(filter_set) + if gtest_filter_for_filter_set: + gtest_env["GTEST_FILTER"] = gtest_filter_for_filter_set + else: + print("Unknown filter set.") + return 1 + else: + gtest_filter_sets.list() + return 1 + if jobs == 1: return command_context.run_process( args=args, diff --git a/testing/config/mozbase_requirements.txt b/testing/config/mozbase_requirements.txt index 3e0852c8acdb..0bfccdde88c2 100644 --- a/testing/config/mozbase_requirements.txt +++ b/testing/config/mozbase_requirements.txt @@ -27,7 +27,7 @@ aiohttp==3.7.4.post0; sys_platform != 'darwin' https://pypi.pub.build.mozilla.org/pub/arsenic-21.8-py3-none-any.whl; sys_platform != 'darwin' requests==2.27.1; sys_platform != 'darwin' -pyyaml==5.1.2; sys_platform != 'darwin' +pyyaml==5.1.2 structlog==20.2.0; sys_platform != 'darwin' toml==0.10.2 tomlkit==0.12.3 diff --git a/testing/gtest/gtest_filter_sets.py b/testing/gtest/gtest_filter_sets.py new file mode 100644 index 000000000000..634cc3409fda --- /dev/null +++ b/testing/gtest/gtest_filter_sets.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os.path + +import yaml + +HERE = os.path.abspath(os.path.dirname(__file__)) +FILTER_SETS_FILE = os.path.join(HERE, "gtest_filter_sets.yml") + + +def get(filter_set_name): + """ + Retrieve the gtest filter associated with a specific filter set name. + """ + + with open(FILTER_SETS_FILE, "r") as file: + filter_set_data = yaml.safe_load(file) + + if filter_set_name in filter_set_data: + return filter_set_data[filter_set_name].get("gtest_filter", None) + + return None + + +def list(): + """ + Lists all available filter sets and their filters from the YAML file. + """ + with open(FILTER_SETS_FILE, "r") as file: + filter_set_data = yaml.safe_load(file) + + print("Filter sets from {}:".format(FILTER_SETS_FILE)) + for key, value in filter_set_data.items(): + gtest_filter = value.get("gtest_filter", "No gtest filter defined") + print(f"- {key}: {gtest_filter}") diff --git a/testing/gtest/gtest_filter_sets.yml b/testing/gtest/gtest_filter_sets.yml new file mode 100644 index 000000000000..832b66b80914 --- /dev/null +++ b/testing/gtest/gtest_filter_sets.yml @@ -0,0 +1,7 @@ +# Filter sets defined here will be available to all users. Run them with: +# $ mach gtest --filter-set FILTER_SET +# +# Please keep this in alphabetical order. + +dom-storage: + gtest_filter: "*Quota*:*IndexedDB*:*LocalStorage*:*FileSystem*" diff --git a/testing/gtest/rungtests.py b/testing/gtest/rungtests.py index de035eca3e18..bc45d9c2fe00 100644 --- a/testing/gtest/rungtests.py +++ b/testing/gtest/rungtests.py @@ -12,8 +12,11 @@ import mozcrash import mozinfo import mozlog import mozprocess +from mozfile import load_source from mozrunner.utils import get_stack_fixer_function +HERE = os.path.abspath(os.path.dirname(__file__)) + log = mozlog.unstructured.getLogger("gtest") @@ -35,6 +38,7 @@ class GTests(object): cwd, symbols_path=None, utility_path=None, + filter_set=None, ): """ Run a single C++ unit test program. @@ -53,7 +57,7 @@ class GTests(object): Return True if the program exits with a zero status, False otherwise. """ self.xre_path = xre_path - env = self.build_environment() + env = self.build_environment(filter_set) log.info("Running gtest") if cwd and not os.path.isdir(cwd): @@ -134,7 +138,7 @@ class GTests(object): return env - def build_environment(self): + def build_environment(self, filter_set): """ Create and return a dictionary of all the appropriate env variables and values. On a remote system, we overload this to set different @@ -185,6 +189,19 @@ class GTests(object): env["MOZ_WEBRENDER"] = "1" env["MOZ_ACCELERATED"] = "1" + if filter_set is not None: + filter_sets_mod_path = os.path.join(HERE, "gtest_filter_sets.py") + load_source("gtest_filter_sets", filter_sets_mod_path) + + import gtest_filter_sets + + gtest_filter_for_filter_set = gtest_filter_sets.get(filter_set) + if gtest_filter_for_filter_set: + env["GTEST_FILTER"] = gtest_filter_for_filter_set + log.info("Using gtest filter for %s", filter_set) + else: + log.info("Failed to get gtest filter for %s", filter_set) + return env @@ -218,6 +235,12 @@ class gtestOptions(argparse.ArgumentParser): default=None, help="path to a directory containing utility program binaries", ) + self.add_argument( + "--filter-set", + dest="filter_set", + default=None, + help="predefined gtest filter", + ) self.add_argument("args", nargs=argparse.REMAINDER) @@ -257,6 +280,7 @@ def main(): options.cwd, symbols_path=options.symbols_path, utility_path=options.utility_path, + filter_set=options.filter_set, ) except Exception as e: log.error(str(e)) diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index bde31d06b9df..558acdcf8f14 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -203,6 +203,15 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, CodeCoverageM "the GL compositor.", }, ], + [ + ["--filter-set"], + { + "action": "store", + "dest": "filter_set", + "default": "", + "help": "Use a predefined filter.", + }, + ], [ ["--threads"], { @@ -719,6 +728,15 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, CodeCoverageM if c["headless"]: base_cmd.append("--headless") + if c["filter_set"]: + if suite_category == "gtest": + base_cmd.append("--filter-set={}".format(c["filter_set"])) + else: + self.warning( + "--filter-set does not currently work with suites other then " + "gtest." + ) + if c.get("threads"): base_cmd.extend(["--threads", c["threads"]]) diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index a422a0ce0c2b..16b9519d278f 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -215,6 +215,8 @@ else endif cp -RL $(DIST)/bin/gtest/*buildid.* $(PKG_STAGE)/gtest/gtest_bin/gtest cp -RL $(DEPTH)/_tests/gtest $(PKG_STAGE) + cp $(topsrcdir)/testing/gtest/gtest_filter_sets.py $(PKG_STAGE)/gtest + cp $(topsrcdir)/testing/gtest/gtest_filter_sets.yml $(PKG_STAGE)/gtest cp $(topsrcdir)/testing/gtest/rungtests.py $(PKG_STAGE)/gtest cp $(topsrcdir)/testing/gtest/remotegtests.py $(PKG_STAGE)/gtest cp $(topsrcdir)/testing/gtest/mach_test_package_commands.py $(PKG_STAGE)/gtest