Skip to content

Commit 477b48a

Browse files
allevatoswiple-rules-gardener
authored andcommitted
Add a discover_tests attribute to swift_test.
This attribute, which is `True` by default, controls whether `XCTest`-style tests are automatically discovered (either using the Objective-C runtime or manual discovery via symbol graphs). If set to `False`, the test is assumed to provide its own `main`. This will subsume the behavior of the `swift.bundled_xctests` feature, which will be removed in the future. PiperOrigin-RevId: 471589475
1 parent d3302c7 commit 477b48a

File tree

3 files changed

+62
-44
lines changed

3 files changed

+62
-44
lines changed

swift/providers.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,17 @@ compiler and other Swift tools (for both incremental and non-incremental
213213
compiles).
214214
""",
215215
"test_configuration": """\
216-
`Struct` containing two fields:
216+
`Struct` containing the following fields:
217217
218218
* `env`: A `dict` of environment variables to be set when running tests
219219
that were built with this toolchain.
220220
221221
* `execution_requirements`: A `dict` of execution requirements for tests
222222
that were built with this toolchain.
223223
224+
* `uses_xctest_bundles`: A Boolean value indicating whether test targets
225+
should emit `.xctest` bundles that are launched with the `xctest` tool.
226+
224227
This is used, for example, with Xcode-based toolchains to ensure that the
225228
`xctest` helper and coverage tools are found in the correct developer
226229
directory when running tests.

swift/swift_test.bzl

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,17 @@ def _swift_test_impl(ctx):
197197
unsupported_features = ctx.disabled_features,
198198
)
199199

200-
is_bundled = swift_common.is_enabled(
201-
feature_configuration = feature_configuration,
202-
feature_name = SWIFT_FEATURE_BUNDLED_XCTESTS,
200+
discover_tests = ctx.attr.discover_tests
201+
202+
# TODO(b/220945250): Remove the `bundled_xctests` feature and use only the
203+
# toolchain bit instead.
204+
is_bundled = (
205+
discover_tests and
206+
swift_toolchain.test_configuration.uses_xctest_bundles and
207+
swift_common.is_enabled(
208+
feature_configuration = feature_configuration,
209+
feature_name = SWIFT_FEATURE_BUNDLED_XCTESTS,
210+
)
203211
)
204212

205213
# If we need to run the test in an .xctest bundle, the binary must have
@@ -216,18 +224,27 @@ def _swift_test_impl(ctx):
216224
# (a separate module) maps to its own `swift_library`. We'll need to modify
217225
# this approach if we want to support test discovery for simple `swift_test`
218226
# targets that just write XCTest-style tests in the `srcs` directly.
219-
if not srcs and not is_bundled:
220-
srcs = _generate_test_discovery_srcs(
221-
actions = ctx.actions,
222-
deps = ctx.attr.deps,
223-
name = ctx.label.name,
224-
test_discoverer = ctx.executable._test_discoverer,
225-
)
227+
if discover_tests:
228+
if (
229+
not srcs and
230+
not swift_toolchain.test_configuration.uses_xctest_bundles
231+
):
232+
srcs = _generate_test_discovery_srcs(
233+
actions = ctx.actions,
234+
deps = ctx.attr.deps,
235+
name = ctx.label.name,
236+
test_discoverer = ctx.executable._test_discoverer,
237+
)
226238

227-
# The generated test runner uses `@main`.
228-
extra_copts = ["-parse-as-library"]
229-
extra_deps = [ctx.attr._test_observer]
230-
elif is_bundled:
239+
# Discovered tests don't need an entry point; on Apple platforms,
240+
# the binary is compiled as a bundle, and on non-Apple platforms,
241+
# the generated sources above use `@main`.
242+
# TODO(b/220945250): This should be moved out of this branch of the
243+
# conditional, but it would break some tests that are already
244+
# depending on this and need to be fixed.
245+
extra_copts = ["-parse-as-library"]
246+
247+
# Inject the test observer that prints the xUnit-style output for Bazel.
231248
extra_deps = [ctx.attr._test_observer]
232249

233250
output_groups = {}
@@ -348,6 +365,27 @@ swift_test = rule(
348365
stamp_default = 0,
349366
),
350367
{
368+
"discover_tests": attr.bool(
369+
default = True,
370+
doc = """\
371+
Determines whether or not tests are automatically discovered in the binary. The
372+
default value is `True`.
373+
374+
If tests are discovered, then you should not provide your own `main` entry point
375+
in the `swift_test` binary; the test runtime provides the entry point for you.
376+
If you set this attribute to `False`, then you are responsible for providing
377+
your own `main`. This allows you to write tests that use a framework other than
378+
Apple's `XCTest`. The only requirement of such a test is that it terminate with
379+
a zero exit code for success or a non-zero exit code for failure.
380+
381+
Additionally, on Apple platforms, test discovery is handled by the Objective-C
382+
runtime and the output of a `swift_test` rule is an `.xctest` bundle that is
383+
invoked using the `xctest` tool in Xcode. If this attribute is used to disable
384+
test discovery, then the output of the `swift_test` rule will instead be a
385+
standard executable binary that is invoked directly.
386+
""",
387+
mandatory = False,
388+
),
351389
"_apple_coverage_support": attr.label(
352390
cfg = "exec",
353391
default = Label(
@@ -377,35 +415,11 @@ swift_test = rule(
377415
doc = """\
378416
Compiles and links Swift code into an executable test target.
379417
380-
The behavior of `swift_test` differs slightly for macOS targets, in order to
381-
provide seamless integration with Apple's XCTest framework. The output of the
382-
rule is still a binary, but one whose Mach-O type is `MH_BUNDLE` (a loadable
383-
bundle). Thus, the binary cannot be launched directly. Instead, running
384-
`bazel test` on the target will launch a test runner script that copies it into
385-
an `.xctest` bundle directory and then launches the `xctest` helper tool from
386-
Xcode, which uses Objective-C runtime reflection to locate the tests.
387-
388-
On Linux, the output of a `swift_test` is a standard executable binary, because
389-
the implementation of XCTest on that platform currently requires authors to
390-
explicitly list the tests that are present and run them from their main program.
391-
392-
Test bundling on macOS can be disabled on a per-target basis, if desired. You
393-
may wish to do this if you are not using XCTest, but rather a different test
394-
framework (or no framework at all) where the pass/fail outcome is represented as
395-
a zero/non-zero exit code (as is the case with other Bazel test rules like
396-
`cc_test`). To do so, disable the `"swift.bundled_xctests"` feature on the
397-
target:
398-
399-
```python
400-
swift_test(
401-
name = "MyTests",
402-
srcs = [...],
403-
features = ["-swift.bundled_xctests"],
404-
)
405-
```
406-
407-
You can also disable this feature for all the tests in a package by applying it
408-
to your BUILD file's `package()` declaration instead of the individual targets.
418+
By default, this rule performs _test discovery_ that finds tests written with
419+
the `XCTest` framework and executes them automatically, without the user
420+
providing their own `main` entry point. See the documentation of the
421+
`discover_tests` attribute for more information about how this affects the rule
422+
output and how to control this behavior.
409423
""",
410424
executable = True,
411425
fragments = ["cpp"],

swift/toolchains/xcode_swift_toolchain.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ def _xcode_swift_toolchain_impl(ctx):
802802
test_configuration = struct(
803803
env = env,
804804
execution_requirements = execution_requirements,
805+
uses_xctest_bundles = True,
805806
),
806807
tool_configs = all_tool_configs,
807808
unsupported_features = ctx.disabled_features + [

0 commit comments

Comments
 (0)