xref: /aosp_15_r20/external/bazelbuild-rules_python/tests/integration/integration_test.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1# Copyright 2023 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Helpers for running bazel-in-bazel integration tests."""
15
16load("@bazel_binaries//:defs.bzl", "bazel_binaries")
17load(
18    "@rules_bazel_integration_test//bazel_integration_test:defs.bzl",
19    "bazel_integration_test",
20    "integration_test_utils",
21)
22load("//python:py_test.bzl", "py_test")
23
24def _test_runner(*, name, bazel_version, py_main, bzlmod, gazelle_plugin):
25    if py_main:
26        test_runner = "{}_bazel_{}_py_runner".format(name, bazel_version)
27        py_test(
28            name = test_runner,
29            srcs = [py_main],
30            main = py_main,
31            deps = [":runner_lib"],
32            # Hide from ... patterns; should only be run as part
33            # of the bazel integration test
34            tags = ["manual"],
35        )
36        return test_runner
37
38    if bazel_version.startswith("6") and not bzlmod:
39        if gazelle_plugin:
40            return "//tests/integration:bazel_6_4_workspace_test_runner_gazelle_plugin"
41        else:
42            return "//tests/integration:bazel_6_4_workspace_test_runner"
43
44    if bzlmod and gazelle_plugin:
45        return "//tests/integration:test_runner_gazelle_plugin"
46    elif bzlmod:
47        return "//tests/integration:test_runner"
48    elif gazelle_plugin:
49        return "//tests/integration:workspace_test_runner_gazelle_plugin"
50    else:
51        return "//tests/integration:workspace_test_runner"
52
53def rules_python_integration_test(
54        name,
55        workspace_path = None,
56        bzlmod = True,
57        gazelle_plugin = False,
58        tags = None,
59        py_main = None,
60        bazel_versions = None,
61        **kwargs):
62    """Runs a bazel-in-bazel integration test.
63
64    Args:
65        name: Name of the test. This gets appended by the bazel version.
66        workspace_path: The directory name. Defaults to `name` without the
67            `_test` suffix.
68        bzlmod: bool, default True. If true, run with bzlmod enabled, otherwise
69            disable bzlmod.
70        gazelle_plugin: Whether the test uses the gazelle plugin.
71        tags: Test tags.
72        py_main: Optional `.py` file to run tests using. When specified, a
73            python based test runner is used, and this source file is the main
74            entry point and responsible for executing tests.
75        bazel_versions: `list[str] | None`, the bazel versions to test. I
76            not specified, defaults to all configured bazel versions.
77        **kwargs: Passed to the upstream `bazel_integration_tests` rule.
78    """
79    workspace_path = workspace_path or name.removesuffix("_test")
80
81    # Because glob expansion happens at loading time, the bazel-* symlinks
82    # in the workspaces can recursively expand to tens-of-thousands of entries,
83    # which consumes lots of CPU and RAM and can render the system unusable.
84    # To help prevent that, cap the size of the glob expansion.
85    workspace_files = integration_test_utils.glob_workspace_files(workspace_path)
86    if len(workspace_files) > 1000:
87        fail("Workspace {} has too many files. This likely means a bazel-* " +
88             "symlink is being followed when it should be ignored.")
89
90    # bazel_integration_tests creates a separate file group target of the workspace
91    # files for each bazel version, even though the file groups are the same
92    # for each one.
93    # To avoid that, manually create a single filegroup once and re-use it.
94    native.filegroup(
95        name = name + "_workspace_files",
96        srcs = workspace_files + [
97            "//:distribution",
98        ],
99    )
100    kwargs.setdefault("size", "enormous")
101    for bazel_version in bazel_versions or bazel_binaries.versions.all:
102        test_runner = _test_runner(
103            name = name,
104            bazel_version = bazel_version,
105            py_main = py_main,
106            bzlmod = bzlmod,
107            gazelle_plugin = gazelle_plugin,
108        )
109        bazel_integration_test(
110            name = "{}_bazel_{}".format(name, bazel_version),
111            workspace_path = workspace_path,
112            test_runner = test_runner,
113            bazel_version = bazel_version,
114            workspace_files = [name + "_workspace_files"],
115            # Override the tags so that the `manual` tag isn't applied.
116            tags = (tags or []) + [
117                # These tests are very heavy weight, so much so that only a couple
118                # can be run in parallel without harming their reliability,
119                # overall runtime, and the system's stability. Unfortunately,
120                # there doesn't appear to be a way to tell Bazel to limit their
121                # concurrency, only disable it entirely with exclusive.
122                "exclusive",
123                # The default_test_runner() assumes it can write to the user's home
124                # directory for caching purposes. Give it access.
125                "no-sandbox",
126                # The CI RBE setup can't successfully run these tests remotely.
127                "no-remote-exec",
128                # A special tag is used so CI can run them as a separate job.
129                "integration-test",
130            ],
131            **kwargs
132        )
133