File: suites.py

package info (click to toggle)
firefox 147.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,320 kB
  • sloc: cpp: 7,607,359; javascript: 6,533,295; ansic: 3,775,223; python: 1,415,500; xml: 634,561; asm: 438,949; java: 186,241; sh: 62,752; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (132 lines) | stat: -rw-r--r-- 3,897 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 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 itertools
import os
import re
import subprocess


def get_gtest_suites(args, cwd, gtest_env):
    """
    Get a list of gtest suite names from a gtest program.

    * args - The arguments (including executable) for the gtest program.
    * cwd - The working directory to use.
    * gtest_env - Additional environment variables to set.

    Returns a list of the suite names.
    """
    # List the tests to get the suite names
    args.append("--gtest_list_tests")

    env = {}
    env.update(os.environ)
    env.update(gtest_env)
    completed_proc = subprocess.run(
        args, cwd=cwd, env=env, capture_output=True, check=True, text=True
    )
    output = completed_proc.stdout

    # Suite names are exclusively text without whitespace, and followed by
    # a '.', optionally with `  #` and type parameter information. This is
    # specific enough to reasonably filter out some extra strings output by
    # firefox.
    SUITE_REGEX = re.compile(r"(\S+).(  # .*)?")

    def get_suite_name(line):
        match = SUITE_REGEX.fullmatch(line)
        if match:
            return match[1]

    suites = list(
        filter(lambda x: x is not None, map(get_suite_name, output.splitlines()))
    )

    # Remove the `--gtest_list_tests` arg that we added
    args.pop()

    return suites


class _JoinedSubsetOfStrings:
    """
    Efficient creation of joined strings for subsets of a list of strings.

    This allows creation of joined strings in O(1) instead of O(n) each time (n = list
    length), with a one-time O(n) cost.
    """

    def __init__(self, between, strs):
        """
        Arguments:
        * between - the string with which to join the strings
        * strs - an iterable of strings
        """
        strs = list(strs)
        self._string = between.join(strs)
        betweenlen = len(between)
        self._offsets = list(
            itertools.accumulate(map(lambda s: len(s) + betweenlen, strs), initial=0)
        )

    def without(self, index):
        """Create a joined string excluding the given index."""
        return (
            self._string[: self._offsets[index]]
            + self._string[self._offsets[index + 1] :]
        )


class SuiteFilter:
    def __init__(self, joined, index, suite):
        self._joined = joined
        self.index = index
        self.suite = suite

    def create(self, existing_filter=None):
        """Create a filter to only run this suite."""
        if existing_filter is None or existing_filter == "*":
            return f"{self.suite}.*"
        else:
            return (
                existing_filter
                + (":" if "-" in existing_filter else "-")
                + self._joined.without(self.index)
            )

    def set_in_env(self, env):
        """
        Set the filter to only run this suite in an environment mapping.

        Returns the passed env.
        """
        env["GTEST_FILTER"] = self.create(env.get("GTEST_FILTER"))
        return env

    def __call__(self, val):
        """
        If called on a dict, creates a copy and forwards to `set_in_env`,
        otherwise forwards to `create`.
        """
        if isinstance(val, dict):
            return self.set_in_env(val.copy())
        else:
            return self.create(val)


def suite_filters(suites):
    """
    Form gtest filters to limit tests to a single suite.

    This is a generator that yields a SuiteFilter for each suite.

    Arguments:
    * suites - an iterable of the suite names
    """
    suites = list(suites)
    joined = _JoinedSubsetOfStrings(":", map(lambda s: f"{s}.*", suites))

    for i, suite in enumerate(suites):
        yield SuiteFilter(joined, i, suite)