File: Test.Case.Generator.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 (266 lines) | stat: -rw-r--r-- 10,681 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
//
// 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
//

extension Test.Case {
  /// A type describing a lazily-generated sequence of test cases generated from
  /// a known collection of argument values.
  ///
  /// Instances of this type can be iterated over multiple times.
  ///
  /// @Comment {
  ///   - Bug: The testing library should support variadic generics.
  ///     ([103416861](rdar://103416861))
  /// }
  struct Generator<S>: Sendable where S: Sequence & Sendable, S.Element: Sendable {
    /// The underlying sequence of argument values.
    ///
    /// The sequence _must_ be iterable multiple times. Hence, initializers
    /// accept only _collections_, not sequences. The constraint here is only to
    /// `Sequence` to allow the storage of computed sequences over collections
    /// (such as `CartesianProduct` or `Zip2Sequence`) that are safe to iterate
    /// multiple times.
    private var _sequence: S

    /// A closure that maps an element from `_sequence` to a test case instance.
    ///
    /// - Parameters:
    ///   - element: The element from `_sequence`.
    ///
    /// - Returns: A test case instance that tests `element`.
    private var _mapElement: @Sendable (_ element: S.Element) -> Test.Case

    /// Initialize an instance of this type.
    ///
    /// - Parameters:
    ///   - sequence: The sequence of argument values for which test cases
    ///     should be generated.
    ///   - mapElement: A function that maps each element in `sequence` to a
    ///     corresponding instance of ``Test/Case``.
    private init(
      sequence: S,
      mapElement: @escaping @Sendable (_ element: S.Element) -> Test.Case
    ) {
      _sequence = sequence
      _mapElement = mapElement
    }

    /// Initialize an instance of this type that generates exactly one test
    /// case.
    ///
    /// - Parameters:
    ///   - testFunction: The test function called by the generated test case.
    init(
      testFunction: @escaping @Sendable () async throws -> Void
    ) where S == CollectionOfOne<Void> {
      // A beautiful hack to give us the right number of cases: iterate over a
      // collection containing a single Void value.
      self.init(sequence: CollectionOfOne(())) { _ in
        Test.Case(arguments: [], body: testFunction)
      }
    }

    /// Initialize an instance of this type that iterates over the specified
    /// collection of argument values.
    ///
    /// - Parameters:
    ///   - collection: The collection of argument values for which test cases
    ///     should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `collection`.
    ///
    /// This initializer is disfavored since it relies on `Mirror` to
    /// de-structure elements of tuples. Other initializers which are
    /// specialized to handle collections of tuple types more efficiently should
    /// be preferred.
    @_disfavoredOverload
    init(
      arguments collection: S,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable (S.Element) async throws -> Void
    ) where S: Collection {
      if parameters.count > 1 {
        self.init(sequence: collection) { element in
          let mirror = Mirror(reflecting: element)
          let values: [any Sendable] = if mirror.displayStyle == .tuple {
            mirror.children.map { unsafeBitCast($0.value, to: (any Sendable).self) }
          } else {
            [element]
          }

          return Test.Case(values: values, parameters: parameters) {
            try await testFunction(element)
          }
        }
      } else {
        self.init(sequence: collection) { element in
          Test.Case(values: [element], parameters: parameters) {
            try await testFunction(element)
          }
        }
      }
    }

    /// Initialize an instance of this type that iterates over the specified
    /// collections of argument values.
    ///
    /// - Parameters:
    ///   - collection1: The first collection of argument values for which test
    ///     cases should be generated.
    ///   - collection2: The second collection of argument values for which test
    ///     cases should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `collection`.
    init<C1, C2>(
      arguments collection1: C1, _ collection2: C2,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable (C1.Element, C2.Element) async throws -> Void
    ) where S == CartesianProduct<C1, C2> {
      self.init(sequence: cartesianProduct(collection1, collection2)) { element in
        Test.Case(values: [element.0, element.1], parameters: parameters) {
          try await testFunction(element.0, element.1)
        }
      }
    }

    /// Initialize an instance of this type that iterates over the specified
    /// sequence of 2-tuple argument values.
    ///
    /// - Parameters:
    ///   - sequence: The sequence of 2-tuple argument values for which test
    ///     cases should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `sequence`.
    ///
    /// This initializer overload is specialized for sequences of 2-tuples to
    /// efficiently de-structure their elements when appropriate.
    ///
    /// @Comment {
    ///   - Bug: The testing library should support variadic generics.
    ///     ([103416861](rdar://103416861))
    /// }
    private init<E1, E2>(
      sequence: S,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable ((E1, E2)) async throws -> Void
    ) where S.Element == (E1, E2), E1: Sendable, E2: Sendable {
      if parameters.count > 1 {
        self.init(sequence: sequence) { element in
          Test.Case(values: [element.0, element.1], parameters: parameters) {
            try await testFunction(element)
          }
        }
      } else {
        self.init(sequence: sequence) { element in
          Test.Case(values: [element], parameters: parameters) {
            try await testFunction(element)
          }
        }
      }
    }

    /// Initialize an instance of this type that iterates over the specified
    /// collection of 2-tuple argument values.
    ///
    /// - Parameters:
    ///   - collection: The collection of 2-tuple argument values for which test
    ///     cases should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `collection`.
    ///
    /// This initializer overload is specialized for collections of 2-tuples to
    /// efficiently de-structure their elements when appropriate.
    ///
    /// @Comment {
    ///   - Bug: The testing library should support variadic generics.
    ///     ([103416861](rdar://103416861))
    /// }
    init<E1, E2>(
      arguments collection: S,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable ((E1, E2)) async throws -> Void
    ) where S: Collection, S.Element == (E1, E2) {
      self.init(sequence: collection, parameters: parameters, testFunction: testFunction)
    }

    /// Initialize an instance of this type that iterates over the specified
    /// zipped sequence of argument values.
    ///
    /// - Parameters:
    ///   - zippedCollections: A zipped sequence of argument values for which
    ///     test cases should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `zippedCollections`.
    init<C1, C2>(
      arguments zippedCollections: Zip2Sequence<C1, C2>,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable ((C1.Element, C2.Element)) async throws -> Void
    ) where S == Zip2Sequence<C1, C2>, C1: Collection, C2: Collection {
      self.init(sequence: zippedCollections, parameters: parameters, testFunction: testFunction)
    }

    /// Initialize an instance of this type that iterates over the specified
    /// dictionary of argument values.
    ///
    /// - Parameters:
    ///   - dictionary: A dictionary of argument values for which test cases
    ///     should be generated.
    ///   - parameters: The parameters of the test function for which test cases
    ///     should be generated.
    ///   - testFunction: The test function to which each generated test case
    ///     passes an argument value from `dictionary`.
    ///
    /// This initializer overload is specialized for dictionary collections, to
    /// efficiently de-structure their elements (which are known to be 2-tuples)
    /// when appropriate. This overload is distinct from those for other
    /// collections of 2-tuples because the `Element` tuple type for
    /// `Dictionary` includes labels (`(key: Key, value: Value)`).
    init<Key, Value>(
      arguments dictionary: Dictionary<Key, Value>,
      parameters: [Test.Parameter],
      testFunction: @escaping @Sendable ((Key, Value)) async throws -> Void
    ) where S == Dictionary<Key, Value> {
      if parameters.count > 1 {
        self.init(sequence: dictionary) { element in
          Test.Case(values: [element.key, element.value], parameters: parameters) {
            try await testFunction(element)
          }
        }
      } else {
        self.init(sequence: dictionary) { element in
          Test.Case(values: [element], parameters: parameters) {
            try await testFunction(element)
          }
        }
      }
    }
  }
}

// MARK: - Sequence

extension Test.Case.Generator: Sequence {
  func makeIterator() -> some IteratorProtocol<Test.Case> {
    _sequence.lazy.map(_mapElement).makeIterator()
  }

  var underestimatedCount: Int {
    _sequence.underestimatedCount
  }
}