diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 079f9aa5..fcd20be4 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -49,6 +49,40 @@ final class MethodModel: Model { private var staticKind: String { return isStatic ? .static : "" } + + /// This is used to uniquely identify methods with the same signature and different generic requirements + var genericWhereClauseToSignatureComponent: String { + guard let genericWhereClause else { + return "" + } + let typeRequirementSyntax = ":" + let typeEqualitySyntax = "==" + + var signatureComponents: [String] = [] + + genericWhereClause.deletingPrefix("where").components(separatedBy: ",").forEach { requirement in + if requirement.contains(typeRequirementSyntax) { + let components = requirement.components(separatedBy: typeRequirementSyntax).map{ $0.trimmingCharacters(in: .whitespaces) } + guard let key = components.first, let value = components.last else { + return + } + let valueDescription = value.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "&", with: "And") + signatureComponents.append(contentsOf: [key, valueDescription]) + } else if requirement.contains(typeEqualitySyntax) { + let components = requirement.components(separatedBy: typeEqualitySyntax).map{ $0.trimmingCharacters(in: .whitespaces) } + guard let key = components.first, let value = components.last else { + return + } + signatureComponents.append(contentsOf: [key, value]) + } + } + + return signatureComponents.map { component in + var newComponent = component + newComponent.removeAll(where: { $0 == "."}) + return newComponent + }.joined() + } var isInitializer: Bool { if case .initKind(_, _) = kind { @@ -79,7 +113,9 @@ final class MethodModel: Model { let genericTypeNames = self.genericTypeParams.map { $0.name.capitalizeFirstLetter + $0.type.displayName } args.append(contentsOf: genericTypeNames) - + if let genericWhereClause { + args.append(genericWhereClauseToSignatureComponent) + } args.append(contentsOf: paramTypes.map(\.displayName)) var displayType = self.type.displayName let capped = min(displayType.count, 32) diff --git a/Sources/MockoloFramework/Utils/StringExtensions.swift b/Sources/MockoloFramework/Utils/StringExtensions.swift index fb6ffafb..d8ae5b71 100644 --- a/Sources/MockoloFramework/Utils/StringExtensions.swift +++ b/Sources/MockoloFramework/Utils/StringExtensions.swift @@ -190,6 +190,11 @@ extension String { } return !argsMap.isEmpty ? argsMap : nil } + + func deletingPrefix(_ prefix: String) -> String { + guard self.hasPrefix(prefix) else { return self } + return String(self.dropFirst(prefix.count)) + } } let separatorsForDisplay = CharacterSet(charactersIn: "<>[] :,()_-.&@#!{}@+\"\'") diff --git a/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift b/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift index d934932e..21844b01 100644 --- a/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift +++ b/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift @@ -229,3 +229,116 @@ class NetworkingMock: Networking { } """ +let funcDuplicateSignatureDifferentWhereClause = """ +/// \(String.mockAnnotation) +protocol Storing { + func connect(adapter: T) where T: Adapter + func connect(adapter: T) where T: KeyedAdapter + func connect(adapter: T) where T: KeyedAdapter2 + func connect(adapter: T) where T: KeyedAdapter3 +} +""" + +let funcDuplicateSignatureDifferentWhereClauseMock = """ +class StoringMock: Storing { + init() { } + + + private(set) var connectCallCount = 0 + var connectHandler: ((Any) -> ())? + func connect(adapter: T) where T: Adapter { + connectCallCount += 1 + if let connectHandler = connectHandler { + connectHandler(adapter) + } + + } + + private(set) var connectAdapterCallCount = 0 + var connectAdapterHandler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter { + connectAdapterCallCount += 1 + if let connectAdapterHandler = connectAdapterHandler { + connectAdapterHandler(adapter) + } + + } + + private(set) var connectAdapterTCallCount = 0 + var connectAdapterTHandler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter2 { + connectAdapterTCallCount += 1 + if let connectAdapterTHandler = connectAdapterTHandler { + connectAdapterTHandler(adapter) + } + + } + + private(set) var connectAdapterTTKeyedAdapter3CallCount = 0 + var connectAdapterTTKeyedAdapter3Handler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter3 { + connectAdapterTTKeyedAdapter3CallCount += 1 + if let connectAdapterTTKeyedAdapter3Handler = connectAdapterTTKeyedAdapter3Handler { + connectAdapterTTKeyedAdapter3Handler(adapter) + } + + } +} +""" + +let funcDuplicateSignatureDifferentWhereClauseEquality = """ +/// \(String.mockAnnotation) +protocol Storing { + func connect(adapter: T) where T: Adapter, T.Element == S.Element + func connect(adapter: T) where T: KeyedAdapter, T.Element == S.Element + func connect(adapter: T) where T: KeyedAdapter2, T.Element == S.Element + func connect(adapter: T) where T: KeyedAdapter3, T.Element == S.Element +} +""" + +let funcDuplicateSignatureDifferentWhereClauseEqualityMock = """ +class StoringMock: Storing { + init() { } + + + private(set) var connectCallCount = 0 + var connectHandler: ((Any) -> ())? + func connect(adapter: T) where T: Adapter, T.Element == S.Element { + connectCallCount += 1 + if let connectHandler = connectHandler { + connectHandler(adapter) + } + + } + + private(set) var connectAdapterCallCount = 0 + var connectAdapterHandler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter, T.Element == S.Element { + connectAdapterCallCount += 1 + if let connectAdapterHandler = connectAdapterHandler { + connectAdapterHandler(adapter) + } + + } + + private(set) var connectAdapterTCallCount = 0 + var connectAdapterTHandler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter2, T.Element == S.Element { + connectAdapterTCallCount += 1 + if let connectAdapterTHandler = connectAdapterTHandler { + connectAdapterTHandler(adapter) + } + + } + + private(set) var connectAdapterTTKeyedAdapter3TElementSElementCallCount = 0 + var connectAdapterTTKeyedAdapter3TElementSElementHandler: ((Any) -> ())? + func connect(adapter: T) where T: KeyedAdapter3, T.Element == S.Element { + connectAdapterTTKeyedAdapter3TElementSElementCallCount += 1 + if let connectAdapterTTKeyedAdapter3TElementSElementHandler = connectAdapterTTKeyedAdapter3TElementSElementHandler { + connectAdapterTTKeyedAdapter3TElementSElementHandler(adapter) + } + + } +} +""" diff --git a/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift b/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift index 6fecc9e4..caf3d783 100644 --- a/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift +++ b/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift @@ -16,6 +16,16 @@ class GenericFuncTests: MockoloTestCase { verify(srcContent: funcWhereClause, dstContent: funcWhereClauseMock) } + + func testWhereClauseWithSameSignature() { + verify(srcContent: funcDuplicateSignatureDifferentWhereClause, + dstContent: funcDuplicateSignatureDifferentWhereClauseMock) + } + + func testWhereClauseWithSameSignatureAndEqualityConstraints() { + verify(srcContent: funcDuplicateSignatureDifferentWhereClauseEquality, + dstContent: funcDuplicateSignatureDifferentWhereClauseEqualityMock) + } }