From 903437b59c126b7eb648088b58439612700d18bd Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 2 Aug 2024 15:24:40 -0400 Subject: [PATCH 1/2] Implement an overload of `confirmation()` that takes an unbounded range. Unbounded ranges are not meaningful when used with `confirmation()` because _any_ confirmation count matches. As well, the `...` operator produces an instance of `UnboundedRange` which is a non-nominal type and cannot conform to `RangeExpression`. It may be non-obvious to a developer why `...` doesn't work as the `expectedCount` argument to that function when other range operators work as expected. This PR implements a stub overload of `confirmation()` that takes an unbounded range. The stub overload is marked unavailable and cannot be called. --- Sources/Testing/Issues/Confirmation.swift | 20 +++++++++++++++++-- Sources/Testing/Support/Environment.swift | 2 +- .../Support/AvailabilityGuards.swift | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Sources/Testing/Issues/Confirmation.swift b/Sources/Testing/Issues/Confirmation.swift index 2ce3f1910..eed47c63e 100644 --- a/Sources/Testing/Issues/Confirmation.swift +++ b/Sources/Testing/Issues/Confirmation.swift @@ -9,12 +9,12 @@ // /// A type that can be used to confirm that an event occurs zero or more times. -public struct Confirmation: Sendable { +public final class Confirmation: Sendable { /// The number of times ``confirm(count:)`` has been called. /// /// This property is fileprivate because it may be mutated asynchronously and /// callers may be tempted to use it in ways that result in data races. - fileprivate var count = Locked(rawValue: 0) + fileprivate let count = Locked(rawValue: 0) /// Confirm this confirmation. /// @@ -179,6 +179,22 @@ public func confirmation( return try await body(confirmation) } +/// An overload of ``confirmation(_:expectedCount:sourceLocation:_:)-9bfdc`` +/// that handles the unbounded range operator (`...`). +/// +/// This overload is necessary because `UnboundedRange` does not conform to +/// `RangeExpression`. It effectively always succeeds because any number of +/// confirmations matches, so it is marked unavailable and is not implemented. +@available(*, unavailable, message: "Unbounded range '...' has no effect when used with a confirmation.") +public func confirmation( + _ comment: Comment? = nil, + expectedCount: UnboundedRange, + sourceLocation: SourceLocation = #_sourceLocation, + _ body: (Confirmation) async throws -> R +) async rethrows -> R { + fatalError("Unsupported") +} + @_spi(Experimental) extension Confirmation { /// A protocol that describes a range expression that can be used with diff --git a/Sources/Testing/Support/Environment.swift b/Sources/Testing/Support/Environment.swift index e4441cfd9..f94d662fe 100644 --- a/Sources/Testing/Support/Environment.swift +++ b/Sources/Testing/Support/Environment.swift @@ -188,7 +188,7 @@ enum Environment { return nil case let errorCode: let error = Win32Error(rawValue: errorCode) - fatalError("unexpected error when getting environment variable '\(name)': \(error) (\(errorCode))") + fatalError("Unexpected error when getting environment variable '\(name)': \(error) (\(errorCode))") } } else if count > buffer.count { // Try again with the larger count. diff --git a/Sources/TestingMacros/Support/AvailabilityGuards.swift b/Sources/TestingMacros/Support/AvailabilityGuards.swift index 1a6fa1f05..8a628094f 100644 --- a/Sources/TestingMacros/Support/AvailabilityGuards.swift +++ b/Sources/TestingMacros/Support/AvailabilityGuards.swift @@ -118,7 +118,7 @@ private func _createAvailabilityTraitExpr( return ".__unavailable(message: \(message), sourceLocation: \(sourceLocationExpr))" default: - fatalError("Unsupported keyword \(whenKeyword) passed to \(#function)") + fatalError("Unsupported keyword \(whenKeyword) passed to \(#function). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new") } } From b7f84e7a5ff51de573ab14a389f1d9e69d85b175 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 6 Aug 2024 11:01:35 -0400 Subject: [PATCH 2/2] Confirmation needs to stay a struct for now --- Sources/Testing/Issues/Confirmation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Testing/Issues/Confirmation.swift b/Sources/Testing/Issues/Confirmation.swift index eed47c63e..b1a1d23e6 100644 --- a/Sources/Testing/Issues/Confirmation.swift +++ b/Sources/Testing/Issues/Confirmation.swift @@ -9,12 +9,12 @@ // /// A type that can be used to confirm that an event occurs zero or more times. -public final class Confirmation: Sendable { +public struct Confirmation: Sendable { /// The number of times ``confirm(count:)`` has been called. /// /// This property is fileprivate because it may be mutated asynchronously and /// callers may be tempted to use it in ways that result in data races. - fileprivate let count = Locked(rawValue: 0) + fileprivate var count = Locked(rawValue: 0) /// Confirm this confirmation. ///