Skip to content

Commit da193cb

Browse files
allevatobrentleyjones
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 (cherry picked from commit 477b48a) Signed-off-by: Brentley Jones <[email protected]>
1 parent 8a7f036 commit da193cb

File tree

6 files changed

+77
-75
lines changed

6 files changed

+77
-75
lines changed

doc/providers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ that use the toolchain.
111111
| <a id="SwiftToolchainInfo-requested_features"></a>requested_features | `List` of `string`s. Features that should be implicitly enabled by default for targets built using this toolchain, unless overridden by the user by listing their negation in the `features` attribute of a target/package or in the `--features` command line flag.<br><br>These features determine various compilation and debugging behaviors of the Swift build rules, and they are also passed to the C++ APIs used when linking (so features defined in CROSSTOOL may be used here). |
112112
| <a id="SwiftToolchainInfo-root_dir"></a>root_dir | `String`. The workspace-relative root directory of the toolchain. |
113113
| <a id="SwiftToolchainInfo-swift_worker"></a>swift_worker | `File`. The executable representing the worker executable used to invoke the compiler and other Swift tools (for both incremental and non-incremental compiles). |
114-
| <a id="SwiftToolchainInfo-test_configuration"></a>test_configuration | `Struct` containing two fields:<br><br>* `env`: A `dict` of environment variables to be set when running tests that were built with this toolchain.<br><br>* `execution_requirements`: A `dict` of execution requirements for tests that were built with this toolchain.<br><br>This is used, for example, with Xcode-based toolchains to ensure that the `xctest` helper and coverage tools are found in the correct developer directory when running tests. |
114+
| <a id="SwiftToolchainInfo-test_configuration"></a>test_configuration | `Struct` containing the following fields:<br><br>* `env`: A `dict` of environment variables to be set when running tests that were built with this toolchain.<br><br>* `execution_requirements`: A `dict` of execution requirements for tests that were built with this toolchain.<br><br>* `uses_xctest_bundles`: A Boolean value indicating whether test targets should emit `.xctest` bundles that are launched with the `xctest` tool.<br><br>This is used, for example, with Xcode-based toolchains to ensure that the `xctest` helper and coverage tools are found in the correct developer directory when running tests. |
115115
| <a id="SwiftToolchainInfo-tool_configs"></a>tool_configs | This field is an internal implementation detail of the build rules. |
116116
| <a id="SwiftToolchainInfo-unsupported_features"></a>unsupported_features | `List` of `string`s. Features that should be implicitly disabled by default for targets built using this toolchain, unless overridden by the user by listing them in the `features` attribute of a target/package or in the `--features` command line flag.<br><br>These features determine various compilation and debugging behaviors of the Swift build rules, and they are also passed to the C++ APIs used when linking (so features defined in CROSSTOOL may be used here). |
117117

doc/rules.md

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -678,41 +678,17 @@ swift_binary(
678678
## swift_test
679679

680680
<pre>
681-
swift_test(<a href="#swift_test-name">name</a>, <a href="#swift_test-deps">deps</a>, <a href="#swift_test-srcs">srcs</a>, <a href="#swift_test-data">data</a>, <a href="#swift_test-copts">copts</a>, <a href="#swift_test-defines">defines</a>, <a href="#swift_test-env">env</a>, <a href="#swift_test-linkopts">linkopts</a>, <a href="#swift_test-malloc">malloc</a>, <a href="#swift_test-module_name">module_name</a>, <a href="#swift_test-package_name">package_name</a>,
682-
<a href="#swift_test-plugins">plugins</a>, <a href="#swift_test-stamp">stamp</a>, <a href="#swift_test-swiftc_inputs">swiftc_inputs</a>)
681+
swift_test(<a href="#swift_test-name">name</a>, <a href="#swift_test-deps">deps</a>, <a href="#swift_test-srcs">srcs</a>, <a href="#swift_test-data">data</a>, <a href="#swift_test-copts">copts</a>, <a href="#swift_test-defines">defines</a>, <a href="#swift_test-discover_tests">discover_tests</a>, <a href="#swift_test-env">env</a>, <a href="#swift_test-linkopts">linkopts</a>, <a href="#swift_test-malloc">malloc</a>,
682+
<a href="#swift_test-module_name">module_name</a>, <a href="#swift_test-package_name">package_name</a>, <a href="#swift_test-plugins">plugins</a>, <a href="#swift_test-stamp">stamp</a>, <a href="#swift_test-swiftc_inputs">swiftc_inputs</a>)
683683
</pre>
684684

685685
Compiles and links Swift code into an executable test target.
686686

687-
The behavior of `swift_test` differs slightly for macOS targets, in order to
688-
provide seamless integration with Apple's XCTest framework. The output of the
689-
rule is still a binary, but one whose Mach-O type is `MH_BUNDLE` (a loadable
690-
bundle). Thus, the binary cannot be launched directly. Instead, running
691-
`bazel test` on the target will launch a test runner script that copies it into
692-
an `.xctest` bundle directory and then launches the `xctest` helper tool from
693-
Xcode, which uses Objective-C runtime reflection to locate the tests.
694-
695-
On Linux, the output of a `swift_test` is a standard executable binary, because
696-
the implementation of XCTest on that platform currently requires authors to
697-
explicitly list the tests that are present and run them from their main program.
698-
699-
Test bundling on macOS can be disabled on a per-target basis, if desired. You
700-
may wish to do this if you are not using XCTest, but rather a different test
701-
framework (or no framework at all) where the pass/fail outcome is represented as
702-
a zero/non-zero exit code (as is the case with other Bazel test rules like
703-
`cc_test`). To do so, disable the `"swift.bundled_xctests"` feature on the
704-
target:
705-
706-
```python
707-
swift_test(
708-
name = "MyTests",
709-
srcs = [...],
710-
features = ["-swift.bundled_xctests"],
711-
)
712-
```
713-
714-
You can also disable this feature for all the tests in a package by applying it
715-
to your BUILD file's `package()` declaration instead of the individual targets.
687+
By default, this rule performs _test discovery_ that finds tests written with
688+
the `XCTest` framework and executes them automatically, without the user
689+
providing their own `main` entry point. See the documentation of the
690+
`discover_tests` attribute for more information about how this affects the rule
691+
output and how to control this behavior.
716692

717693
If integrating with Xcode, the relative paths in test binaries can prevent the
718694
Issue navigator from working for test failures. To work around this, you can
@@ -738,6 +714,7 @@ bazel test //:Tests --test_filter=TestModuleName.TestClassName/testMethodName
738714
| <a id="swift_test-data"></a>data | The list of files needed by this target at runtime.<br><br>Files and targets named in the `data` attribute will appear in the `*.runfiles` area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
739715
| <a id="swift_test-copts"></a>copts | Additional compiler options that should be passed to `swiftc`. These strings are subject to `$(location ...)` and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | `[]` |
740716
| <a id="swift_test-defines"></a>defines | A list of defines to add to the compilation command line.<br><br>Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** `name=value` pairs.<br><br>Each string is prepended with `-D` and added to the command line. Unlike `copts`, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to `copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | `[]` |
717+
| <a id="swift_test-discover_tests"></a>discover_tests | Determines whether or not tests are automatically discovered in the binary. The default value is `True`.<br><br>If tests are discovered, then you should not provide your own `main` entry point in the `swift_test` binary; the test runtime provides the entry point for you. If you set this attribute to `False`, then you are responsible for providing your own `main`. This allows you to write tests that use a framework other than Apple's `XCTest`. The only requirement of such a test is that it terminate with a zero exit code for success or a non-zero exit code for failure.<br><br>Additionally, on Apple platforms, test discovery is handled by the Objective-C runtime and the output of a `swift_test` rule is an `.xctest` bundle that is invoked using the `xctest` tool in Xcode. If this attribute is used to disable test discovery, then the output of the `swift_test` rule will instead be a standard executable binary that is invoked directly. | Boolean | optional | `True` |
741718
| <a id="swift_test-env"></a>env | Dictionary of environment variables that should be set during the test execution. | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | `{}` |
742719
| <a id="swift_test-linkopts"></a>linkopts | Additional linker options that should be passed to `clang`. These strings are subject to `$(location ...)` expansion. | List of strings | optional | `[]` |
743720
| <a id="swift_test-malloc"></a>malloc | Override the default dependency on `malloc`.<br><br>By default, Swift binaries are linked against `@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use libc's `malloc`. This label must refer to a `cc_library` rule. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `"@bazel_tools//tools/cpp:malloc"` |

swift/providers.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,17 @@ compiler and other Swift tools (for both incremental and non-incremental
301301
compiles).
302302
""",
303303
"test_configuration": """\
304-
`Struct` containing two fields:
304+
`Struct` containing the following fields:
305305
306306
* `env`: A `dict` of environment variables to be set when running tests
307307
that were built with this toolchain.
308308
309309
* `execution_requirements`: A `dict` of execution requirements for tests
310310
that were built with this toolchain.
311311
312+
* `uses_xctest_bundles`: A Boolean value indicating whether test targets
313+
should emit `.xctest` bundles that are launched with the `xctest` tool.
314+
312315
This is used, for example, with Xcode-based toolchains to ensure that the
313316
`xctest` helper and coverage tools are found in the correct developer
314317
directory when running tests.

swift/swift_test.bzl

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,17 @@ def _swift_test_impl(ctx):
224224
unsupported_features = ctx.disabled_features,
225225
)
226226

227-
is_bundled = swift_common.is_enabled(
228-
feature_configuration = feature_configuration,
229-
feature_name = SWIFT_FEATURE_BUNDLED_XCTESTS,
227+
discover_tests = ctx.attr.discover_tests
228+
229+
# TODO(b/220945250): Remove the `bundled_xctests` feature and use only the
230+
# toolchain bit instead.
231+
is_bundled = (
232+
discover_tests and
233+
swift_toolchain.test_configuration.uses_xctest_bundles and
234+
swift_common.is_enabled(
235+
feature_configuration = feature_configuration,
236+
feature_name = SWIFT_FEATURE_BUNDLED_XCTESTS,
237+
)
230238
)
231239

232240
# If we need to run the test in an .xctest bundle, the binary must have
@@ -255,6 +263,7 @@ def _swift_test_impl(ctx):
255263
additional_linking_contexts.append(plugin_info.cc_info.linking_context)
256264

257265
srcs = ctx.files.srcs
266+
extra_copts = []
258267
extra_deps = []
259268

260269
# If no sources were provided and we're not using `.xctest` bundling, assume
@@ -263,16 +272,30 @@ def _swift_test_impl(ctx):
263272
# (a separate module) maps to its own `swift_library`. We'll need to modify
264273
# this approach if we want to support test discovery for simple `swift_test`
265274
# targets that just write XCTest-style tests in the `srcs` directly.
266-
if not srcs and not is_bundled:
267-
srcs = _generate_test_discovery_srcs(
268-
actions = ctx.actions,
269-
deps = ctx.attr.deps,
270-
name = ctx.label.name,
271-
test_discoverer = ctx.executable._test_discoverer,
272-
)
273-
extra_deps = [ctx.attr._test_observer]
274-
elif is_bundled:
275+
if discover_tests:
276+
if (
277+
not srcs and
278+
not swift_toolchain.test_configuration.uses_xctest_bundles
279+
):
280+
srcs = _generate_test_discovery_srcs(
281+
actions = ctx.actions,
282+
deps = ctx.attr.deps,
283+
name = ctx.label.name,
284+
test_discoverer = ctx.executable._test_discoverer,
285+
)
286+
287+
# Discovered tests don't need an entry point; on Apple platforms,
288+
# the binary is compiled as a bundle, and on non-Apple platforms,
289+
# the generated sources above use `@main`.
290+
# TODO(b/220945250): This should be moved out of this branch of the
291+
# conditional, but it would break some tests that are already
292+
# depending on this and need to be fixed.
293+
extra_copts = ["-parse-as-library"]
294+
295+
# Inject the test observer that prints the xUnit-style output for Bazel.
275296
extra_deps = [ctx.attr._test_observer]
297+
else:
298+
extra_copts = _maybe_parse_as_library_copts(srcs)
276299

277300
module_contexts = []
278301
all_supplemental_outputs = []
@@ -292,7 +315,7 @@ def _swift_test_impl(ctx):
292315
ctx,
293316
ctx.attr.copts,
294317
ctx.attr.swiftc_inputs,
295-
) + _maybe_parse_as_library_copts(srcs),
318+
) + extra_copts,
296319
defines = ctx.attr.defines,
297320
extra_swift_infos = extra_swift_infos,
298321
feature_configuration = feature_configuration,
@@ -434,6 +457,27 @@ swift_test = rule(
434457
stamp_default = 0,
435458
),
436459
{
460+
"discover_tests": attr.bool(
461+
default = True,
462+
doc = """\
463+
Determines whether or not tests are automatically discovered in the binary. The
464+
default value is `True`.
465+
466+
If tests are discovered, then you should not provide your own `main` entry point
467+
in the `swift_test` binary; the test runtime provides the entry point for you.
468+
If you set this attribute to `False`, then you are responsible for providing
469+
your own `main`. This allows you to write tests that use a framework other than
470+
Apple's `XCTest`. The only requirement of such a test is that it terminate with
471+
a zero exit code for success or a non-zero exit code for failure.
472+
473+
Additionally, on Apple platforms, test discovery is handled by the Objective-C
474+
runtime and the output of a `swift_test` rule is an `.xctest` bundle that is
475+
invoked using the `xctest` tool in Xcode. If this attribute is used to disable
476+
test discovery, then the output of the `swift_test` rule will instead be a
477+
standard executable binary that is invoked directly.
478+
""",
479+
mandatory = False,
480+
),
437481
"env": attr.string_dict(
438482
doc = """
439483
Dictionary of environment variables that should be set during the test execution.
@@ -473,35 +517,11 @@ swift_test = rule(
473517
doc = """\
474518
Compiles and links Swift code into an executable test target.
475519
476-
The behavior of `swift_test` differs slightly for macOS targets, in order to
477-
provide seamless integration with Apple's XCTest framework. The output of the
478-
rule is still a binary, but one whose Mach-O type is `MH_BUNDLE` (a loadable
479-
bundle). Thus, the binary cannot be launched directly. Instead, running
480-
`bazel test` on the target will launch a test runner script that copies it into
481-
an `.xctest` bundle directory and then launches the `xctest` helper tool from
482-
Xcode, which uses Objective-C runtime reflection to locate the tests.
483-
484-
On Linux, the output of a `swift_test` is a standard executable binary, because
485-
the implementation of XCTest on that platform currently requires authors to
486-
explicitly list the tests that are present and run them from their main program.
487-
488-
Test bundling on macOS can be disabled on a per-target basis, if desired. You
489-
may wish to do this if you are not using XCTest, but rather a different test
490-
framework (or no framework at all) where the pass/fail outcome is represented as
491-
a zero/non-zero exit code (as is the case with other Bazel test rules like
492-
`cc_test`). To do so, disable the `"swift.bundled_xctests"` feature on the
493-
target:
494-
495-
```python
496-
swift_test(
497-
name = "MyTests",
498-
srcs = [...],
499-
features = ["-swift.bundled_xctests"],
500-
)
501-
```
502-
503-
You can also disable this feature for all the tests in a package by applying it
504-
to your BUILD file's `package()` declaration instead of the individual targets.
520+
By default, this rule performs _test discovery_ that finds tests written with
521+
the `XCTest` framework and executes them automatically, without the user
522+
providing their own `main` entry point. See the documentation of the
523+
`discover_tests` attribute for more information about how this affects the rule
524+
output and how to control this behavior.
505525
506526
If integrating with Xcode, the relative paths in test binaries can prevent the
507527
Issue navigator from working for test failures. To work around this, you can

swift/toolchains/swift_toolchain.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ def _swift_toolchain_impl(ctx):
549549
test_configuration = struct(
550550
env = env,
551551
execution_requirements = {},
552+
uses_xctest_bundles = False,
552553
),
553554
tool_configs = all_tool_configs,
554555
unsupported_features = ctx.disabled_features + [

swift/toolchains/xcode_swift_toolchain.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ def _xcode_swift_toolchain_impl(ctx):
806806
test_configuration = struct(
807807
env = env,
808808
execution_requirements = execution_requirements,
809+
uses_xctest_bundles = True,
809810
),
810811
tool_configs = all_tool_configs,
811812
unsupported_features = ctx.disabled_features + [

0 commit comments

Comments
 (0)