File: TestingAdditions.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (368 lines) | stat: -rw-r--r-- 14,848 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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
//
// 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
//

@testable @_spi(ForToolsIntegrationOnly) import Testing
#if canImport(XCTest)
import XCTest
#endif

extension Tag {
  /// A tag indicating that a test is related to a trait.
  @Tag static var traitRelated: Self

  /// Convenience initializer/shorthand for tags.
  ///
  /// This initializer is equivalent to ``Tag/init(userProvidedStringValue:)``
  /// but is shorter to help keep tests readable.
  init(_ stringValue: _const String) {
    self.init(userProvidedStringValue: stringValue)
  }
}

/// Get the ``Test`` instance representing a type, if one is found in the
/// current process.
///
/// - Parameters:
///   - containingType: The type for which a ``Test`` instance is needed.
///
/// - Returns: The test instance representing the specified type, or `nil` if
///   none is found.
func test(for containingType: Any.Type) async -> Test? {
  let containingTypeInfo = TypeInfo(describing: containingType)
  return await Test.all.first {
    $0.isSuite && $0.containingTypeInfo == containingTypeInfo
  }
}

/// Get the ``Test`` instance representing the test function named `name`
/// defined within a suite type, if one is found in the current process.
///
/// - Parameters:
///   - name: The name of the test function to search for.
///
/// - Returns: The test instance representing the specified test function, or
///   `nil` if none is found.
func testFunction(named name: String, in containingType: Any.Type) async -> Test? {
  let containingTypeInfo = TypeInfo(describing: containingType)
  return await Test.all.first {
    $0.name == name && !$0.isSuite && $0.containingTypeInfo == containingTypeInfo
  }
}

/// Run the ``Test`` instance representing a suite type, if one is found in the
/// current process.
///
/// - Parameters:
///   - containingType: The type containing the tests that should be run.
///   - configuration: The configuration to use for running.
///
/// Any tests defined within `containingType` are also run. If no test is found
/// representing that type, nothing is run.
func runTest(for containingType: Any.Type, configuration: Configuration = .init()) async {
  let plan = await Runner.Plan(selecting: containingType, configuration: configuration)
  let runner = Runner(plan: plan, configuration: configuration)
  await runner.run()
}

/// Run the ``Test`` instance representing the test function named `name`
/// defined within a suite type, if one is found in the current process.
///
/// - Parameters:
///   - name: The name of the test function to run.
///   - containingType: The type under which the test function should be found.
///   - configuration: The configuration to use for running.
///
/// If no test is found representing `containingType`, nothing is run.
func runTestFunction(named name: String, in containingType: Any.Type, configuration: Configuration = .init()) async {
  var configuration = configuration
  let selection = [Test.ID(type: containingType).child(named: name)]
  configuration.setTestFilter(toInclude: selection, includeHiddenTests: true)

  let runner = await Runner(configuration: configuration)
  await runner.run()
}

extension Runner {
  /// Initialize an instance of this type that runs the free test function
  /// named `testName` in the module specified in `fileID`.
  ///
  /// - Parameters:
  ///   - testName: The name of the test function this instance should run.
  ///   - fileID: The `#fileID` string whose module should be used to locate
  ///     the test function to run.
  ///   - configuration: The configuration to use for running.
  init(
    selecting testName: String,
    inModuleOf fileID: String = #fileID,
    configuration: Configuration = .init()
  ) async {
    let moduleName = String(fileID[..<fileID.lastIndex(of: "/")!])

    var configuration = configuration
    let selection = [Test.ID(moduleName: moduleName, nameComponents: [testName], sourceLocation: nil)]
    configuration.setTestFilter(toInclude: selection, includeHiddenTests: true)

    await self.init(configuration: configuration)
  }
}

extension Runner.Plan {
  /// Initialize an instance of this type with the specified suite type.
  ///
  /// - Parameters:
  ///   - containingType: The suite type this plan should select.
  ///   - configuration: The configuration to use for planning.
  init(selecting containingType: Any.Type, configuration: Configuration = .init()) async {
    var configuration = configuration
    let selection = [Test.ID(type: containingType)]
    configuration.setTestFilter(toInclude: selection, includeHiddenTests: true)

    await self.init(configuration: configuration)
  }
}

extension Test {
  /// Initialize an instance of this type with a function or closure to call.
  ///
  /// - Parameters:
  ///   - traits: Zero or more traits to apply to this test.
  ///   - testFunction: The function to call when running this test.
  ///
  /// Use this initializer to construct an instance of ``Test`` without using
  /// the `@Test` macro.
  init(
    _ traits: any TestTrait...,
    sourceLocation: SourceLocation = #_sourceLocation,
    name: String = #function,
    testFunction: @escaping @Sendable () async throws -> Void
  ) {
    let caseGenerator = Case.Generator(testFunction: testFunction)
    self.init(name: name, displayName: name, traits: traits, sourceLocation: sourceLocation, containingTypeInfo: nil, testCases: caseGenerator, parameters: [])
  }

  /// Initialize an instance of this type with a function or closure to call,
  /// parameterized over a collection of values.
  ///
  /// - Parameters:
  ///   - traits: Zero or more traits to apply to this test.
  ///   - collection: A collection of values to pass to `testFunction`.
  ///   - parameters: The parameters of this instance's test function.
  ///   - testFunction: The function to call when running this test. During
  ///     testing, this function is called once for each element in
  ///     `collection`.
  ///
  /// @Comment {
  ///   - Bug: The testing library should support variadic generics.
  ///     ([103416861](rdar://103416861))
  /// }
  init<C>(
    _ traits: any TestTrait...,
    arguments collection: C,
    parameters: [Parameter] = [],
    sourceLocation: SourceLocation = #_sourceLocation,
    column: Int = #column,
    name: String = #function,
    testFunction: @escaping @Sendable (C.Element) async throws -> Void
  ) where C: Collection & Sendable, C.Element: Sendable {
    let caseGenerator = Case.Generator(arguments: collection, parameters: parameters, testFunction: testFunction)
    self.init(name: name, displayName: name, traits: traits, sourceLocation: sourceLocation, containingTypeInfo: nil, testCases: caseGenerator, parameters: parameters)
  }

  /// Initialize an instance of this type with a function or closure to call,
  /// parameterized over two collections of values.
  ///
  /// - Parameters:
  ///   - traits: Zero or more traits to apply to this test.
  ///   - collection1: A collection of values to pass to `testFunction`.
  ///   - collection2: A second collection of values to pass to `testFunction`.
  ///   - parameters: The parameters of this instance's test function.
  ///   - testFunction: The function to call when running this test. During
  ///     testing, this function is called once for each pair of elements in
  ///     `collection1` and `collection2`.
  ///
  /// @Comment {
  ///   - Bug: The testing library should support variadic generics.
  ///     ([103416861](rdar://103416861))
  /// }
  init<C1, C2>(
    _ traits: any TestTrait...,
    arguments collection1: C1, _ collection2: C2,
    parameters: [Parameter] = [],
    sourceLocation: SourceLocation = #_sourceLocation,
    name: String = #function,
    testFunction: @escaping @Sendable (C1.Element, C2.Element) async throws -> Void
  ) where C1: Collection & Sendable, C1.Element: Sendable, C2: Collection & Sendable, C2.Element: Sendable {
    let caseGenerator = Case.Generator(arguments: collection1, collection2, parameters: parameters, testFunction: testFunction)
    self.init(name: name, displayName: name, traits: traits, sourceLocation: sourceLocation, containingTypeInfo: nil, testCases: caseGenerator, parameters: parameters)
  }

  /// Initialize an instance of this type with a function or closure to call,
  /// parameterized over a zipped sequence of argument values.
  ///
  /// - Parameters:
  ///   - traits: Zero or more traits to apply to this test.
  ///   - zippedCollections: A zipped sequence of argument values to pass to
  ///     `testFunction`.
  ///   - parameters: The parameters of this instance's test function.
  ///   - testFunction: The function to call when running this test. During
  ///     testing, this function is called once for each pair of elements in
  ///     `zippedCollections`.
  init<C1, C2>(
    _ traits: any TestTrait...,
    arguments zippedCollections: Zip2Sequence<C1, C2>,
    parameters: [Parameter] = [],
    sourceLocation: SourceLocation = #_sourceLocation,
    name: String = #function,
    testFunction: @escaping @Sendable ((C1.Element, C2.Element)) async throws -> Void
  ) where C1: Collection & Sendable, C1.Element: Sendable, C2: Collection & Sendable, C2.Element: Sendable {
    let caseGenerator = Case.Generator(arguments: zippedCollections, parameters: parameters, testFunction: testFunction)
    self.init(name: name, displayName: name, traits: traits, sourceLocation: sourceLocation, containingTypeInfo: nil, testCases: caseGenerator, parameters: parameters)
  }
}

extension Test {
  /// Run a single test in isolation.
  ///
  /// - Parameters:
  ///   - configuration: The configuration to apply when running this test.
  ///
  /// This function constructs an instance of ``Runner`` to run this test, then
  /// runs it. It is provided as a convenience for use in the testing library's
  /// own test suite; when writing tests for other test suites, it should not be
  /// necessary to call this function.
  func run(configuration: Configuration = .init()) async {
    let runner = await Runner(testing: [self], configuration: configuration)
    await runner.run()
  }
}

extension Test.ID {
  /// Whether or not this instance can be used to create child test IDs.
  ///
  /// A test ID can have children unless it represents a test function (as
  /// indicated by the presence of a value for its ``sourceLocation``
  /// property.)
  ///
  /// ## See Also
  ///
  /// - ``child(named:)``
  var canHaveChildren: Bool {
    sourceLocation == nil
  }

  /// Make an ID for a test that is a child of this instance.
  ///
  /// - Parameters:
  ///   - name: The name of the child test, relative to this instance's
  ///     corresponding test. For example, if `self` represents a test named
  ///     `"A.B.C"`, and the fully-qualified name of the child is `"A.B.C.D"`,
  ///     pass `"D"`.
  ///
  /// - Returns: An instance of this type representing the specified child test.
  ///
  /// - Precondition: The value of this instance's ``canHaveChildren`` property
  ///   must be `true`.
  func child(named name: String) -> Self {
    precondition(canHaveChildren, "A child test cannot be added to this test ID because it represents a function.")

    var result = self
    result.nameComponents.append(name)
    return result
  }
}

extension Test.ID.Selection {
  /// Initialize an instance of this type with test IDs created from the
  /// specified collection of fully-qualified name components.
  ///
  /// - Parameters:
  ///   - testIDs: The collection of fully-qualified name components from test
  ///     IDs to include in the selection.
  init(testIDs: some Collection<[String]>) {
    self.init(testIDs: testIDs.lazy.map(Test.ID.init(_:)))
  }
}

extension Configuration {
  /// Set an event handler which automatically handles thrown errors.
  ///
  /// - Parameters:
  ///   - eventHandler: The throwing ``Event/Handler`` to set.
  ///
  /// Errors thrown by `eventHandler` are caught and recorded as an ``Issue``.
  ///
  /// This is meant for testing the testing library itself. In production tests,
  /// event handlers should not typically need to be throwing, but if they do,
  /// the event handler should implement its own error-handling logic since
  /// recording an ``Issue`` would be inappropriate.
  mutating func setEventHandler(_ eventHandler: @escaping @Sendable (_ event: borrowing Event, _ context: borrowing Event.Context) throws -> Void) {
    self.eventHandler = { event, context in
      do {
        try eventHandler(event, context)
      } catch {
        Issue.record(error)
      }
    }
  }

  /// Set the test filter of this instance to include the given test ID
  /// selection.
  ///
  /// - Parameters:
  ///   - selection: The test IDs to include.
  ///   - includeHiddenTests: Whether or not to include tests with the `.hidden`
  ///     trait.
  mutating func setTestFilter(toInclude selection: some Collection<Test.ID>, includeHiddenTests: Bool) {
    var testFilter = TestFilter(including: selection)
    testFilter.includeHiddenTests = includeHiddenTests
    self.testFilter = testFilter
  }
}

/// Whether or not to enable "noisy" tests that produce a lot of output.
///
/// This flag can be used with `.enabled(if:)` to disable noisy tests unless the
/// developer specifies an environment variable when testing. Use it with tests
/// whose output could make it hard to read "real" output from the testing
/// library.
let testsWithSignificantIOAreEnabled = Environment.flag(named: "SWT_ENABLE_TESTS_WITH_SIGNIFICANT_IO") == true

extension JSON {
  /// Round-trip a value through JSON encoding/decoding.
  ///
  /// - Parameters:
  ///   - value: The value to round-trip.
  ///
  /// - Returns: A copy of `value` after encoding and decoding.
  ///
  /// - Throws: Any error encountered encoding or decoding `value`.
  static func encodeAndDecode<T>(_ value: T) throws -> T where T: Codable {
    try JSON.withEncoding(of: value) { data in
      try JSON.decode(T.self, from: data)
    }
  }
}

@available(_clockAPI, *)
extension Trait where Self == TimeLimitTrait {
  /// Construct a time limit trait that causes a test to time out if it runs for
  /// too long.
  ///
  /// - Parameters:
  ///   - timeLimit: The maximum amount of time the test may run for.
  ///
  /// - Returns: An instance of ``TimeLimitTrait``.
  ///
  /// This function is meant for use only in testing ``TimeLimitTrait`` itself,
  /// and accepts any arbitrary Swift `Duration` value.
  static func timeLimit(_ timeLimit: Swift.Duration) -> Self {
    return Self(timeLimit: timeLimit)
  }
}