Support generic test function
Kyle-Ye opened this issue · comments
Description
We have some generic interface in the codebase. And I'd request generic function support for @Test
macro.
The following is a simplified version of my use case.
protocol Key {
associated Value
static var defaultValue: Value
static func reduce(value: inout Value, nextValue: () -> Value)
}
struct AddIntKey: Key {
static var defaultValue: Int { 0 }
static func reduce(value: inout Int, nextValue: () -> Int) {
value += nextValue()
}
}
struct MultipleIntKey: Key {
static var defaultValue: Int { 0 }
static func reduce(value: inout Int, nextValue: () -> Int) {
value *= nextValue()
}
}
Expected behavior
@Test(arguments: [ // ✅
(AddIntKey.self, [1, 2], 3)
(MultipleIntKey.self, [1, 2], 2)
])
func reduce<Key: PreferenceKey>(
type: Key.Type, values: [Key.Value], expectedValues: Key.Value
) {
...
}
Actual behavior
@Test(arguments: [ // ❌: The @Test attribute cannot be applied to a generic function.
(AddIntKey.self, [1, 2], 3)
(MultipleIntKey.self, [1, 2], 2)
])
func reduce<Key: PreferenceKey>(
type: Key.Type, values: [Key.Value], expectedValues: Key.Value
) {
...
}
Steps to reproduce
No response
swift-testing version/commit hash
No response
Swift & OS version (output of swift --version && uname -a
)
No response
The expression:
[
(AddIntKey.self, [1, 2], 3)
(MultipleIntKey.self, [1, 2], 2)
]
Would have the type [Any]
rather than something like [some PreferenceKey]
as you presumably intended. The types of a test's parameters must be explicitly specified in the test function's declaration because during macro expansion, we can only rely on the syntax (the specific characters the developer typed and saved into the source file) and do not have semantic type information for the test function's arguments.
Even if we assume for a moment that the syntax above were valid anyway, swift-testing discovers tests at runtime. A generic function may have an unbounded number of specializations at runtime, and the Swift runtime would require swift-testing to know how to specialize the test function before it could be called.
If you really wanted to do something like this, consider an abstraction over a common implementation function instead of using parameterization:
func commonReduce<Key: PreferenceKey>(
type: Key.Type,
values: [Key.Value],
expectedValues: Key.Value
) {
...
}
@Test func reduceByAdding(values: [Int], expectedValues: Int) {
commonReduce(AddIntKey.self, [1, 2], 3)
}
@Test func reduceByMultiplying(values: [Int], expectedValues: Int) {
commonReduce(MultiplyIntKey.self, [1, 2], 2)
}