Skip to content

Commit c53220e

Browse files
committed
Update the PR and proposal to v4, renaming the APIs to use "scope" as the base verb instead of "execute"
1 parent 26d4fe2 commit c53220e

File tree

6 files changed

+224
-210
lines changed

6 files changed

+224
-210
lines changed

Diff for: Documentation/Proposals/NNNN-custom-test-execution-traits.md

+120-105
Large diffs are not rendered by default.

Diff for: Sources/Testing/Running/Runner.swift

+19-22
Original file line numberDiff line numberDiff line change
@@ -56,45 +56,42 @@ public struct Runner: Sendable {
5656
// MARK: - Running tests
5757

5858
extension Runner {
59-
/// Execute the ``TestExecuting/execute(_:for:testCase:)`` functions of any
60-
/// test executors for traits associated with the test in a plan step.
59+
/// Apply the custom scope for any test scope providers of the traits
60+
/// associated with a specified test by calling their
61+
/// ``TestScoping/provideScope(for:testCase:performing:)`` function.
6162
///
6263
/// - Parameters:
63-
/// - step: The step being performed.
64-
/// - testCase: The test case, if applicable, for which to execute the
65-
/// function.
64+
/// - test: The test being run, for which to provide custom scope.
65+
/// - testCase: The test case, if applicable, for which to provide custom
66+
/// scope.
6667
/// - body: A function to execute from within the
67-
/// ``TestExecuting/execute(_:for:testCase:)`` function of each non-`nil`
68-
/// test executor of the traits applied to `step.test`.
68+
/// ``TestScoping/provideScope(for:testCase:performing:)`` function of
69+
/// each non-`nil` scope provider of the traits applied to `test`.
6970
///
7071
/// - Throws: Whatever is thrown by `body` or by any of the
71-
/// ``TestExecuting/execute(_:for:testCase:)`` functions.
72-
private func _executeTraits(
73-
for step: Plan.Step,
72+
/// ``TestScoping/provideScope(for:testCase:performing:)`` function calls.
73+
private func _applyScopingTraits(
74+
for test: Test,
7475
testCase: Test.Case?,
7576
_ body: @escaping @Sendable () async throws -> Void
7677
) async throws {
7778
// If the test does not have any traits, exit early to avoid unnecessary
7879
// heap allocations below.
79-
if step.test.traits.isEmpty {
80-
return try await body()
81-
}
82-
83-
if case .skip = step.action {
80+
if test.traits.isEmpty {
8481
return try await body()
8582
}
8683

8784
// Construct a recursive function that invokes each trait's ``execute(_:for:testCase:)``
8885
// function. The order of the sequence is reversed so that the last trait is
8986
// the one that invokes body, then the second-to-last invokes the last, etc.
9087
// and ultimately the first trait is the first one to be invoked.
91-
let executeAllTraits = step.test.traits.lazy
88+
let executeAllTraits = test.traits.lazy
9289
.reversed()
93-
.compactMap { $0.executor(for: step.test, testCase: testCase) }
94-
.map { $0.execute(_:for:testCase:) }
95-
.reduce(body) { executeAllTraits, testExecutor in
90+
.compactMap { $0.scopeProvider(for: test, testCase: testCase) }
91+
.map { $0.provideScope(for:testCase:performing:) }
92+
.reduce(body) { executeAllTraits, provideScope in
9693
{
97-
try await testExecutor(executeAllTraits, step.test, testCase)
94+
try await provideScope(test, testCase, executeAllTraits)
9895
}
9996
}
10097

@@ -200,7 +197,7 @@ extension Runner {
200197
if let step = stepGraph.value, case .run = step.action {
201198
await Test.withCurrent(step.test) {
202199
_ = await Issue.withErrorRecording(at: step.test.sourceLocation, configuration: configuration) {
203-
try await _executeTraits(for: step, testCase: nil) {
200+
try await _applyScopingTraits(for: step.test, testCase: nil) {
204201
// Run the test function at this step (if one is present.)
205202
if let testCases = step.test.testCases {
206203
try await _runTestCases(testCases, within: step)
@@ -336,7 +333,7 @@ extension Runner {
336333
let sourceLocation = step.test.sourceLocation
337334
await Issue.withErrorRecording(at: sourceLocation, configuration: configuration) {
338335
try await withTimeLimit(for: step.test, configuration: configuration) {
339-
try await _executeTraits(for: step, testCase: testCase) {
336+
try await _applyScopingTraits(for: step.test, testCase: testCase) {
340337
try await testCase.body()
341338
}
342339
} timeoutHandler: { timeLimit in

Diff for: Sources/Testing/Testing.docc/Traits.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ behavior of test functions.
5353
- ``Trait``
5454
- ``TestTrait``
5555
- ``SuiteTrait``
56+
- ``TestScoping``
5657

5758
### Supporting types
5859

Diff for: Sources/Testing/Testing.docc/Traits/Trait.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ See https://swift.org/CONTRIBUTORS.txt for Swift project authors
4646

4747
- ``Trait/prepare(for:)-3s3zo``
4848

49-
### Customizing the execution of tests
49+
### Providing custom execution scope for tests
5050

51-
- ``TestExecuting``
52-
- ``Trait/executor(for:testCase:)-26qgm``
53-
- ``Trait/TestExecutor``
51+
- ``TestScoping``
52+
- ``Trait/scopeProvider(for:testCase:)-cjmg``
53+
- ``Trait/TestScopeProvider``

Diff for: Sources/Testing/Traits/Trait.swift

+65-64
Original file line numberDiff line numberDiff line change
@@ -42,114 +42,115 @@ public protocol Trait: Sendable {
4242
/// By default, the value of this property is an empty array.
4343
var comments: [Comment] { get }
4444

45-
/// The type of the test executor for this trait.
45+
/// The type of the test scope provider for this trait.
4646
///
4747
/// The default type is `Never`, which cannot be instantiated. The
48-
/// ``executor(for:testCase:)-26qgm`` method for any trait with this default
49-
/// test executor type must return `nil`, meaning that trait will not perform
50-
/// any custom behavior for the tests it's applied to.
51-
associatedtype TestExecutor: TestExecuting = Never
48+
/// ``scopeProvider(for:testCase:)-cjmg`` method for any trait with this
49+
/// default type must return `nil`, meaning that trait will not provide a
50+
/// custom scope for the tests it's applied to.
51+
associatedtype TestScopeProvider: TestScoping = Never
5252

53-
/// Get this trait's executor for the specified test and/or test case, if any.
53+
/// Get this trait's scope provider for the specified test and/or test case,
54+
/// if any.
5455
///
5556
/// - Parameters:
56-
/// - test: The test for which an executor is being requested.
57-
/// - testCase: The test case for which an executor is being requested, if
58-
/// any. When `test` represents a suite, the value of this argument is
57+
/// - test: The test for which a scope provider is being requested.
58+
/// - testCase: The test case for which a scope provider is being requested,
59+
/// if any. When `test` represents a suite, the value of this argument is
5960
/// `nil`.
6061
///
61-
/// - Returns: An value of ``Trait/TestExecutor`` which should be used to
62-
/// customize the behavior of `test` and/or `testCase`, or `nil` if custom
63-
/// behavior should not be performed.
62+
/// - Returns: A value conforming to ``Trait/TestScopeProvider`` which may be
63+
/// used to provide custom scoping for `test` and/or `testCase`, or `nil` if
64+
/// they should not have any custom scope.
6465
///
65-
/// If this trait's type conforms to ``TestExecuting``, the default value
66+
/// If this trait's type conforms to ``TestScoping``, the default value
6667
/// returned by this method depends on `test` and/or `testCase`:
6768
///
6869
/// - If `test` represents a suite, this trait must conform to ``SuiteTrait``.
6970
/// If the value of this suite trait's ``SuiteTrait/isRecursive`` property
7071
/// is `true`, then this method returns `nil`; otherwise, it returns `self`.
71-
/// This means that by default, a suite trait will _either_ perform its
72-
/// custom behavior once for the entire suite, or once per-test function it
72+
/// This means that by default, a suite trait will _either_ provide its
73+
/// custom scope once for the entire suite, or once per-test function it
7374
/// contains.
7475
/// - Otherwise `test` represents a test function. If `testCase` is `nil`,
7576
/// this method returns `nil`; otherwise, it returns `self`. This means that
7677
/// by default, a trait which is applied to or inherited by a test function
77-
/// will perform its custom behavior once for each of that function's cases.
78+
/// will provide its custom scope once for each of that function's cases.
7879
///
7980
/// A trait may explicitly implement this method to further customize the
80-
/// default behaviors above. For example, if a trait should perform custom
81-
/// test behavior both once per-suite and once per-test function in that suite,
82-
/// it may implement the method and return a non-`nil` executor under those
83-
/// conditions.
81+
/// default behaviors above. For example, if a trait should provide custom
82+
/// test scope both once per-suite and once per-test function in that suite,
83+
/// it may implement the method and return a non-`nil` scope provider under
84+
/// those conditions.
8485
///
8586
/// A trait may also implement this method and return `nil` if it determines
86-
/// that it does not need to perform any custom behavior for a particular test
87-
/// at runtime, even if the test has the trait applied. This can improve
87+
/// that it does not need to provide a custom scope for a particular test at
88+
/// runtime, even if the test has the trait applied. This can improve
8889
/// performance and make diagnostics clearer by avoiding an unnecessary call
89-
/// to ``TestExecuting/execute(_:for:testCase:)``.
90+
/// to ``TestScoping/provideScope(for:testCase:performing:)``.
9091
///
91-
/// If this trait's type does not conform to ``TestExecuting`` and its
92-
/// associated ``Trait/TestExecutor`` type is the default `Never`, then this
93-
/// method returns `nil` by default. This means that instances of this type
94-
/// will not perform any custom test execution for tests they are applied to.
95-
func executor(for test: Test, testCase: Test.Case?) -> TestExecutor?
92+
/// If this trait's type does not conform to ``TestScoping`` and its
93+
/// associated ``Trait/TestScopeProvider`` type is the default `Never`, then
94+
/// this method returns `nil` by default. This means that instances of this
95+
/// trait will not provide a custom scope for tests to which they're applied.
96+
func scopeProvider(for test: Test, testCase: Test.Case?) -> TestScopeProvider?
9697
}
9798

98-
/// A protocol that allows customizing the execution of a test function (and
99-
/// each of its cases) or a test suite by performing custom code before or after
100-
/// it runs.
99+
/// A protocol that allows providing a custom execution scope for a test
100+
/// function (and each of its cases) or a test suite by performing custom code
101+
/// before or after it runs.
101102
///
102103
/// Types conforming to this protocol may be used in conjunction with a
103104
/// ``Trait``-conforming type by implementing the
104-
/// ``Trait/executor(for:testCase:)-26qgm`` method, allowing custom traits to
105-
/// customize the execution of tests. Consolidating common set-up and tear-down
105+
/// ``Trait/scopeProvider(for:testCase:)-cjmg`` method, allowing custom traits
106+
/// to provide custom scope for tests. Consolidating common set-up and tear-down
106107
/// logic for tests which have similar needs allows each test function to be
107108
/// more succinct with less repetitive boilerplate so it can focus on what makes
108109
/// it unique.
109-
public protocol TestExecuting: Sendable {
110-
/// Execute a function for the specified test and/or test case.
110+
public protocol TestScoping: Sendable {
111+
/// Provide custom execution scope for a function call which is related to the
112+
/// specified test and/or test case.
111113
///
112114
/// - Parameters:
113-
/// - function: The function to perform. If `test` represents a test suite,
114-
/// this function encapsulates running all the tests in that suite. If
115-
/// `test` represents a test function, this function is the body of that
116-
/// test function (including all cases if it is parameterized.)
117115
/// - test: The test under which `function` is being performed.
118116
/// - testCase: The test case, if any, under which `function` is being
119117
/// performed. When invoked on a suite, the value of this argument is
120118
/// `nil`.
119+
/// - function: The function to perform. If `test` represents a test suite,
120+
/// this function encapsulates running all the tests in that suite. If
121+
/// `test` represents a test function, this function is the body of that
122+
/// test function (including all cases if it is parameterized.)
121123
///
122-
/// - Throws: Whatever is thrown by `function`, or an error preventing
123-
/// execution from running correctly. An error thrown from this method is
124-
/// recorded as an issue associated with `test`. If an error is thrown
125-
/// before `function` is called, the corresponding test will not run.
126-
///
127-
/// When the testing library is preparing to run a test, it finds all traits
128-
/// applied to that test (including those inherited from containing suites)
129-
/// and asks each for its test executor (if any) by calling
130-
/// ``Trait/executor(for:testCase:)-26qgm``. It then calls this method
131-
/// on all non-`nil` instances, giving each an opportunity to perform
132-
/// arbitrary work before or after invoking `function`.
124+
/// - Throws: Whatever is thrown by `function`, or an error preventing this
125+
/// type from providing a custom scope correctly. An error thrown from this
126+
/// method is recorded as an issue associated with `test`. If an error is
127+
/// thrown before `function` is called, the corresponding test will not run.
128+
///
129+
/// When the testing library is preparing to run a test, it starts by finding
130+
/// all traits applied to that test, including those inherited from containing
131+
/// suites. It begins with inherited suite traits, sorting them
132+
/// outermost-to-innermost, and if the test is a function, it then adds all
133+
/// traits applied directly to that functions in the order they were applied
134+
/// (left-to-right). It then asks each trait for its scope provider (if any)
135+
/// by calling ``Trait/scopeProvider(for:testCase:)-cjmg``. Finally, it calls
136+
/// this method on all non-`nil` scope providers, giving each an opportunity
137+
/// to perform arbitrary work before or after invoking `function`.
133138
///
134139
/// This method should either invoke `function` once before returning or throw
135-
/// an error if it is unable to perform its custom logic successfully.
136-
///
137-
/// This method is invoked once for the test its associated trait is applied
138-
/// to, and then once for each test case in that test, if applicable. If a
139-
/// test is skipped, this method is not invoked for that test or its cases.
140+
/// an error if it is unable to provide a custom scope.
140141
///
141142
/// Issues recorded by this method are associated with `test`.
142-
func execute(_ function: @Sendable () async throws -> Void, for test: Test, testCase: Test.Case?) async throws
143+
func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws
143144
}
144145

145-
extension Trait where Self: TestExecuting {
146-
public func executor(for test: Test, testCase: Test.Case?) -> Self? {
146+
extension Trait where Self: TestScoping {
147+
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? {
147148
testCase == nil ? nil : self
148149
}
149150
}
150151

151-
extension SuiteTrait where Self: TestExecuting {
152-
public func executor(for test: Test, testCase: Test.Case?) -> Self? {
152+
extension SuiteTrait where Self: TestScoping {
153+
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? {
153154
if test.isSuite {
154155
isRecursive ? nil : self
155156
} else {
@@ -158,8 +159,8 @@ extension SuiteTrait where Self: TestExecuting {
158159
}
159160
}
160161

161-
extension Never: TestExecuting {
162-
public func execute(_ function: @Sendable () async throws -> Void, for test: Test, testCase: Test.Case?) async throws {}
162+
extension Never: TestScoping {
163+
public func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {}
163164
}
164165

165166
/// A protocol describing traits that can be added to a test function.
@@ -191,8 +192,8 @@ extension Trait {
191192
}
192193
}
193194

194-
extension Trait where TestExecutor == Never {
195-
public func executor(for test: Test, testCase: Test.Case?) -> TestExecutor? {
195+
extension Trait where TestScopeProvider == Never {
196+
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Never? {
196197
nil
197198
}
198199
}

0 commit comments

Comments
 (0)