From c4a5deedc1114b3a219a4d848783b221a3a169fe Mon Sep 17 00:00:00 2001 From: Nayanda Haberty Date: Tue, 18 Jun 2024 18:53:14 +0700 Subject: [PATCH] fix: excluding private modifiers for mock members --- .../Factory/BuilderFactory.swift | 8 +- .../Factory/ConformanceFactory.swift | 6 +- .../MockableMacro/Factory/EnumFactory.swift | 4 +- .../MockableMacro/Factory/MemberFactory.swift | 10 +- .../Requirements/Requirements.swift | 10 +- .../AccessModifierTests.swift | 126 ++++++++++++++++++ Tests/MockableTests/PolicyTests.swift | 2 +- 7 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 Tests/MockableMacroTests/AccessModifierTests.swift diff --git a/Sources/MockableMacro/Factory/BuilderFactory.swift b/Sources/MockableMacro/Factory/BuilderFactory.swift index 2f931f26..d325dd97 100644 --- a/Sources/MockableMacro/Factory/BuilderFactory.swift +++ b/Sources/MockableMacro/Factory/BuilderFactory.swift @@ -29,7 +29,7 @@ extension BuilderFactory { _ requirements: Requirements ) throws -> some DeclSyntaxProtocol { StructDeclSyntax( - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: kind.name, inheritanceClause: InheritanceClauseSyntax(inheritedTypes: inheritedTypes(kind)), memberBlock: MemberBlockSyntax(members: try members(kind, requirements)) @@ -53,7 +53,7 @@ extension BuilderFactory { MemberBlockItemSyntax( decl: try variable.builder( of: kind, - with: requirements.syntax.modifiers, + with: requirements.modifiers, using: requirements.syntax.mockType ) ) @@ -62,7 +62,7 @@ extension BuilderFactory { MemberBlockItemSyntax( decl: try function.builder( of: kind, - with: requirements.syntax.modifiers, + with: requirements.modifiers, using: requirements.syntax.mockType ) ) @@ -101,7 +101,7 @@ extension BuilderFactory { _ requirements: Requirements ) -> InitializerDeclSyntax { InitializerDeclSyntax( - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, signature: initializerSignature(kind, requirements) ) { InfixOperatorExprSyntax( diff --git a/Sources/MockableMacro/Factory/ConformanceFactory.swift b/Sources/MockableMacro/Factory/ConformanceFactory.swift index 0df0b3da..f600638b 100644 --- a/Sources/MockableMacro/Factory/ConformanceFactory.swift +++ b/Sources/MockableMacro/Factory/ConformanceFactory.swift @@ -27,7 +27,7 @@ extension ConformanceFactory { try MemberBlockItemListSyntax { for variable in requirements.variables { MemberBlockItemSyntax( - decl: try variable.implement(with: requirements.syntax.modifiers.trimmed) + decl: try variable.implement(with: requirements.modifiers) ) } } @@ -37,7 +37,7 @@ extension ConformanceFactory { try MemberBlockItemListSyntax { for function in requirements.functions { MemberBlockItemSyntax( - decl: try function.implement(with: requirements.syntax.modifiers.trimmed) + decl: try function.implement(with: requirements.modifiers) ) } } @@ -47,7 +47,7 @@ extension ConformanceFactory { try MemberBlockItemListSyntax { for initializer in requirements.initializers { MemberBlockItemSyntax( - decl: try initializer.implement(with: requirements.syntax.modifiers.trimmed) + decl: try initializer.implement(with: requirements.modifiers) ) } } diff --git a/Sources/MockableMacro/Factory/EnumFactory.swift b/Sources/MockableMacro/Factory/EnumFactory.swift index 0540db70..c453c50d 100644 --- a/Sources/MockableMacro/Factory/EnumFactory.swift +++ b/Sources/MockableMacro/Factory/EnumFactory.swift @@ -14,7 +14,7 @@ import SwiftSyntax enum EnumFactory: Factory { static func build(from requirements: Requirements) throws -> EnumDeclSyntax { EnumDeclSyntax( - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.Member, inheritanceClause: inheritanceClause, memberBlock: MemberBlockSyntax(members: try members(requirements)) @@ -67,7 +67,7 @@ extension EnumFactory { try matcherSwitch(requirements) } let decl = FunctionDeclSyntax( - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.match, signature: signature, body: .init(statements: statement) diff --git a/Sources/MockableMacro/Factory/MemberFactory.swift b/Sources/MockableMacro/Factory/MemberFactory.swift index a454bb57..b8f5775b 100644 --- a/Sources/MockableMacro/Factory/MemberFactory.swift +++ b/Sources/MockableMacro/Factory/MemberFactory.swift @@ -29,7 +29,7 @@ enum MemberFactory: Factory { extension MemberFactory { private static func defaultInit(_ requirements: Requirements) -> InitializerDeclSyntax { InitializerDeclSyntax( - modifiers: requirements.syntax.modifiers.trimmed, + modifiers: requirements.modifiers, signature: .init(parameterClause: defaultInitParameters), body: .init { CodeBlockItemSyntax(item: .expr(mockerAssignmentWithPolicy)) } ) @@ -64,7 +64,7 @@ extension MemberFactory { private static func given(_ requirements: Requirements) -> FunctionDeclSyntax { FunctionDeclSyntax( attributes: unavailableAttribute(message: Messages.givenMessage), - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.given, signature: .init( parameterClause: FunctionParameterClauseSyntax(parameters: []), @@ -78,7 +78,7 @@ extension MemberFactory { private static func when(_ requirements: Requirements) -> FunctionDeclSyntax { FunctionDeclSyntax( attributes: unavailableAttribute(message: Messages.whenMessage), - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.when, signature: .init( parameterClause: FunctionParameterClauseSyntax(parameters: []), @@ -92,7 +92,7 @@ extension MemberFactory { private static func verify(_ requirements: Requirements) -> FunctionDeclSyntax { FunctionDeclSyntax( attributes: unavailableAttribute(message: Messages.verifyMessage), - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.verify, signature: .init( parameterClause: FunctionParameterClauseSyntax( @@ -110,7 +110,7 @@ extension MemberFactory { } private static func reset(_ requirements: Requirements) -> FunctionDeclSyntax { FunctionDeclSyntax( - modifiers: requirements.syntax.modifiers, + modifiers: requirements.modifiers, name: NS.reset, signature: .init( parameterClause: FunctionParameterClauseSyntax( diff --git a/Sources/MockableMacro/Requirements/Requirements.swift b/Sources/MockableMacro/Requirements/Requirements.swift index fbc6627d..9a320e5f 100644 --- a/Sources/MockableMacro/Requirements/Requirements.swift +++ b/Sources/MockableMacro/Requirements/Requirements.swift @@ -11,7 +11,8 @@ struct Requirements { // MARK: Properties - var syntax: ProtocolDeclSyntax + let syntax: ProtocolDeclSyntax + let modifiers: DeclModifierListSyntax var functions = [FunctionRequirement]() var variables = [VariableRequirement]() var initializers = [InitializerRequirement]() @@ -20,12 +21,17 @@ struct Requirements { init(_ syntax: ProtocolDeclSyntax) throws { self.syntax = syntax - let members = syntax.memberBlock.members guard members.compactMap({ $0.decl.as(SubscriptDeclSyntax.self) }).isEmpty else { throw MockableMacroError.subscriptsNotSupported } + self.modifiers = syntax.modifiers.trimmed.filter { modifier in + guard case .keyword(let keyword) = modifier.name.tokenKind else { + return true + } + return keyword != .private + } self.variables = try members .compactMap { $0.decl.as(VariableDeclSyntax.self) } diff --git a/Tests/MockableMacroTests/AccessModifierTests.swift b/Tests/MockableMacroTests/AccessModifierTests.swift new file mode 100644 index 00000000..e9096a60 --- /dev/null +++ b/Tests/MockableMacroTests/AccessModifierTests.swift @@ -0,0 +1,126 @@ +// +// File.swift +// +// +// Created by Nayanda Haberty on 29/5/24. +// + +import MacroTesting +import XCTest +import SwiftSyntax +@testable import Mockable + +final class AccessModifierTests: MockableMacroTestCase { + func test_private_access_modifier() { + assertMacro { + """ + @Mockable + private protocol Test { + var foo: Int { get } + func bar(number: Int) -> Int + } + """ + } expansion: { + """ + private protocol Test { + var foo: Int { get } + func bar(number: Int) -> Int + } + + #if MOCKING + private final class MockTest: Test, MockableService { + private var mocker = Mocker() + @available(*, deprecated, message: "Use given(_ service:) of Mockable instead. ") + func given() -> ReturnBuilder { + .init(mocker: mocker) + } + @available(*, deprecated, message: "Use when(_ service:) of Mockable instead. ") + func when() -> ActionBuilder { + .init(mocker: mocker) + } + @available(*, deprecated, message: "Use verify(_ service:) of MockableTest instead. ") + func verify(with assertion: @escaping MockableAssertion) -> VerifyBuilder { + .init(mocker: mocker, assertion: assertion) + } + func reset(_ scopes: Set = .all) { + mocker.reset(scopes: scopes) + } + init(policy: MockerPolicy? = nil) { + if let policy { + mocker.policy = policy + } + } + func bar(number: Int) -> Int { + let member: Member = .m2_bar(number: .value(number)) + return mocker.mock(member) { producer in + let producer = try cast(producer) as (Int) -> Int + return producer(number) + } + } + var foo: Int { + get { + let member: Member = .m1_foo + return mocker.mock(member) { producer in + let producer = try cast(producer) as () -> Int + return producer() + } + } + } + enum Member: Matchable, CaseIdentifiable { + case m1_foo + case m2_bar(number: Parameter) + func match(_ other: Member) -> Bool { + switch (self, other) { + case (.m1_foo, .m1_foo): + return true + case (.m2_bar(number: let leftNumber), .m2_bar(number: let rightNumber)): + return leftNumber.match(rightNumber) + default: + return false + } + } + } + struct ReturnBuilder: EffectBuilder { + private let mocker: Mocker + init(mocker: Mocker) { + self.mocker = mocker + } + var foo: FunctionReturnBuilder Int> { + .init(mocker, kind: .m1_foo) + } + func bar(number: Parameter) -> FunctionReturnBuilder Int> { + .init(mocker, kind: .m2_bar(number: number)) + } + } + struct ActionBuilder: EffectBuilder { + private let mocker: Mocker + init(mocker: Mocker) { + self.mocker = mocker + } + var foo: FunctionActionBuilder { + .init(mocker, kind: .m1_foo) + } + func bar(number: Parameter) -> FunctionActionBuilder { + .init(mocker, kind: .m2_bar(number: number)) + } + } + struct VerifyBuilder: AssertionBuilder { + private let mocker: Mocker + private let assertion: MockableAssertion + init(mocker: Mocker, assertion: @escaping MockableAssertion) { + self.mocker = mocker + self.assertion = assertion + } + var foo: FunctionVerifyBuilder { + .init(mocker, kind: .m1_foo, assertion: assertion) + } + func bar(number: Parameter) -> FunctionVerifyBuilder { + .init(mocker, kind: .m2_bar(number: number), assertion: assertion) + } + } + } + #endif + """ + } + } +} diff --git a/Tests/MockableTests/PolicyTests.swift b/Tests/MockableTests/PolicyTests.swift index dec332c2..33811781 100644 --- a/Tests/MockableTests/PolicyTests.swift +++ b/Tests/MockableTests/PolicyTests.swift @@ -20,7 +20,7 @@ extension Car: Mockable { } @Mockable -protocol PolicyService { +private protocol PolicyService { func throwingVoidFunc() throws var throwingVoidProp: Void { get throws } func nonThrowingVoidFunc()