Skip to content

Commit 610b2bc

Browse files
authored
Merge pull request #1141 from stmontgomery/main-6.2-merge
Merge 'main' branch to 'release/6.2'
2 parents 9d38c66 + 7a9d9e7 commit 610b2bc

15 files changed

+244
-60
lines changed

Package.swift

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,23 @@ let package = Package(
127127
"Testing",
128128
"_Testing_CoreGraphics",
129129
"_Testing_Foundation",
130+
"MemorySafeTestingTests",
130131
],
131-
swiftSettings: .packageSettings
132+
swiftSettings: .packageSettings + .disableMandatoryOptimizationsSettings
133+
),
134+
135+
// Use a plain `.target` instead of a `.testTarget` to avoid the unnecessary
136+
// overhead of having a separate test target for this module. Conceptually,
137+
// the content in this module is no different than content which would
138+
// typically be placed in the `TestingTests` target, except this content
139+
// needs the (module-wide) strict memory safety feature to be enabled.
140+
.target(
141+
name: "MemorySafeTestingTests",
142+
dependencies: [
143+
"Testing",
144+
],
145+
path: "Tests/_MemorySafeTestingTests",
146+
swiftSettings: .packageSettings + .strictMemorySafety
132147
),
133148

134149
.macro(
@@ -219,7 +234,7 @@ package.targets.append(contentsOf: [
219234
"Testing",
220235
"TestingMacros",
221236
],
222-
swiftSettings: .packageSettings
237+
swiftSettings: .packageSettings + .disableMandatoryOptimizationsSettings
223238
)
224239
])
225240
#endif
@@ -275,7 +290,10 @@ extension Array where Element == PackageDescription.SwiftSetting {
275290
// This setting is enabled in the package, but not in the toolchain build
276291
// (via CMake). Enabling it is dependent on acceptance of the @section
277292
// proposal via Swift Evolution.
278-
.enableExperimentalFeature("SymbolLinkageMarkers"),
293+
//
294+
// FIXME: Re-enable this once a CI blocker is resolved:
295+
// https://github.com/swiftlang/swift-testing/issues/1138.
296+
// .enableExperimentalFeature("SymbolLinkageMarkers"),
279297

280298
// This setting is no longer needed when building with a 6.2 or later
281299
// toolchain now that SE-0458 has been accepted and implemented, but it is
@@ -355,6 +373,32 @@ extension Array where Element == PackageDescription.SwiftSetting {
355373

356374
return result
357375
}
376+
377+
/// Settings necessary to enable Strict Memory Safety, introduced in
378+
/// [SE-0458: Opt-in Strict Memory Safety Checking](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0458-strict-memory-safety.md#swiftpm-integration).
379+
static var strictMemorySafety: Self {
380+
#if compiler(>=6.2)
381+
// FIXME: Adopt official `.strictMemorySafety()` condition once the minimum
382+
// supported toolchain is 6.2.
383+
[.unsafeFlags(["-strict-memory-safety"])]
384+
#else
385+
[]
386+
#endif
387+
}
388+
389+
/// Settings which disable Swift's mandatory optimizations pass.
390+
///
391+
/// This is intended only to work around a build failure caused by a Swift
392+
/// compiler regression which is expected to be resolved in
393+
/// [swiftlang/swift#82034](https://github.com/swiftlang/swift/pull/82034).
394+
///
395+
/// @Comment {
396+
/// - Bug: This should be removed once the CI issue is resolved.
397+
/// [swiftlang/swift-testin#1138](https://github.com/swiftlang/swift-testing/issues/1138).
398+
/// }
399+
static var disableMandatoryOptimizationsSettings: Self {
400+
[.unsafeFlags(["-Xllvm", "-sil-disable-pass=mandatory-performance-optimizations"])]
401+
}
358402
}
359403

360404
extension Array where Element == PackageDescription.CXXSetting {

Sources/Testing/ExitTests/ExitTest.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ extension ExitTest {
327327
///
328328
/// - Warning: This function is used to implement the
329329
/// `#expect(processExitsWith:)` macro. Do not use it directly.
330+
#if compiler(>=6.2)
331+
@safe
332+
#endif
330333
public static func __store<each T>(
331334
_ id: (UInt64, UInt64, UInt64, UInt64),
332335
_ body: @escaping @Sendable (repeat each T) async throws -> Void,

Sources/Testing/Test+Discovery.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ extension Test {
3939
///
4040
/// - Warning: This function is used to implement the `@Test` macro. Do not
4141
/// use it directly.
42+
#if compiler(>=6.2)
43+
@safe
44+
#endif
4245
public static func __store(
4346
_ generator: @escaping @Sendable () async -> Test,
4447
into outValue: UnsafeMutableRawPointer,

Sources/Testing/Test+Macro.swift

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,10 @@ public macro Test<C>(
217217
/// - collection: A collection of values to pass to the associated test
218218
/// function.
219219
///
220-
/// During testing, the associated test function is called once for each element
221-
/// in `collection`.
220+
/// You can prefix the expression you pass to `collection` with `try` or `await`.
221+
/// The testing library evaluates the expression lazily only if it determines
222+
/// that the associated test will run. During testing, the testing library calls
223+
/// the associated test function once for each element in `collection`.
222224
///
223225
/// @Comment {
224226
/// - Bug: The testing library should support variadic generics.
@@ -270,7 +272,10 @@ extension Test {
270272
/// - collection1: A collection of values to pass to `testFunction`.
271273
/// - collection2: A second collection of values to pass to `testFunction`.
272274
///
273-
/// During testing, the associated test function is called once for each pair of
275+
/// You can prefix the expressions you pass to `collection1` or `collection2`
276+
/// with `try` or `await`. The testing library evaluates the expressions lazily
277+
/// only if it determines that the associated test will run. During testing, the
278+
/// testing library calls the associated test function once for each pair of
274279
/// elements in `collection1` and `collection2`.
275280
///
276281
/// @Comment {
@@ -298,7 +303,10 @@ public macro Test<C1, C2>(
298303
/// - collection1: A collection of values to pass to `testFunction`.
299304
/// - collection2: A second collection of values to pass to `testFunction`.
300305
///
301-
/// During testing, the associated test function is called once for each pair of
306+
/// You can prefix the expressions you pass to `collection1` or `collection2`
307+
/// with `try` or `await`. The testing library evaluates the expressions lazily
308+
/// only if it determines that the associated test will run. During testing, the
309+
/// testing library calls the associated test function once for each pair of
302310
/// elements in `collection1` and `collection2`.
303311
///
304312
/// @Comment {
@@ -324,8 +332,11 @@ public macro Test<C1, C2>(
324332
/// - zippedCollections: Two zipped collections of values to pass to
325333
/// `testFunction`.
326334
///
327-
/// During testing, the associated test function is called once for each element
328-
/// in `zippedCollections`.
335+
/// You can prefix the expression you pass to `zippedCollections` with `try` or
336+
/// `await`. The testing library evaluates the expression lazily only if it
337+
/// determines that the associated test will run. During testing, the testing
338+
/// library calls the associated test function once for each element in
339+
/// `zippedCollections`.
329340
///
330341
/// @Comment {
331342
/// - Bug: The testing library should support variadic generics.
@@ -352,8 +363,11 @@ public macro Test<C1, C2>(
352363
/// - zippedCollections: Two zipped collections of values to pass to
353364
/// `testFunction`.
354365
///
355-
/// During testing, the associated test function is called once for each element
356-
/// in `zippedCollections`.
366+
/// You can prefix the expression you pass to `zippedCollections` with `try` or
367+
/// `await`. The testing library evaluates the expression lazily only if it
368+
/// determines that the associated test will run. During testing, the testing
369+
/// library calls the associated test function once for each element in
370+
/// `zippedCollections`.
357371
///
358372
/// @Comment {
359373
/// - Bug: The testing library should support variadic generics.

Sources/Testing/Testing.docc/ParameterizedTesting.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,37 @@ func makeLargeOrder(count: Int) async throws {
101101
- Note: Very large ranges such as `0 ..< .max` may take an excessive amount of
102102
time to test, or may never complete due to resource constraints.
103103

104+
### Pass the same arguments to multiple test functions
105+
106+
If you want to pass the same collection of arguments to two or more
107+
parameterized test functions, you can extract the arguments to a separate
108+
function or property and pass it to each `@Test` attribute. For example:
109+
110+
```swift
111+
extension Food {
112+
static var bestSelling: [Food] {
113+
get async throws { /* ... */ }
114+
}
115+
}
116+
117+
@Test(arguments: try await Food.bestSelling)
118+
func `Order entree`(food: Food) {
119+
let foodTruck = FoodTruck()
120+
#expect(foodTruck.order(food))
121+
}
122+
123+
@Test(arguments: try await Food.bestSelling)
124+
func `Package leftovers`(food: Food) throws {
125+
let foodTruck = FoodTruck()
126+
let container = try #require(foodTruck.container(fitting: food))
127+
try container.add(food)
128+
}
129+
```
130+
131+
> Tip: You can prefix expressions passed to `arguments:` with `try` or `await`.
132+
> The testing library evaluates them lazily only if it determines that the
133+
> associated test will run.
134+
104135
### Test with more than one collection
105136

106137
It's possible to test more than one collection. Consider the following test

Sources/Testing/Testing.docc/Traits.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ types that customize the behavior of your tests.
5151
<!--
5252
### Handling issues
5353
54-
- ``Trait/transformIssues(_:)``
54+
- ``Trait/compactMapIssues(_:)``
5555
- ``Trait/filterIssues(_:)``
5656
-->
5757

Sources/Testing/Traits/IssueHandlingTrait.swift

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
/// modifying one or more of its properties, and returning the copy. You can
1616
/// observe recorded issues by returning them unmodified. Or you can suppress an
1717
/// issue by either filtering it using ``Trait/filterIssues(_:)`` or returning
18-
/// `nil` from the closure passed to ``Trait/transformIssues(_:)``.
18+
/// `nil` from the closure passed to ``Trait/compactMapIssues(_:)``.
1919
///
2020
/// When an instance of this trait is applied to a suite, it is recursively
2121
/// inherited by all child suites and tests.
2222
///
2323
/// To add this trait to a test, use one of the following functions:
2424
///
25-
/// - ``Trait/transformIssues(_:)``
25+
/// - ``Trait/compactMapIssues(_:)``
2626
/// - ``Trait/filterIssues(_:)``
2727
@_spi(Experimental)
2828
public struct IssueHandlingTrait: TestTrait, SuiteTrait {
@@ -96,15 +96,26 @@ extension IssueHandlingTrait: TestScoping {
9696
return
9797
}
9898

99+
// Ignore system issues, as they are not expected to be caused by users.
100+
if case .system = issue.kind {
101+
oldConfiguration.eventHandler(event, context)
102+
return
103+
}
104+
99105
// Use the original configuration's event handler when invoking the
100-
// transformer to avoid infinite recursion if the transformer itself
106+
// handler closure to avoid infinite recursion if the handler itself
101107
// records new issues. This means only issue handling traits whose scope
102108
// is outside this one will be allowed to handle such issues.
103109
let newIssue = Configuration.withCurrent(oldConfiguration) {
104110
handleIssue(issue)
105111
}
106112

107113
if let newIssue {
114+
// Prohibit assigning the issue's kind to system.
115+
if case .system = newIssue.kind {
116+
preconditionFailure("Issue returned by issue handling closure cannot have kind 'system': \(newIssue)")
117+
}
118+
108119
var event = event
109120
event.kind = .issueRecorded(newIssue)
110121
oldConfiguration.eventHandler(event, context)
@@ -120,31 +131,35 @@ extension Trait where Self == IssueHandlingTrait {
120131
/// Constructs an trait that transforms issues recorded by a test.
121132
///
122133
/// - Parameters:
123-
/// - transformer: The closure called for each issue recorded by the test
134+
/// - transform: A closure called for each issue recorded by the test
124135
/// this trait is applied to. It is passed a recorded issue, and returns
125136
/// an optional issue to replace the passed-in one.
126137
///
127138
/// - Returns: An instance of ``IssueHandlingTrait`` that transforms issues.
128139
///
129-
/// The `transformer` closure is called synchronously each time an issue is
140+
/// The `transform` closure is called synchronously each time an issue is
130141
/// recorded by the test this trait is applied to. The closure is passed the
131142
/// recorded issue, and if it returns a non-`nil` value, that will be recorded
132143
/// instead of the original. Otherwise, if the closure returns `nil`, the
133144
/// issue is suppressed and will not be included in the results.
134145
///
135-
/// The `transformer` closure may be called more than once if the test records
146+
/// The `transform` closure may be called more than once if the test records
136147
/// multiple issues. If more than one instance of this trait is applied to a
137-
/// test (including via inheritance from a containing suite), the `transformer`
148+
/// test (including via inheritance from a containing suite), the `transform`
138149
/// closure for each instance will be called in right-to-left, innermost-to-
139150
/// outermost order, unless `nil` is returned, which will skip invoking the
140151
/// remaining traits' closures.
141152
///
142-
/// Within `transformer`, you may access the current test or test case (if any)
153+
/// Within `transform`, you may access the current test or test case (if any)
143154
/// using ``Test/current`` ``Test/Case/current``, respectively. You may also
144155
/// record new issues, although they will only be handled by issue handling
145156
/// traits which precede this trait or were inherited from a containing suite.
146-
public static func transformIssues(_ transformer: @escaping @Sendable (Issue) -> Issue?) -> Self {
147-
Self(handler: transformer)
157+
///
158+
/// - Note: `transform` will never be passed an issue for which the value of
159+
/// ``Issue/kind`` is ``Issue/Kind/system``, and may not return such an
160+
/// issue.
161+
public static func compactMapIssues(_ transform: @escaping @Sendable (Issue) -> Issue?) -> Self {
162+
Self(handler: transform)
148163
}
149164

150165
/// Constructs a trait that filters issues recorded by a test.
@@ -174,6 +189,9 @@ extension Trait where Self == IssueHandlingTrait {
174189
/// using ``Test/current`` ``Test/Case/current``, respectively. You may also
175190
/// record new issues, although they will only be handled by issue handling
176191
/// traits which precede this trait or were inherited from a containing suite.
192+
///
193+
/// - Note: `isIncluded` will never be passed an issue for which the value of
194+
/// ``Issue/kind`` is ``Issue/Kind/system``.
177195
public static func filterIssues(_ isIncluded: @escaping @Sendable (Issue) -> Bool) -> Self {
178196
Self { issue in
179197
isIncluded(issue) ? issue : nil

Sources/TestingMacros/ConditionMacro.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,10 +496,11 @@ extension ExitTestConditionMacro {
496496
var recordDecl: DeclSyntax?
497497
#if !SWT_NO_LEGACY_TEST_DISCOVERY
498498
let legacyEnumName = context.makeUniqueName("__🟡$")
499+
let unsafeKeyword: TokenSyntax? = isUnsafeKeywordSupported ? .keyword(.unsafe, trailingTrivia: .space) : nil
499500
recordDecl = """
500501
enum \(legacyEnumName): Testing.__TestContentRecordContainer {
501502
nonisolated static var __testContentRecord: Testing.__TestContentRecord {
502-
\(enumName).testContentRecord
503+
\(unsafeKeyword)\(enumName).testContentRecord
503504
}
504505
}
505506
"""

Sources/TestingMacros/SuiteDeclarationMacro.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,13 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable {
169169
#if !SWT_NO_LEGACY_TEST_DISCOVERY
170170
// Emit a type that contains a reference to the test content record.
171171
let enumName = context.makeUniqueName("__🟡$")
172+
let unsafeKeyword: TokenSyntax? = isUnsafeKeywordSupported ? .keyword(.unsafe, trailingTrivia: .space) : nil
172173
result.append(
173174
"""
174175
@available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
175176
enum \(enumName): Testing.__TestContentRecordContainer {
176177
nonisolated static var __testContentRecord: Testing.__TestContentRecord {
177-
\(testContentRecordName)
178+
\(unsafeKeyword)\(testContentRecordName)
178179
}
179180
}
180181
"""

0 commit comments

Comments
 (0)