diff --git a/Sources/Testing/Parameterization/Test.Case.Generator.swift b/Sources/Testing/Parameterization/Test.Case.Generator.swift index c682be19a..4ed4534d5 100644 --- a/Sources/Testing/Parameterization/Test.Case.Generator.swift +++ b/Sources/Testing/Parameterization/Test.Case.Generator.swift @@ -226,24 +226,24 @@ extension Test.Case { /// - 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( - arguments dictionary: Dictionary, + /// This initializer overload is specialized for dictionary-like 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 these + /// kinds of collections includes labels (`(key: Key, value: Value)`). + init( + arguments collection: S, parameters: [Test.Parameter], - testFunction: @escaping @Sendable ((Key, Value)) async throws -> Void - ) where S == Dictionary { + testFunction: @escaping @Sendable ((S.Key, S.Value)) async throws -> Void + ) where S: __DictionaryLikeCollection { if parameters.count > 1 { - self.init(sequence: dictionary) { element in + self.init(sequence: collection) { element in Test.Case(values: [element.key, element.value], parameters: parameters) { try await testFunction(element) } } } else { - self.init(sequence: dictionary) { element in + self.init(sequence: collection) { element in Test.Case(values: [element], parameters: parameters) { try await testFunction(element) } diff --git a/Sources/Testing/Test+Macro.swift b/Sources/Testing/Test+Macro.swift index 97fe104de..d89b21bfc 100644 --- a/Sources/Testing/Test+Macro.swift +++ b/Sources/Testing/Test+Macro.swift @@ -437,25 +437,25 @@ extension Test { /// Create an instance of ``Test`` for a parameterized function. /// - /// 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)`). + /// This initializer overload is specialized for dictionary-like 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 these kinds + /// of collections includes labels (`(key: Key, value: Value)`). /// /// - Warning: This function is used to implement the `@Test` macro. Do not /// call it directly. - public static func __function( + public static func __function( named testFunctionName: String, in containingType: S.Type?, xcTestCompatibleSelector: __XCTestCompatibleSelector?, displayName: String? = nil, traits: [any TestTrait], - arguments dictionary: @escaping @Sendable () async throws -> Dictionary, + arguments dictionary: @escaping @Sendable () async throws -> C, sourceBounds: __SourceBounds, parameters paramTuples: [__Parameter], - testFunction: @escaping @Sendable ((Key, Value)) async throws -> Void - ) -> Self where S: ~Copyable & ~Escapable, Key: Sendable, Value: Sendable { + testFunction: @escaping @Sendable (C.Element) async throws -> Void + ) -> Self where S: ~Copyable & ~Escapable, C: __DictionaryLikeCollection & Sendable, C.Key: Sendable, C.Value: Sendable { let containingTypeInfo: TypeInfo? = if let containingType { TypeInfo(describing: containingType) } else { @@ -529,6 +529,21 @@ extension Test { warning message: _const String ) = #externalMacro(module: "TestingMacros", type: "PragmaMacro") +// MARK: - Dictionary-like collection disambiguation + +/// A protocol that describes the basic shape of a "dictionary-like" collection +/// whose elements are key-value pairs. +/// +/// - Warning: This protocol is used to implement the `@Test` macro. Do not use +/// it directly. +public protocol __DictionaryLikeCollection: Collection where Element == (key: Key, value: Value) { + associatedtype Key + associatedtype Value +} + +extension Dictionary: __DictionaryLikeCollection {} +extension KeyValuePairs: __DictionaryLikeCollection {} + // MARK: - Helper functions /// A function that abstracts away whether or not the `try` keyword is needed on