Skip to content

Commit

Permalink
fix: handle protocol constrained parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
swift-student committed Jan 27, 2025
1 parent 8fa32a5 commit 56922f5
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
19 changes: 17 additions & 2 deletions Sources/TestDRS/Spy/Blackbox/BlackBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,23 @@ public final class BlackBox: @unchecked Sendable {
returning outputType: Output.Type
) -> [FunctionCall<Input, Output>] {
storageQueue.sync {
storage.filter(signature: signature).compactMap {
$0 as? FunctionCall<Input, Output>
storage.filter(signature: signature).compactMap { call in
if let typedCall = call as? FunctionCall<Input, Output> {
return typedCall
}
if let input = call.input as? Input {
// If we have the input type now, but didn't when recording, we can just create new FunctionCall with expected type
// See ProtocolConstrainedNonGenericParameterTests
return FunctionCall(
signature: call.signature,
input: input,
outputType: outputType,
time: call.time,
id: call.id
)
}

return nil
}
}
}
Expand Down
52 changes: 52 additions & 0 deletions Tests/TestDRSTests/Integration/ParameterProtocolTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// Created on 1/27/25.
// Copyright © 2025 Turo Open Source. All rights reserved.
//

import TestDRS
import Testing

/// Tests for verifying behavior when the input to a method uses a parameter constrained to a protocol,
/// but the function itself is not generic over that parameter.
struct ProtocolConstrainedNonGenericParameterTests {

@Test
func testWithSingleProtocolConstrainedParameter() {
let myMock = MyMock()
let parameter = ParamStruct()

// Since foo isn't generic over `ParamProtocol`, the input will be recorded as a `ParamProtocol` and not a `ParamStruct`
myMock.foo(paramOne: parameter)

// Without specifying the input type, it resolves to `ParamProtocol`
#expectWasCalled(myMock.foo)

// When the input type is specified as `ParamStruct`, the expectation can be checked with the concrete type
#expectWasCalled(myMock.foo, taking: ParamStruct.self)

// When the expected input is specified, the expectation can be checked with the concrete type.
#expectWasCalled(myMock.foo, with: parameter)
}

@Test
func testWithMultipleProtocolConstrainedParameters() {
let myMock = MyMock()
let parameter = ParamStruct()

myMock.bar(paramOne: parameter, paramTwo: parameter)

#expectWasCalled(myMock.bar)
#expectWasCalled(myMock.bar, taking: (ParamStruct, ParamStruct).self)
#expectWasCalled(myMock.bar, with: parameter, parameter)
}

}

private protocol ParamProtocol {}
private struct ParamStruct: ParamProtocol, Equatable {}

@Mock
private struct MyMock {
fileprivate func foo(paramOne: ParamProtocol)
fileprivate func bar(paramOne: ParamProtocol, paramTwo: ParamProtocol)
}

0 comments on commit 56922f5

Please sign in to comment.