Fix not being able to put Optional into functions accepting Optionals.
This commit is contained in:
parent
417075a94a
commit
4b403883ea
|
@ -71,14 +71,20 @@ public struct Generator {
|
|||
private func matchableGenerics(with parameters: [MethodParameter]) -> String {
|
||||
guard parameters.isEmpty == false else { return "" }
|
||||
|
||||
let genericParameters = (1...parameters.count).map { "M\($0): Cuckoo.Matchable" }.joined(separator: ", ")
|
||||
let genericParameters = parameters.enumerated().map { index, parameter -> String in
|
||||
let type = parameter.isOptional ? "OptionalMatchable" : "Matchable"
|
||||
return "M\(index + 1): Cuckoo.\(type)"
|
||||
}.joined(separator: ", ")
|
||||
return "<\(genericParameters)>"
|
||||
}
|
||||
|
||||
private func matchableGenerics(where parameters: [MethodParameter]) -> String {
|
||||
guard parameters.isEmpty == false else { return "" }
|
||||
|
||||
let whereClause = parameters.enumerated().map { "M\($0 + 1).MatchedType == \(genericSafeType(from: $1.type.withoutAttributes.unoptionaled.sugarized))" }.joined(separator: ", ")
|
||||
let whereClause = parameters.enumerated().map { index, parameter in
|
||||
let type = parameter.isOptional ? "OptionalMatchedType" : "MatchedType"
|
||||
return "M\(index + 1).\(type) == \(genericSafeType(from: parameter.type.withoutAttributes.unoptionaled.sugarized))"
|
||||
}.joined(separator: ", ")
|
||||
return " where \(whereClause)"
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,12 @@ public protocol Matchable {
|
|||
|
||||
/// Matcher for this instance. This should be an equalTo type of a matcher, but it is not required.
|
||||
var matcher: ParameterMatcher<MatchedType> { get }
|
||||
}
|
||||
|
||||
var optionalMatcher: ParameterMatcher<MatchedType?> { get }
|
||||
public protocol OptionalMatchable {
|
||||
associatedtype OptionalMatchedType
|
||||
|
||||
var optionalMatcher: ParameterMatcher<OptionalMatchedType?> { get }
|
||||
}
|
||||
|
||||
public extension Matchable {
|
||||
|
@ -50,9 +54,20 @@ extension Optional: Matchable where Wrapped: Matchable, Wrapped.MatchedType == W
|
|||
}
|
||||
}
|
||||
|
||||
extension Matchable where Self == MatchedType {
|
||||
public var optionalMatcher: ParameterMatcher<MatchedType?> {
|
||||
return Optional(self).matcher
|
||||
extension Optional: OptionalMatchable where Wrapped: OptionalMatchable, Wrapped.OptionalMatchedType == Wrapped {
|
||||
public typealias OptionalMatchedType = Wrapped
|
||||
|
||||
public var optionalMatcher: ParameterMatcher<Wrapped?> {
|
||||
return ParameterMatcher<Wrapped?> { other in
|
||||
switch (self, other) {
|
||||
case (.none, .none):
|
||||
return true
|
||||
case (.some(let lhs), .some(let rhs)):
|
||||
return lhs.optionalMatcher.matches(rhs)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,32 +77,85 @@ extension Matchable where Self: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
extension OptionalMatchable where OptionalMatchedType == Self, Self: Equatable {
|
||||
public var optionalMatcher: ParameterMatcher<OptionalMatchedType?> {
|
||||
return ParameterMatcher { other in
|
||||
return Optional(self) == other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool: Matchable {}
|
||||
extension Bool: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Bool
|
||||
}
|
||||
|
||||
extension String: Matchable {}
|
||||
extension String: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = String
|
||||
}
|
||||
|
||||
extension Float: Matchable {}
|
||||
extension Float: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Float
|
||||
}
|
||||
|
||||
extension Double: Matchable {}
|
||||
extension Double: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Double
|
||||
}
|
||||
|
||||
extension Character: Matchable {}
|
||||
extension Character: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Character
|
||||
}
|
||||
|
||||
extension Int: Matchable {}
|
||||
extension Int: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Int
|
||||
}
|
||||
|
||||
extension Int8: Matchable {}
|
||||
extension Int8: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Int8
|
||||
}
|
||||
|
||||
extension Int16: Matchable {}
|
||||
extension Int16: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Int16
|
||||
}
|
||||
|
||||
extension Int32: Matchable {}
|
||||
extension Int32: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Int32
|
||||
}
|
||||
|
||||
extension Int64: Matchable {}
|
||||
extension Int64: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = Int64
|
||||
}
|
||||
|
||||
extension UInt: Matchable {}
|
||||
extension UInt: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = UInt
|
||||
}
|
||||
|
||||
extension UInt8: Matchable {}
|
||||
extension UInt8: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = UInt8
|
||||
}
|
||||
|
||||
extension UInt16: Matchable {}
|
||||
extension UInt16: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = UInt16
|
||||
}
|
||||
|
||||
extension UInt32: Matchable {}
|
||||
extension UInt32: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = UInt32
|
||||
}
|
||||
|
||||
extension UInt64: Matchable {}
|
||||
extension UInt64: OptionalMatchable {
|
||||
public typealias OptionalMatchedType = UInt64
|
||||
}
|
||||
|
|
|
@ -17,14 +17,30 @@ public struct ParameterMatcher<T>: Matchable {
|
|||
public var matcher: ParameterMatcher<T> {
|
||||
return self
|
||||
}
|
||||
|
||||
public var optionalMatcher: ParameterMatcher<Optional<T>> {
|
||||
return ParameterMatcher<Optional<T>> { other in
|
||||
other.map(self.matchesFunction) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
public func matches(_ input: T) -> Bool {
|
||||
return matchesFunction(input)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol CuckooOptionalType {
|
||||
associatedtype Wrapped
|
||||
|
||||
static func from(optional: Optional<Wrapped>) -> Self
|
||||
}
|
||||
|
||||
extension Optional: CuckooOptionalType {
|
||||
public static func from(optional: Optional<Wrapped>) -> Optional<Wrapped> {
|
||||
return optional
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterMatcher: OptionalMatchable where T: CuckooOptionalType {
|
||||
public typealias OptionalMatchedType = T.Wrapped
|
||||
|
||||
public var optionalMatcher: ParameterMatcher<T.Wrapped?> {
|
||||
return ParameterMatcher<T.Wrapped?> { other in
|
||||
other.map { self.matchesFunction(T.from(optional: $0)) } ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public func wrap<M: Matchable, IN>(matchable: M, mapping: @escaping (IN) -> M.Ma
|
|||
}
|
||||
}
|
||||
|
||||
public func wrap<M: Matchable, IN, O>(matchable: M, mapping: @escaping (IN) -> M.MatchedType?) -> ParameterMatcher<IN> where M.MatchedType == O {
|
||||
public func wrap<M: OptionalMatchable, IN, O>(matchable: M, mapping: @escaping (IN) -> M.OptionalMatchedType?) -> ParameterMatcher<IN> where M.OptionalMatchedType == O {
|
||||
return ParameterMatcher {
|
||||
return matchable.optionalMatcher.matches(mapping($0))
|
||||
}
|
||||
|
|
|
@ -206,17 +206,23 @@ class ClassTest: XCTestCase {
|
|||
let mock = MockOptionalParamsClass()
|
||||
|
||||
stub(mock) { mock in
|
||||
// when(mock.clashingFunction(param1: anyInt(), param2: "Henlo Fren")).thenDoNothing()
|
||||
// when(mock.clashingFunction(param1: anyInt(), param2: Optional("What's the question?"))).then { print("What's 6 times 9? \($0.0)") }
|
||||
// mock.function(param: "string")
|
||||
when(mock.clashingFunction(param1: Optional(1), param2: "Henlo Fren") as Cuckoo.ClassStubNoReturnFunction<(Int?, String)>).thenDoNothing()
|
||||
when(mock.clashingFunction(param1: Optional.none, param2: "Henlo Fren") as Cuckoo.ClassStubNoReturnFunction<(Int?, String)>).thenDoNothing()
|
||||
when(mock.clashingFunction(param1: anyInt(), param2: "What's the question?") as Cuckoo.ClassStubNoReturnFunction<(Int, String?)>).then {
|
||||
print("What's 6 times 9? \($0.0)")
|
||||
}
|
||||
when(mock.clashingFunction(param1: anyInt(), param2: isNil()) as Cuckoo.ClassStubNoReturnFunction<(Int, String?)>).then {
|
||||
print("What's 6 times 9? \($0.0)")
|
||||
}
|
||||
mock.function(param: "string")
|
||||
when(mock.function(param: "string")).thenDoNothing()
|
||||
}
|
||||
|
||||
// mock.clashingFunction(param1: Optional(22), param2: "Henlo Fren")
|
||||
// mock.clashingFunction(param1: 42, param2: Optional("What's the question?"))
|
||||
mock.clashingFunction(param1: Optional(22), param2: "Henlo Fren")
|
||||
mock.clashingFunction(param1: 42, param2: Optional("What's the question?"))
|
||||
|
||||
// verify(mock).clashingFunction(param1: anyInt(), param2: "Henlo Fren")
|
||||
// verify(mock).clashingFunction(param1: 42, param2: Optional("What's the question?"))
|
||||
_ = verify(mock).clashingFunction(param1: anyInt(), param2: "Henlo Fren") as Cuckoo.__DoNotUse<(Int?, String), Void>
|
||||
_ = verify(mock).clashingFunction(param1: 42, param2: "What's the question?") as Cuckoo.__DoNotUse<(Int, String?), Void>
|
||||
}
|
||||
|
||||
private enum TestError: Error {
|
||||
|
|
|
@ -145,16 +145,16 @@ class StubbingTest: XCTestCase {
|
|||
when(stub.subSubMethod()).thenReturn("not nil")
|
||||
|
||||
// sub-class
|
||||
// when(stub.withImplicitlyUnwrappedOptional(i: anyInt())).thenReturn("implicit unwrapped return")
|
||||
// when(stub.withThrows()).thenReturn(10)
|
||||
// when(stub.withNamedTuple(tuple: any())).thenReturn(11)
|
||||
// when(stub.subclassMethod()).thenReturn(12)
|
||||
when(stub.withImplicitlyUnwrappedOptional(i: anyInt())).thenReturn("implicit unwrapped return")
|
||||
when(stub.withThrows()).thenReturn(10)
|
||||
when(stub.withNamedTuple(tuple: any())).thenReturn(11)
|
||||
when(stub.subclassMethod()).thenReturn(12)
|
||||
// when(stub.withOptionalClosureAndReturn(anyString(), closure: isNil())).thenReturn(2)
|
||||
// when(stub.withClosureAndParam(anyString(), closure: anyClosure())).thenReturn(3)
|
||||
// when(stub.withMultClosures(closure: anyClosure(), closureB: anyClosure(), closureC: anyClosure())).thenReturn(4)
|
||||
// when(stub.withThrowingClosure(closure: anyThrowingClosure())).thenReturn("throwing closure")
|
||||
// when(stub.withThrowingClosureThrows(closure: anyThrowingClosure())).thenReturn("closure throwing")
|
||||
// when(stub.withThrowingEscapingClosure(closure: anyThrowingClosure())).thenReturn("escaping closure")
|
||||
when(stub.withClosureAndParam(anyString(), closure: anyClosure())).thenReturn(3)
|
||||
when(stub.withMultClosures(closure: anyClosure(), closureB: anyClosure(), closureC: anyClosure())).thenReturn(4)
|
||||
when(stub.withThrowingClosure(closure: anyThrowingClosure())).thenReturn("throwing closure")
|
||||
when(stub.withThrowingClosureThrows(closure: anyThrowingClosure())).thenReturn("closure throwing")
|
||||
when(stub.withThrowingEscapingClosure(closure: anyThrowingClosure())).thenReturn("escaping closure")
|
||||
// when(stub.withThrowingOptionalClosureThrows(closure: anyOptionalThrowingClosure())).thenReturn("optional closure throwing")
|
||||
when(stub.methodWithParameter(anyString())).thenReturn("parameter string")
|
||||
when(stub.methodWithParameter(anyInt())).thenReturn("parameter int")
|
||||
|
@ -181,9 +181,9 @@ class StubbingTest: XCTestCase {
|
|||
when(stub.withEscape(anyString(), action: anyClosure())).then { _ in
|
||||
callWithEscape = true
|
||||
}
|
||||
// when(stub.withOptionalClosure(anyString(), closure: anyClosure())).then { _ in
|
||||
// callWithOptionalClosure = true
|
||||
// }
|
||||
when(stub.withOptionalClosure(anyString(), closure: anyClosure())).then { _ in
|
||||
callWithOptionalClosure = true
|
||||
}
|
||||
when(stub.withLabelAndUnderscore(labelA: anyString(), anyString())).then { _ in
|
||||
callWithLabelAndUnderscore = true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue