1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//
/// A protocol describing traits that can be added to a test function or to a
/// test suite.
///
/// The testing library defines a number of traits that can be added to test
/// functions and to test suites. Define your own traits by
/// creating types that conform to ``TestTrait`` or ``SuiteTrait``:
///
/// - term ``TestTrait``: Conform to this type in traits that you add to test
/// functions.
/// - term ``SuiteTrait``: Conform to this type in traits that you add to test
/// suites.
///
/// You can add a trait that conforms to both ``TestTrait`` and ``SuiteTrait``
/// to test functions and test suites.
public protocol Trait: Sendable {
/// Prepare to run the test that has this trait.
///
/// - Parameters:
/// - test: The test that has this trait.
///
/// - Throws: Any error that prevents the test from running. If an error
/// is thrown from this method, the test is skipped and the error is
/// recorded as an ``Issue``.
///
/// The testing library calls this method after it discovers all tests and
/// their traits, and before it begins to run any tests.
/// Use this method to prepare necessary internal state, or to determine
/// whether the test should run.
///
/// The default implementation of this method does nothing.
func prepare(for test: Test) async throws
/// The user-provided comments for this trait.
///
/// The default value of this property is an empty array.
var comments: [Comment] { get }
/// The type of the test scope provider for this trait.
///
/// The default type is `Never`, which can't be instantiated. The
/// ``scopeProvider(for:testCase:)-cjmg`` method for any trait with
/// `Never` as its test scope provider type must return `nil`, meaning that
/// the trait doesn't provide a custom scope for tests it's applied to.
associatedtype TestScopeProvider: TestScoping = Never
/// Get this trait's scope provider for the specified test and optional test
/// case.
///
/// - Parameters:
/// - test: The test for which a scope provider is being requested.
/// - testCase: The test case for which a scope provider is being requested,
/// if any. When `test` represents a suite, the value of this argument is
/// `nil`.
///
/// - Returns: A value conforming to ``Trait/TestScopeProvider`` which you
/// use to provide custom scoping for `test` or `testCase`. Returns `nil` if
/// the trait doesn't provide any custom scope for the test or test case.
///
/// If this trait's type conforms to ``TestScoping``, the default value
/// returned by this method depends on the values of`test` and `testCase`:
///
/// - If `test` represents a suite, this trait must conform to ``SuiteTrait``.
/// If the value of this suite trait's ``SuiteTrait/isRecursive`` property
/// is `true`, then this method returns `nil`, and the suite trait
/// provides its custom scope once for each test function the test suite
/// contains. If the value of ``SuiteTrait/isRecursive`` is `false`, this
/// method returns `self`, and the suite trait provides its custom scope
/// once for the entire test suite.
/// - If `test` represents a test function, this trait also conforms to
/// ``TestTrait``. If `testCase` is `nil`, this method returns `nil`;
/// otherwise, it returns `self`. This means that by default, a trait which
/// is applied to or inherited by a test function provides its custom scope
/// once for each of that function's cases.
///
/// A trait may override this method to further customize the
/// default behaviors above. For example, if a trait needs to provide custom
/// test scope both once per-suite and once per-test function in that suite,
/// it implements the method to return a non-`nil` scope provider under
/// those conditions.
///
/// A trait may also implement this method and return `nil` if it determines
/// that it does not need to provide a custom scope for a particular test at
/// runtime, even if the test has the trait applied. This can improve
/// performance and make diagnostics clearer by avoiding an unnecessary call
/// to ``TestScoping/provideScope(for:testCase:performing:)``.
///
/// If this trait's type does not conform to ``TestScoping`` and its
/// associated ``Trait/TestScopeProvider`` type is the default `Never`, then
/// this method returns `nil` by default. This means that instances of this
/// trait don't provide a custom scope for tests to which they're applied.
func scopeProvider(for test: Test, testCase: Test.Case?) -> TestScopeProvider?
}
/// A protocol that tells the test runner to run custom code before or after it
/// runs a test suite or test function.
///
/// Provide custom scope for tests by implementing the
/// ``Trait/scopeProvider(for:testCase:)-cjmg`` method, returning a type that
/// conforms to this protocol. Create a custom scope to consolidate common
/// set-up and tear-down logic for tests which have similar needs, which allows
/// each test function to focus on the unique aspects of its test.
public protocol TestScoping: Sendable {
/// Provide custom execution scope for a function call which is related to the
/// specified test or test case.
///
/// - Parameters:
/// - test: The test which `function` encapsulates.
/// - testCase: The test case, if any, which `function` encapsulates.
/// When invoked on a suite, the value of this argument is `nil`.
/// - function: The function to perform. If `test` represents a test suite,
/// this function encapsulates running all the tests in that suite. If
/// `test` represents a test function, this function is the body of that
/// test function (including all cases if the test function is
/// parameterized.)
///
/// - Throws: Any error that `function` throws, or an error that prevents this
/// type from providing a custom scope correctly. The testing library
/// records an error thrown from this method as an issue associated with
/// `test`. If an error is thrown before this method calls `function`, the
/// corresponding test doesn't run.
///
/// When the testing library prepares to run a test, it starts by finding
/// all traits applied to that test, including those inherited from containing
/// suites. It begins with inherited suite traits, sorting them
/// outermost-to-innermost, and if the test is a function, it then adds all
/// traits applied directly to that functions in the order they were applied
/// (left-to-right). It then asks each trait for its scope provider (if any)
/// by calling ``Trait/scopeProvider(for:testCase:)-cjmg``. Finally, it calls
/// this method on all non-`nil` scope providers, giving each an opportunity
/// to perform arbitrary work before or after invoking `function`.
///
/// This method should either invoke `function` once before returning,
/// or throw an error if it's unable to provide a custom scope.
///
/// Issues recorded by this method are associated with `test`.
func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws
}
extension Trait where Self: TestScoping {
/// Get this trait's scope provider for the specified test or test case.
///
/// - Parameters:
/// - test: The test for which the testing library requests a
/// scope provider.
/// - testCase: The test case for which the testing library requests a scope
/// provider, if any. When `test` represents a suite, the value of this argument is
/// `nil`.
///
/// The testing library uses this implementation of
/// ``Trait/scopeProvider(for:testCase:)-cjmg`` when the trait type conforms
/// to ``TestScoping``.
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? {
testCase == nil ? nil : self
}
}
extension SuiteTrait where Self: TestScoping {
/// Get this trait's scope provider for the specified test and optional test
/// case.
///
/// - Parameters:
/// - test: The test for which the testing library requests a scope
/// provider.
/// - testCase: The test case for which the testing library requests a scope
/// provider, if any. When `test` represents a suite, the value of this
/// argument is `nil`.
///
/// The testing library uses this implementation of
/// ``Trait/scopeProvider(for:testCase:)-cjmg`` when the trait type conforms
/// to both ``SuiteTrait`` and ``TestScoping``.
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? {
if test.isSuite {
isRecursive ? nil : self
} else {
testCase == nil ? nil : self
}
}
}
extension Never: TestScoping {
public func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {}
}
/// A protocol describing a trait that you can add to a test function.
///
/// The testing library defines a number of traits that you can add to test
/// functions. You can also define your own traits by creating types
/// that conform to this protocol, or to the ``SuiteTrait`` protocol.
public protocol TestTrait: Trait {}
/// A protocol describing a trait that you can add to a test suite.
///
/// The testing library defines a number of traits that you can add to test
/// suites. You can also define your own traits by creating types that
/// conform to this protocol, or to the ``TestTrait`` protocol.
public protocol SuiteTrait: Trait {
/// Whether this instance should be applied recursively to child test suites
/// and test functions.
///
/// If the value is `true`, then the testing library applies this trait
/// recursively to child test suites and test functions. Otherwise, it only
/// applies the trait to the test suite to which you added the trait.
///
/// By default, traits are not recursively applied to children.
var isRecursive: Bool { get }
}
extension Trait {
public func prepare(for test: Test) async throws {}
public var comments: [Comment] {
[]
}
}
extension Trait where TestScopeProvider == Never {
/// Get this trait's scope provider for the specified test or test case.
///
/// - Parameters:
/// - test: The test for which the testing library requests a
/// scope provider.
/// - testCase: The test case for which the testing library requests a scope
/// provider, if any. When `test` represents a suite, the value of this argument is
/// `nil`.
///
/// The testing library uses this implementation of
/// ``Trait/scopeProvider(for:testCase:)-cjmg`` when the trait type's
/// associated ``Trait/TestScopeProvider`` type is `Never`.
public func scopeProvider(for test: Test, testCase: Test.Case?) -> Never? {
nil
}
}
extension SuiteTrait {
public var isRecursive: Bool {
false
}
}
|