@@ -41,6 +41,150 @@ public protocol Trait: Sendable {
4141  ///
4242  /// By default, the value of this property is an empty array.
4343  var  comments :  [ Comment ]  {  get  } 
44+ 
45+   /// The type of the test scope provider for this trait.
46+   ///
47+   /// The default type is `Never`, which cannot be instantiated. The
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 
52+ 
53+   /// Get this trait's scope provider for the specified test and/or test case,
54+   /// if any.
55+   ///
56+   /// - Parameters:
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
60+   ///     `nil`.
61+   ///
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.
65+   ///
66+   /// If this trait's type conforms to ``TestScoping``, the default value
67+   /// returned by this method depends on `test` and/or `testCase`:
68+   ///
69+   /// - If `test` represents a suite, this trait must conform to ``SuiteTrait``.
70+   ///   If the value of this suite trait's ``SuiteTrait/isRecursive`` property
71+   ///   is `true`, then this method returns `nil`; otherwise, it returns `self`.
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
74+   ///   contains.
75+   /// - Otherwise `test` represents a test function. If `testCase` is `nil`,
76+   ///   this method returns `nil`; otherwise, it returns `self`. This means that
77+   ///   by default, a trait which is applied to or inherited by a test function
78+   ///   will provide its custom scope once for each of that function's cases.
79+   ///
80+   /// A trait may explicitly implement this method to further customize the
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.
85+   ///
86+   /// A trait may also implement this method and return `nil` if it determines
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
89+   /// performance and make diagnostics clearer by avoiding an unnecessary call
90+   /// to ``TestScoping/provideScope(for:testCase:performing:)``.
91+   ///
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 ? 
97+ } 
98+ 
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.
102+ ///
103+ /// Types conforming to this protocol may be used in conjunction with a
104+ /// ``Trait``-conforming type by implementing the
105+ /// ``Trait/scopeProvider(for:testCase:)-cjmg`` method, allowing custom traits
106+ /// to provide custom scope for tests. Consolidating common set-up and tear-down
107+ /// logic for tests which have similar needs allows each test function to be
108+ /// more succinct with less repetitive boilerplate so it can focus on what makes
109+ /// it unique.
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.
113+   ///
114+   /// - Parameters:
115+   ///   - test: The test under which `function` is being performed.
116+   ///   - testCase: The test case, if any, under which `function` is being
117+   ///     performed. When invoked on a suite, the value of this argument is
118+   ///     `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.)
123+   ///
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`.
138+   ///
139+   /// This method should either invoke `function` once before returning or throw
140+   /// an error if it is unable to provide a custom scope.
141+   ///
142+   /// Issues recorded by this method are associated with `test`.
143+   func  provideScope( for test:  Test ,  testCase:  Test . Case ? ,  performing function:  @Sendable   ( )  async  throws  ->  Void )  async  throws 
144+ } 
145+ 
146+ extension  Trait  where  Self:  TestScoping  { 
147+   /// Get this trait's scope provider for the specified test and/or test case,
148+   /// if any.
149+   ///
150+   /// - Parameters:
151+   ///   - test: The test for which a scope provider is being requested.
152+   ///   - testCase: The test case for which a scope provider is being requested,
153+   ///     if any. When `test` represents a suite, the value of this argument is
154+   ///     `nil`.
155+   ///
156+   /// This default implementation is used when this trait type conforms to
157+   /// ``TestScoping`` and its return value is discussed in
158+   /// ``Trait/scopeProvider(for:testCase:)-cjmg``.
159+   public  func  scopeProvider( for test:  Test ,  testCase:  Test . Case ? )  ->  Self ? { 
160+     testCase ==  nil  ?  nil  :  self 
161+   } 
162+ } 
163+ 
164+ extension  SuiteTrait  where  Self:  TestScoping  { 
165+   /// Get this trait's scope provider for the specified test and/or test case,
166+   /// if any.
167+   ///
168+   /// - Parameters:
169+   ///   - test: The test for which a scope provider is being requested.
170+   ///   - testCase: The test case for which a scope provider is being requested,
171+   ///     if any. When `test` represents a suite, the value of this argument is
172+   ///     `nil`.
173+   ///
174+   /// This default implementation is used when this trait type conforms to
175+   /// ``TestScoping`` and its return value is discussed in
176+   /// ``Trait/scopeProvider(for:testCase:)-cjmg``.
177+   public  func  scopeProvider( for test:  Test ,  testCase:  Test . Case ? )  ->  Self ? { 
178+     if  test. isSuite { 
179+       isRecursive ?  nil  :  self 
180+     }  else  { 
181+       testCase ==  nil  ?  nil  :  self 
182+     } 
183+   } 
184+ } 
185+ 
186+ extension  Never :  TestScoping  { 
187+   public  func  provideScope( for test:  Test ,  testCase:  Test . Case ? ,  performing function:  @Sendable   ( )  async  throws  ->  Void )  async  throws  { } 
44188} 
45189
46190/// A protocol describing traits that can be added to a test function.
@@ -72,43 +216,26 @@ extension Trait {
72216  } 
73217} 
74218
219+ extension  Trait  where  TestScopeProvider ==  Never  { 
220+   /// Get this trait's scope provider for the specified test and/or test case,
221+   /// if any.
222+   ///
223+   /// - Parameters:
224+   ///   - test: The test for which a scope provider is being requested.
225+   ///   - testCase: The test case for which a scope provider is being requested,
226+   ///     if any. When `test` represents a suite, the value of this argument is
227+   ///     `nil`.
228+   ///
229+   /// This default implementation is used when this trait type's associated
230+   /// ``Trait/TestScopeProvider`` type is the default value of `Never`, and its
231+   /// return value is discussed in ``Trait/scopeProvider(for:testCase:)-cjmg``.
232+   public  func  scopeProvider( for test:  Test ,  testCase:  Test . Case ? )  ->  Never ? { 
233+     nil 
234+   } 
235+ } 
236+ 
75237extension  SuiteTrait  { 
76238  public  var  isRecursive :  Bool  { 
77239    false 
78240  } 
79241} 
80- 
81- /// A protocol extending ``Trait`` that offers an additional customization point
82- /// for trait authors to execute code before and after each test function (if
83- /// added to the traits of a test function), or before and after each test suite
84- /// (if added to the traits of a test suite).
85- @_spi ( Experimental)  
86- public  protocol  CustomExecutionTrait :  Trait  { 
87- 
88-   /// Execute a function with the effects of this trait applied.
89-   ///
90-   /// - Parameters:
91-   ///   - function: The function to perform. If `test` represents a test suite,
92-   ///     this function encapsulates running all the tests in that suite. If
93-   ///     `test` represents a test function, this function is the body of that
94-   ///     test function (including all cases if it is parameterized.)
95-   ///   - test: The test under which `function` is being performed.
96-   ///   - testCase: The test case, if any, under which `function` is being
97-   ///     performed. This is `nil` when invoked on a suite.
98-   ///
99-   /// - Throws: Whatever is thrown by `function`, or an error preventing the
100-   ///   trait from running correctly.
101-   ///
102-   /// This function is called for each ``CustomExecutionTrait`` on a test suite
103-   /// or test function and allows additional work to be performed before and
104-   /// after the test runs.
105-   ///
106-   /// This function is invoked once for the test it is applied to, and then once
107-   /// for each test case in that test, if applicable.
108-   ///
109-   /// Issues recorded by this function are recorded against `test`.
110-   ///
111-   /// - Note: If a test function or test suite is skipped, this function does
112-   ///   not get invoked by the runner.
113-   func  execute( _ function:  @Sendable   ( )  async  throws  ->  Void ,  for test:  Test ,  testCase:  Test . Case ? )  async  throws 
114- } 
0 commit comments