From 8e2ec5fb6041c0912db3259899748124a0934f01 Mon Sep 17 00:00:00 2001 From: Ryan Aveo Date: Fri, 9 Jun 2023 13:11:55 -0700 Subject: [PATCH 1/4] [Bugfix] Fix generic functions with same signature and different generic constraints not generated --- .../MockoloFramework/Models/MethodModel.swift | 3 +- .../TestGenericFuncs/FixtureGenericFunc.swift | 36 +++++++++++++++++++ .../TestGenericFuncs/GenericFuncTests.swift | 5 +++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 079f9aa5..5c59ae61 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -79,7 +79,8 @@ final class MethodModel: Model { let genericTypeNames = self.genericTypeParams.map { $0.name.capitalizeFirstLetter + $0.type.displayName } args.append(contentsOf: genericTypeNames) - + let genericWhereClause = self.genericWhereClause?.replacingOccurrences(of: " ", with: "_") ?? "" + args.append(genericWhereClause) args.append(contentsOf: paramTypes.map(\.displayName)) var displayType = self.type.displayName let capped = min(displayType.count, 32) diff --git a/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift b/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift index d934932e..58f9d78c 100644 --- a/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift +++ b/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift @@ -229,3 +229,39 @@ class NetworkingMock: Networking { } """ +let funcDuplicateSignatureDifferentWhereClause = """ +/// \(String.mockAnnotation) +protocol Storing { + func connect(adapter: T) where T: Adapter + func connect(adapter: T) where T: KeyedAdapter +} +""" + +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) + } + + } +} + + +""" diff --git a/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift b/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift index 6fecc9e4..ac3af1a8 100644 --- a/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift +++ b/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift @@ -16,6 +16,11 @@ class GenericFuncTests: MockoloTestCase { verify(srcContent: funcWhereClause, dstContent: funcWhereClauseMock) } + + func testWhereClauseWithSameSignature() { + verify(srcContent: funcDuplicateSignatureDifferentWhereClause, + dstContent: funcDuplicateSignatureDifferentWhereClauseMock) + } } From 01ca52b914ba06e402ba930d5c3c6b40b0cdcaaf Mon Sep 17 00:00:00 2001 From: Ryan Aveo Date: Fri, 9 Jun 2023 17:17:52 -0700 Subject: [PATCH 2/4] Fix the generated handler name to include equality and type constraints in camelcase --- .../MockoloFramework/Models/MethodModel.swift | 39 +++++++++- .../Utils/StringExtensions.swift | 5 ++ .../TestGenericFuncs/FixtureGenericFunc.swift | 77 +++++++++++++++++++ .../TestGenericFuncs/GenericFuncTests.swift | 5 ++ 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 5c59ae61..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,8 +113,9 @@ final class MethodModel: Model { let genericTypeNames = self.genericTypeParams.map { $0.name.capitalizeFirstLetter + $0.type.displayName } args.append(contentsOf: genericTypeNames) - let genericWhereClause = self.genericWhereClause?.replacingOccurrences(of: " ", with: "_") ?? "" - args.append(genericWhereClause) + 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 58f9d78c..21844b01 100644 --- a/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift +++ b/Tests/TestFuncs/TestGenericFuncs/FixtureGenericFunc.swift @@ -234,6 +234,8 @@ let funcDuplicateSignatureDifferentWhereClause = """ 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 } """ @@ -261,7 +263,82 @@ class StoringMock: Storing { } } + + 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 ac3af1a8..caf3d783 100644 --- a/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift +++ b/Tests/TestFuncs/TestGenericFuncs/GenericFuncTests.swift @@ -21,6 +21,11 @@ class GenericFuncTests: MockoloTestCase { verify(srcContent: funcDuplicateSignatureDifferentWhereClause, dstContent: funcDuplicateSignatureDifferentWhereClauseMock) } + + func testWhereClauseWithSameSignatureAndEqualityConstraints() { + verify(srcContent: funcDuplicateSignatureDifferentWhereClauseEquality, + dstContent: funcDuplicateSignatureDifferentWhereClauseEqualityMock) + } } From 6096bced5612fbc3ad33772575feb5762941b981 Mon Sep 17 00:00:00 2001 From: Ryan Aveo Date: Fri, 9 Jun 2023 17:24:49 -0700 Subject: [PATCH 3/4] Move genericWhereClause to the end of requirements to prevent any breaking API changes --- Sources/MockoloFramework/Models/MethodModel.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index fcd20be4..4fb2b503 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -122,6 +122,9 @@ final class MethodModel: Model { displayType.removeLast(displayType.count-capped) args.append(displayType) args.append(self.staticKind) + if let genericWhereClause { + args.append(genericWhereClauseToSignatureComponent) + } let ret = args.filter{ arg in !arg.isEmpty } return ret }() From fdc5ac7c64c129ffcfc53193ada4b304c65d85e0 Mon Sep 17 00:00:00 2001 From: Ryan Aveo Date: Fri, 9 Jun 2023 17:27:46 -0700 Subject: [PATCH 4/4] Revert "Move genericWhereClause to the end of requirements to prevent any breaking API changes" This reverts commit 6096bced5612fbc3ad33772575feb5762941b981. --- Sources/MockoloFramework/Models/MethodModel.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/MockoloFramework/Models/MethodModel.swift b/Sources/MockoloFramework/Models/MethodModel.swift index 4fb2b503..fcd20be4 100644 --- a/Sources/MockoloFramework/Models/MethodModel.swift +++ b/Sources/MockoloFramework/Models/MethodModel.swift @@ -122,9 +122,6 @@ final class MethodModel: Model { displayType.removeLast(displayType.count-capped) args.append(displayType) args.append(self.staticKind) - if let genericWhereClause { - args.append(genericWhereClauseToSignatureComponent) - } let ret = args.filter{ arg in !arg.isEmpty } return ret }()