swiftlang / swift-testing

A modern, expressive testing package for Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support generic test function

Kyle-Ye opened this issue · comments

commented

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)
}