From 51bb99c95eb844a824067a0fe69f7d74fd8cbf54 Mon Sep 17 00:00:00 2001 From: Tiago Pereira Date: Mon, 21 Oct 2024 12:58:42 +0200 Subject: [PATCH] fix: automockable to support same name on functions and all variable types --- CHANGELOG.md | 7 ++ .../Protocols/MockableFunctionBuilder.swift | 5 + .../Protocols/MockableVariablesBuilder.swift | 58 +++++++---- .../MockableMacrosTests.swift | 98 ++++++++++++++++--- 4 files changed, 136 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d2c46f..f4694bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [0.1.1] - 2024-10-21 + +### Fixed + +- Automockable to support functions with same name +- Automackable to support all kind of variables + # [0.1.0] - 2024-10-18 ### Added diff --git a/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableFunctionBuilder.swift b/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableFunctionBuilder.swift index 46fc3f2..69d329c 100644 --- a/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableFunctionBuilder.swift +++ b/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableFunctionBuilder.swift @@ -53,6 +53,7 @@ struct MockableFunctionBuilder { } private var argumentsRegistration: CodeBlockItemSyntax { + let allParameters = variables.allParameters ?? [] let varName = variables.parametersNameCall let callVariables: String = variablesName.reduce("", { acc, name in @@ -65,6 +66,10 @@ struct MockableFunctionBuilder { } }) + if allParameters.count == 1 { + return .init(stringLiteral: "self.\(varName).append(\(variablesName.first ?? ""))") + } + return .init(stringLiteral: "self.\(varName).append((\(callVariables)))") } diff --git a/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableVariablesBuilder.swift b/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableVariablesBuilder.swift index fff0b1f..3b1a9e4 100644 --- a/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableVariablesBuilder.swift +++ b/Sources/SageSwiftKitMacros/MockableMacros/Protocols/MockableVariablesBuilder.swift @@ -17,7 +17,15 @@ struct MockableVariablesBuilder { self.accessLevel = accessLevel } - private var name: String { functionSyntax.name.text } + private var name: String { + let name = functionSyntax.name.text + + let params = allParameters ?? [] + + return params.reduce(name, { acc, param -> String in + return acc+"_\(param.firstName.text)" + }) + } var callsCountName: String { "\(name)CallsCount" } var calledName: String { "\(name)Called" } @@ -40,30 +48,28 @@ struct MockableVariablesBuilder { ) } - var parametersTupple: String? { + var allParameters: [FunctionParameterSyntax]? { guard let clause = functionSyntax.signature.parameterClause.as(FunctionParameterClauseSyntax.self) else { return nil } - let allParameters: [FunctionParameterSyntax] = clause.parameters.compactMap { $0.as(FunctionParameterSyntax.self) } + return clause.parameters.compactMap { $0.as(FunctionParameterSyntax.self) + } + } + + var parametersTupple: String? { + guard let allParameters else { return nil } - var parameterTupple: (FunctionParameterSyntax) -> String = { (param: FunctionParameterSyntax) -> String in + let parameterTupple: (FunctionParameterSyntax) -> String = { (param: FunctionParameterSyntax) -> String in let name = param.secondName?.text ?? param.firstName.text - if var attributed = param.type.as(AttributedTypeSyntax.self) { - attributed.attributes = .init(stringLiteral: "") - return "\(name): \(attributed.description)" - } - - if var identifier = param.type.as(IdentifierTypeSyntax.self) { - return "\(name): \(identifier.description)" - } + let description = param.type.description.replacingOccurrences(of: "@escaping", with: "") - return "\(name): \(param.description)" + return "\(name): \(description)" } if allParameters.count == 1 { - return parameterTupple(allParameters[0]) + return allParameters[0].type.description } return allParameters.reduce("", { acc, param in @@ -78,7 +84,16 @@ struct MockableVariablesBuilder { } func parametersVar() throws -> VariableDeclSyntax? { - guard let parametersTupple else { return nil } + guard let parametersTupple, let allParameters else { return nil } + + if allParameters.count == 1 { + return try .init( + .init(stringLiteral: "\(accessLevel) var \(parametersName): \(parametersTupple)?"), + accessor: { + .init(stringLiteral: "self.\(parametersNameCall).last") + } + ) + } return try .init( .init(stringLiteral: "\(accessLevel) var \(parametersName): (\(parametersTupple))?"), @@ -97,17 +112,22 @@ struct MockableVariablesBuilder { } func returnVariable() throws -> VariableDeclSyntax? { - guard let returnValue = functionSyntax.signature.returnClause, - let type = returnValue.type.as(IdentifierTypeSyntax.self) else { + guard let returnValue = functionSyntax.signature.returnClause else { return nil } - if type.name.text == "Void" { + if returnValue.description.contains("Void") { return nil } + if returnValue.description.contains("?") { + return try .init( + .init(stringLiteral: "\(accessLevel) var \(returnName): \(returnValue.type.description)") + ) + } + return try .init( - .init(stringLiteral: "\(accessLevel) var \(returnName): \(type.description)!") + .init(stringLiteral: "\(accessLevel) var \(returnName): \(returnValue.type.description)!") ) } } diff --git a/Tests/SageSwiftKitTests/MockableMacrosTests/MockableMacrosTests.swift b/Tests/SageSwiftKitTests/MockableMacrosTests/MockableMacrosTests.swift index a79e410..2faa4b6 100644 --- a/Tests/SageSwiftKitTests/MockableMacrosTests/MockableMacrosTests.swift +++ b/Tests/SageSwiftKitTests/MockableMacrosTests/MockableMacrosTests.swift @@ -7,6 +7,9 @@ import SwiftSyntax import SwiftSyntaxBuilder import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport +import SageSwiftKit +import Foundation +import Combine import XCTest final class MockableMacrosTests: XCTestCase { @@ -14,7 +17,7 @@ final class MockableMacrosTests: XCTestCase { #if canImport(SageSwiftKitMacros) assertMacroExpansion( """ - @AutoMockable + @AutoMockable() protocol PlayingObject { var value: String? { get } @@ -31,21 +34,21 @@ final class MockableMacrosTests: XCTestCase { class PlayingObjectMock: PlayingObject { init() { } - var tmpFuncCallsCount: Int = 0 - var tmpFuncCalled: Bool { - tmpFuncCallsCount > 0 + var tmpFunc_valueCallsCount: Int = 0 + var tmpFunc_valueCalled: Bool { + tmpFunc_valueCallsCount > 0 } - var tmpFuncParameters: (value: String)? { - self.tmpFuncParametersCalls.last + var tmpFunc_valueParameters: String? { + self.tmpFunc_valueParametersCalls.last } - var tmpFuncParametersCalls: [(value: String)] = [] - var tmpFuncReturnValue: Int! - - + var tmpFunc_valueParametersCalls: [(String)] = [] + var tmpFunc_valueReturnValue: Int! + + func tmpFunc(value: String) -> Int { - self.tmpFuncCallsCount += 1 - self.tmpFuncParametersCalls.append((value: value)) - return self.tmpFuncReturnValue + self.tmpFunc_valueCallsCount += 1 + self.tmpFunc_valueParametersCalls.append(value) + return self.tmpFunc_valueReturnValue } } """, @@ -55,4 +58,73 @@ final class MockableMacrosTests: XCTestCase { throw XCTSkip("macros are only supported when running tests for the host platform") #endif } + + enum TestError: Error { + case noValue + } + + @AutoMockable + public protocol ProtocolToTest { + func fetchPeriodsList( + employeeId: Int, + startDate: Date?, + endDate: Date? + ) -> AnyPublisher<[Int], TestError> + + func fetchDetailPeriod( + employeeId: Int, + period: Double + ) -> AnyPublisher + + func fetchDetailPeriod( + employeeId: Int, + date: Date + ) -> AnyPublisher + + func detailPeriodCache(date: Date) -> TestError? + func fetchCommentsAndChangelog( + employeeId: Int64, + date: Date + ) -> AnyPublisher + + func saveWorkDay( + employeeId: Int64, + date: Date, + from: Date, + to: Date, + breakStart: Date?, + breakEnd: Date?, + breakLength: Int, + comments: [Bool] + ) -> AnyPublisher + func cancelTimesheet(employeeId: Int64, period: Bool) -> AnyPublisher + + func submitTimesheet( + employeeId: Int64, + period: Bool, + overtime: Int? + ) -> AnyPublisher + + func fetchGeofencingZones( + employeeId: Int64, + cache: Bool + ) -> AnyPublisher + + func fetchCurrentUser() -> AnyPublisher + + func updatePermission( + employeeId: Int64, + consent: Bool?, + deviceEnable: Bool? + ) -> AnyPublisher + } + + func testProtocol() { + let mockProtocol: ProtocolToTestMock = ProtocolToTestMock() + + XCTAssertEqual(mockProtocol.detailPeriodCache(date: Date()), nil) + mockProtocol.detailPeriodCache_dateReturnValue = .noValue + + XCTAssertEqual(mockProtocol.detailPeriodCache(date: Date()), .noValue) + } }