File: Trait.swift

package info (click to toggle)
swiftlang 6.1.3-4
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,791,644 kB
  • sloc: cpp: 9,901,738; ansic: 2,201,433; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (248 lines) | stat: -rw-r--r-- 11,250 bytes parent folder | download
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
  }
}