From 0ce20ac077cccfebe780d16233fdf8c89a8e8ff9 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Tue, 16 Jan 2024 22:00:02 +0000 Subject: [PATCH 01/12] Inherit access modifier --- .../SpyableMacro/Factories/SpyFactory.swift | 1 + .../Macro/UT_SpyableMacro.swift | 50 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index b73421b..f8b250f 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -108,6 +108,7 @@ struct SpyFactory { .compactMap { $0.decl.as(FunctionDeclSyntax.self)?.removingLeadingSpaces } return try ClassDeclSyntax( + modifiers: protocolDeclaration.modifiers, name: identifier, genericParameterClause: genericParameterClause, inheritanceClause: InheritanceClauseSyntax { diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index b84fe3c..18a56b9 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -49,7 +49,7 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) - class ServiceProtocolSpy: ServiceProtocol { + public class ServiceProtocolSpy: ServiceProtocol { var name: String { get { underlyingName @@ -199,7 +199,7 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) #if CUSTOM - class ServiceProtocolSpy: ServiceProtocol { + public class ServiceProtocolSpy: ServiceProtocol { var variable: Bool? } #endif @@ -207,4 +207,50 @@ final class UT_SpyableMacro: XCTestCase { macros: sut ) } + + func testMacroWithNoFlag() { + let protocolDeclaration = """ + public protocol ServiceProtocol { + var variable: Bool? { get set } + } + """ + assertMacroExpansion( + """ + @Spyable(behindPreprocessorFlag: nil) + \(protocolDeclaration) + """, + expandedSource: """ + + \(protocolDeclaration) + + public class ServiceProtocolSpy: ServiceProtocol { + var variable: Bool? + } + """, + macros: sut + ) + } + + func testMacroInternal() { + let protocolDeclaration = """ + protocol ServiceProtocol { + var variable: Bool? { get set } + } + """ + assertMacroExpansion( + """ + @Spyable(behindPreprocessorFlag: nil) + \(protocolDeclaration) + """, + expandedSource: """ + + \(protocolDeclaration) + + class ServiceProtocolSpy: ServiceProtocol { + var variable: Bool? + } + """, + macros: sut + ) + } } From 60b571ec5db932646b571de34671acb351120e64 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Tue, 16 Jan 2024 22:11:04 +0000 Subject: [PATCH 02/12] Add access to variables --- .../SpyableMacro/Factories/SpyFactory.swift | 4 +++- .../VariablesImplementationFactory.swift | 6 +++-- .../Factories/UT_SpyFactory.swift | 23 +++++++++++++++++++ .../UT_VariablesImplementationFactory.swift | 10 ++++++++ .../Macro/UT_SpyableMacro.swift | 6 ++--- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index f8b250f..de9d447 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -93,6 +93,7 @@ struct SpyFactory { private let functionImplementationFactory = FunctionImplementationFactory() func classDeclaration(for protocolDeclaration: ProtocolDeclSyntax) throws -> ClassDeclSyntax { + let modifiers = protocolDeclaration.modifiers let identifier = TokenSyntax.identifier(protocolDeclaration.name.text + "Spy") let assosciatedtypeDeclarations = protocolDeclaration.memberBlock.members.compactMap { @@ -108,7 +109,7 @@ struct SpyFactory { .compactMap { $0.decl.as(FunctionDeclSyntax.self)?.removingLeadingSpaces } return try ClassDeclSyntax( - modifiers: protocolDeclaration.modifiers, + modifiers: modifiers, name: identifier, genericParameterClause: genericParameterClause, inheritanceClause: InheritanceClauseSyntax { @@ -119,6 +120,7 @@ struct SpyFactory { memberBlockBuilder: { for variableDeclaration in variableDeclarations { try variablesImplementationFactory.variablesDeclarations( + modifiers: modifiers, protocolVariableDeclaration: variableDeclaration ) } diff --git a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift index fdeff47..2c0d227 100644 --- a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift +++ b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift @@ -47,6 +47,7 @@ struct VariablesImplementationFactory { @MemberBlockItemListBuilder func variablesDeclarations( + modifiers: DeclModifierListSyntax, protocolVariableDeclaration: VariableDeclSyntax ) throws -> MemberBlockItemListSyntax { if protocolVariableDeclaration.bindings.count == 1 { @@ -56,7 +57,7 @@ struct VariablesImplementationFactory { if binding.typeAnnotation?.type.is(OptionalTypeSyntax.self) == true { accessorRemovalVisitor.visit(protocolVariableDeclaration) } else { - try protocolVariableDeclarationWithGetterAndSetter(binding: binding) + try protocolVariableDeclarationWithGetterAndSetter(modifiers: modifiers, binding: binding) try underlyingVariableDeclaration(binding: binding) } @@ -67,11 +68,12 @@ struct VariablesImplementationFactory { } private func protocolVariableDeclarationWithGetterAndSetter( + modifiers: DeclModifierListSyntax, binding: PatternBindingSyntax ) throws -> VariableDeclSyntax { try VariableDeclSyntax( """ - var \(binding.pattern.trimmed)\(binding.typeAnnotation!.trimmed) { + \(modifiers)var \(binding.pattern.trimmed)\(binding.typeAnnotation!.trimmed) { get { \(raw: underlyingVariableName(binding: binding)) } set { \(raw: underlyingVariableName(binding: binding)) = newValue } } diff --git a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift index 4a6ffb4..000f596 100644 --- a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift @@ -234,6 +234,29 @@ final class UT_SpyFactory: XCTestCase { ) } + func testDeclarationVariablePublic() throws { + try assertProtocol( + withDeclaration: """ + public protocol ServiceProtocol { + var data: Data { get } + } + """, + expectingClassDeclaration: """ + public class ServiceProtocolSpy: ServiceProtocol { + public var data: Data { + get { + underlyingData + } + set { + underlyingData = newValue + } + } + var underlyingData: (Data)! + } + """ + ) + } + func testDeclarationOptionalVariable() throws { try assertProtocol( withDeclaration: """ diff --git a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift index ee49aa6..a19002b 100644 --- a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift @@ -48,11 +48,19 @@ final class UT_VariablesImplementationFactory: XCTestCase { ) } + func testVariableDelcarationsWithAccess() throws { + try assertProtocolVariable( + withVariableDeclaration: "public var foo: String? { get }", + expectingVariableDeclaration: "public var foo: String?" + ) + } + func testVariablesDeclarationsWithMultiBindings() throws { let protocolVariableDeclaration = try VariableDeclSyntax("var foo: String?, bar: Int") XCTAssertThrowsError( try VariablesImplementationFactory().variablesDeclarations( + modifiers: [], protocolVariableDeclaration: protocolVariableDeclaration ) ) { error in @@ -68,6 +76,7 @@ final class UT_VariablesImplementationFactory: XCTestCase { XCTAssertThrowsError( try VariablesImplementationFactory().variablesDeclarations( + modifiers: [], protocolVariableDeclaration: protocolVariableDeclaration ) ) { error in @@ -89,6 +98,7 @@ final class UT_VariablesImplementationFactory: XCTestCase { let protocolVariableDeclaration = try VariableDeclSyntax("\(raw: variableDeclaration)") let result = try VariablesImplementationFactory().variablesDeclarations( + modifiers: [], protocolVariableDeclaration: protocolVariableDeclaration ) diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index 18a56b9..c25235d 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -50,7 +50,7 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) public class ServiceProtocolSpy: ServiceProtocol { - var name: String { + public var name: String { get { underlyingName } @@ -59,7 +59,7 @@ final class UT_SpyableMacro: XCTestCase { } } var underlyingName: (String)! - var anyProtocol: any Codable { + public var anyProtocol: any Codable { get { underlyingAnyProtocol } @@ -69,7 +69,7 @@ final class UT_SpyableMacro: XCTestCase { } var underlyingAnyProtocol: (any Codable)! var secondName: String? - var added: () -> Void { + public var added: () -> Void { get { underlyingAdded } From 1d21ea6a0daacff900065069c0bc9873689a3a5a Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Wed, 17 Jan 2024 12:31:34 +0000 Subject: [PATCH 03/12] Add access modifiers to all generated protocol conformances --- .../Factories/CalledFactory.swift | 8 +- .../Factories/CallsCountFactory.swift | 8 +- .../Factories/ClosureFactory.swift | 5 +- .../FunctionImplementationFactory.swift | 10 +- .../Factories/ReceivedArgumentsFactory.swift | 5 +- .../ReceivedInvocationsFactory.swift | 5 +- .../Factories/ReturnValueFactory.swift | 5 +- .../SpyableMacro/Factories/SpyFactory.swift | 14 ++- .../Factories/ThrowableErrorFactory.swift | 8 +- .../VariablesImplementationFactory.swift | 27 ++++- .../Factories/UT_CalledFactory.swift | 19 ++- .../Factories/UT_CallsCountFactory.swift | 17 ++- .../Factories/UT_ClosureFactory.swift | 1 + .../UT_FunctionImplementationFactory.swift | 14 +++ .../UT_ReceivedArgumentsFactory.swift | 9 ++ .../UT_ReceivedInvocationsFactory.swift | 1 + .../Factories/UT_ReturnValueFactory.swift | 1 + .../Factories/UT_SpyFactory.swift | 2 +- .../Factories/UT_ThrowableErrorFactory.swift | 19 ++- .../UT_VariablesImplementationFactory.swift | 2 +- .../Macro/UT_SpyableMacro.swift | 111 ++++++++++++------ 21 files changed, 229 insertions(+), 62 deletions(-) diff --git a/Sources/SpyableMacro/Factories/CalledFactory.swift b/Sources/SpyableMacro/Factories/CalledFactory.swift index 3f75036..88ffad6 100644 --- a/Sources/SpyableMacro/Factories/CalledFactory.swift +++ b/Sources/SpyableMacro/Factories/CalledFactory.swift @@ -25,13 +25,17 @@ import SwiftSyntaxBuilder /// ``` /// and an argument `variablePrefix` equal to `foo`. struct CalledFactory { - func variableDeclaration(variablePrefix: String) throws -> VariableDeclSyntax { - try VariableDeclSyntax( + func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws + -> VariableDeclSyntax + { + var decl = try VariableDeclSyntax( """ var \(raw: variablePrefix)Called: Bool { return \(raw: variablePrefix)CallsCount > 0 } """ ) + decl.modifiers = modifiers + return decl } } diff --git a/Sources/SpyableMacro/Factories/CallsCountFactory.swift b/Sources/SpyableMacro/Factories/CallsCountFactory.swift index e6cc68e..06bc2cf 100644 --- a/Sources/SpyableMacro/Factories/CallsCountFactory.swift +++ b/Sources/SpyableMacro/Factories/CallsCountFactory.swift @@ -21,12 +21,16 @@ import SwiftSyntaxBuilder /// ``` /// and an argument `variablePrefix` equal to `foo`. struct CallsCountFactory { - func variableDeclaration(variablePrefix: String) throws -> VariableDeclSyntax { - try VariableDeclSyntax( + func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws + -> VariableDeclSyntax + { + var decl = try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)) = 0 """ ) + decl.modifiers = modifiers + return decl } func incrementVariableExpression(variablePrefix: String) -> ExprSyntax { diff --git a/Sources/SpyableMacro/Factories/ClosureFactory.swift b/Sources/SpyableMacro/Factories/ClosureFactory.swift index c1f39b3..84522ed 100644 --- a/Sources/SpyableMacro/Factories/ClosureFactory.swift +++ b/Sources/SpyableMacro/Factories/ClosureFactory.swift @@ -29,6 +29,7 @@ import SwiftSyntaxBuilder /// interacts correctly with the function. struct ClosureFactory { func variableDeclaration( + modifiers: DeclModifierListSyntax, variablePrefix: String, functionSignature: FunctionSignatureSyntax ) throws -> VariableDeclSyntax { @@ -54,11 +55,13 @@ struct ClosureFactory { ) } - return try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)): (\(elements))? """ ) + decl.modifiers = modifiers + return decl } func callExpression( diff --git a/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift b/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift index 006748c..6674c0f 100644 --- a/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift +++ b/Sources/SpyableMacro/Factories/FunctionImplementationFactory.swift @@ -64,12 +64,20 @@ struct FunctionImplementationFactory { private let returnValueFactory = ReturnValueFactory() func declaration( + modifiers: DeclModifierListSyntax, variablePrefix: String, protocolFunctionDeclaration: FunctionDeclSyntax ) -> FunctionDeclSyntax { var spyFunctionDeclaration = protocolFunctionDeclaration - spyFunctionDeclaration.modifiers = protocolFunctionDeclaration.modifiers.removingMutatingKeyword + spyFunctionDeclaration.modifiers = DeclModifierListSyntax { + for protoModifier in modifiers { + protoModifier + } + for functionModifier in protocolFunctionDeclaration.modifiers.removingMutatingKeyword { + functionModifier + } + } spyFunctionDeclaration.body = CodeBlockSyntax { let parameterList = protocolFunctionDeclaration.signature.parameterClause.parameters diff --git a/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift b/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift index 3a43f6b..5a6d357 100644 --- a/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift +++ b/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift @@ -37,6 +37,7 @@ import SwiftSyntaxBuilder /// and an argument `variablePrefix` equal to `bar`. struct ReceivedArgumentsFactory { func variableDeclaration( + modifiers: DeclModifierListSyntax, variablePrefix: String, parameterList: FunctionParameterListSyntax ) throws -> VariableDeclSyntax { @@ -46,11 +47,13 @@ struct ReceivedArgumentsFactory { ) let type = variableType(parameterList: parameterList) - return try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ var \(identifier): \(type) """ ) + decl.modifiers = modifiers + return decl } private func variableType(parameterList: FunctionParameterListSyntax) -> TypeSyntaxProtocol { diff --git a/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift b/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift index 9e966c7..c39b97e 100644 --- a/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift +++ b/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift @@ -42,17 +42,20 @@ import SwiftSyntaxBuilder /// about the arguments in the last invocation, use `ReceivedArgumentsFactory`. struct ReceivedInvocationsFactory { func variableDeclaration( + modifiers: DeclModifierListSyntax, variablePrefix: String, parameterList: FunctionParameterListSyntax ) throws -> VariableDeclSyntax { let identifier = variableIdentifier(variablePrefix: variablePrefix) let elementType = arrayElementType(parameterList: parameterList) - return try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ var \(identifier): [\(elementType)] = [] """ ) + decl.modifiers = modifiers + return decl } private func arrayElementType(parameterList: FunctionParameterListSyntax) -> TypeSyntaxProtocol { diff --git a/Sources/SpyableMacro/Factories/ReturnValueFactory.swift b/Sources/SpyableMacro/Factories/ReturnValueFactory.swift index e8d285a..ba7b344 100644 --- a/Sources/SpyableMacro/Factories/ReturnValueFactory.swift +++ b/Sources/SpyableMacro/Factories/ReturnValueFactory.swift @@ -39,6 +39,7 @@ import SwiftSyntaxBuilder /// correctly to different returned values. struct ReturnValueFactory { func variableDeclaration( + modifiers: DeclModifierListSyntax, variablePrefix: String, functionReturnType: TypeSyntax ) throws -> VariableDeclSyntax { @@ -51,11 +52,13 @@ struct ReturnValueFactory { ) } - return try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix))\(typeAnnotation) """ ) + decl.modifiers = modifiers + return decl } func returnStatement(variablePrefix: String) -> StmtSyntax { diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index de9d447..88a0928 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -129,37 +129,45 @@ struct SpyFactory { let variablePrefix = variablePrefixFactory.text(for: functionDeclaration) let parameterList = functionDeclaration.signature.parameterClause.parameters - try callsCountFactory.variableDeclaration(variablePrefix: variablePrefix) - try calledFactory.variableDeclaration(variablePrefix: variablePrefix) + try callsCountFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix) + try calledFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix) if parameterList.supportsParameterTracking { try receivedArgumentsFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix, parameterList: parameterList ) try receivedInvocationsFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix, parameterList: parameterList ) } if functionDeclaration.signature.effectSpecifiers?.throwsSpecifier != nil { - try throwableErrorFactory.variableDeclaration(variablePrefix: variablePrefix) + try throwableErrorFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix) } if let returnType = functionDeclaration.signature.returnClause?.type { try returnValueFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix, functionReturnType: returnType ) } try closureFactory.variableDeclaration( + modifiers: modifiers, variablePrefix: variablePrefix, functionSignature: functionDeclaration.signature ) functionImplementationFactory.declaration( + modifiers: modifiers, variablePrefix: variablePrefix, protocolFunctionDeclaration: functionDeclaration ) diff --git a/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift b/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift index 770c6d2..cbfe1e6 100644 --- a/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift +++ b/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift @@ -29,12 +29,16 @@ import SwiftSyntaxBuilder /// your tests. You can use it to simulate different scenarios and verify that your code handles /// errors correctly. struct ThrowableErrorFactory { - func variableDeclaration(variablePrefix: String) throws -> VariableDeclSyntax { - try VariableDeclSyntax( + func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws + -> VariableDeclSyntax + { + var decl = try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)): (any Error)? """ ) + decl.modifiers = modifiers + return decl } func throwErrorExpression(variablePrefix: String) -> ExprSyntax { diff --git a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift index 2c0d227..a39e65d 100644 --- a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift +++ b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift @@ -55,11 +55,15 @@ struct VariablesImplementationFactory { let binding = protocolVariableDeclaration.bindings.first! if binding.typeAnnotation?.type.is(OptionalTypeSyntax.self) == true { - accessorRemovalVisitor.visit(protocolVariableDeclaration) + if let variableDecl = accessorRemovalVisitor.visit(protocolVariableDeclaration).as( + VariableDeclSyntax.self) + { + variableDecl.settingModifiers(modifiers: modifiers) + } } else { try protocolVariableDeclarationWithGetterAndSetter(modifiers: modifiers, binding: binding) - try underlyingVariableDeclaration(binding: binding) + try underlyingVariableDeclaration(modifiers: modifiers, binding: binding) } } else { // As far as I know variable declaration in a protocol should have exactly one binding. @@ -71,24 +75,29 @@ struct VariablesImplementationFactory { modifiers: DeclModifierListSyntax, binding: PatternBindingSyntax ) throws -> VariableDeclSyntax { - try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ - \(modifiers)var \(binding.pattern.trimmed)\(binding.typeAnnotation!.trimmed) { + var \(binding.pattern.trimmed)\(binding.typeAnnotation!.trimmed) { get { \(raw: underlyingVariableName(binding: binding)) } set { \(raw: underlyingVariableName(binding: binding)) = newValue } } """ ) + decl.modifiers = modifiers + return decl } private func underlyingVariableDeclaration( + modifiers: DeclModifierListSyntax, binding: PatternBindingListSyntax.Element ) throws -> VariableDeclSyntax { - try VariableDeclSyntax( + var decl = try VariableDeclSyntax( """ var \(raw: underlyingVariableName(binding: binding)): (\(binding.typeAnnotation!.type.trimmed))! """ ) + decl.modifiers = modifiers + return decl } private func underlyingVariableName(binding: PatternBindingListSyntax.Element) throws -> String { @@ -103,6 +112,14 @@ struct VariablesImplementationFactory { } } +extension VariableDeclSyntax { + fileprivate func settingModifiers(modifiers: DeclModifierListSyntax) -> VariableDeclSyntax { + var copy = self + copy.modifiers = modifiers + return copy + } +} + private class AccessorRemovalVisitor: SyntaxRewriter { override func visit(_ node: PatternBindingSyntax) -> PatternBindingSyntax { let superResult = super.visit(node) diff --git a/Tests/SpyableMacroTests/Factories/UT_CalledFactory.swift b/Tests/SpyableMacroTests/Factories/UT_CalledFactory.swift index 3db8dee..659ed70 100644 --- a/Tests/SpyableMacroTests/Factories/UT_CalledFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_CalledFactory.swift @@ -10,7 +10,8 @@ final class UT_CalledFactory: XCTestCase { func testVariableDeclaration() throws { let variablePrefix = "functionName" - let result = try CalledFactory().variableDeclaration(variablePrefix: variablePrefix) + let result = try CalledFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix) assertBuildResult( result, @@ -21,4 +22,20 @@ final class UT_CalledFactory: XCTestCase { """ ) } + + func testVariableDeclarationWithAccess() throws { + let variablePrefix = "functionName" + + let result = try CalledFactory().variableDeclaration( + modifiers: [.init(name: "public")], variablePrefix: variablePrefix) + + assertBuildResult( + result, + """ + public var functionNameCalled: Bool { + return functionNameCallsCount > 0 + } + """ + ) + } } diff --git a/Tests/SpyableMacroTests/Factories/UT_CallsCountFactory.swift b/Tests/SpyableMacroTests/Factories/UT_CallsCountFactory.swift index ab8ea09..d20d2e8 100644 --- a/Tests/SpyableMacroTests/Factories/UT_CallsCountFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_CallsCountFactory.swift @@ -10,7 +10,8 @@ final class UT_CallsCountFactory: XCTestCase { func testVariableDeclaration() throws { let variablePrefix = "functionName" - let result = try CallsCountFactory().variableDeclaration(variablePrefix: variablePrefix) + let result = try CallsCountFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix) assertBuildResult( result, @@ -20,6 +21,20 @@ final class UT_CallsCountFactory: XCTestCase { ) } + func testVariableDeclarationWithAccess() throws { + let variablePrefix = "functionName" + + let result = try CallsCountFactory().variableDeclaration( + modifiers: [.init(name: "public")], variablePrefix: variablePrefix) + + assertBuildResult( + result, + """ + public var functionNameCallsCount = 0 + """ + ) + } + // MARK: - Variable Expression func testIncrementVariableExpression() { diff --git a/Tests/SpyableMacroTests/Factories/UT_ClosureFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ClosureFactory.swift index c9e71cb..7d57f3c 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ClosureFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ClosureFactory.swift @@ -136,6 +136,7 @@ final class UT_ClosureFactory: XCTestCase { let protocolFunctionDeclaration = try FunctionDeclSyntax("\(raw: functionDeclaration)") {} let result = try ClosureFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix, functionSignature: protocolFunctionDeclaration.signature ) diff --git a/Tests/SpyableMacroTests/Factories/UT_FunctionImplementationFactory.swift b/Tests/SpyableMacroTests/Factories/UT_FunctionImplementationFactory.swift index eafdefb..ea16570 100644 --- a/Tests/SpyableMacroTests/Factories/UT_FunctionImplementationFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_FunctionImplementationFactory.swift @@ -20,6 +20,19 @@ final class UT_FunctionImplementationFactory: XCTestCase { ) } + func testDeclarationAccessModifiers() throws { + try assertProtocolFunction( + withFunctionDeclaration: "public func foo()", + prefixForVariable: "_prefix_", + expectingFunctionDeclaration: """ + public func foo() { + _prefix_CallsCount += 1 + _prefix_Closure?() + } + """ + ) + } + func testDeclarationArguments() throws { try assertProtocolFunction( withFunctionDeclaration: "func foo(text: String, count: Int)", @@ -129,6 +142,7 @@ final class UT_FunctionImplementationFactory: XCTestCase { let protocolFunctionDeclaration = try FunctionDeclSyntax("\(raw: functionDeclaration)") {} let result = FunctionImplementationFactory().declaration( + modifiers: [], variablePrefix: variablePrefix, protocolFunctionDeclaration: protocolFunctionDeclaration ) diff --git a/Tests/SpyableMacroTests/Factories/UT_ReceivedArgumentsFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ReceivedArgumentsFactory.swift index 4189d55..3c906e9 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ReceivedArgumentsFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ReceivedArgumentsFactory.swift @@ -15,6 +15,14 @@ final class UT_ReceivedArgumentsFactory: XCTestCase { ) } + func testVariableDeclarationAccess() throws { + try assertProtocolFunction( + withFunctionDeclaration: "func foo(bar: String)", + prefixForVariable: "_prefix_", + expectingVariableDeclaration: "var _prefix_ReceivedBar: String?" + ) + } + func testVariableDeclarationSingleOptionalArgument() throws { try assertProtocolFunction( withFunctionDeclaration: "func foo(_ price: Decimal?)", @@ -157,6 +165,7 @@ final class UT_ReceivedArgumentsFactory: XCTestCase { let protocolFunctionDeclaration = try FunctionDeclSyntax("\(raw: functionDeclaration)") {} let result = try ReceivedArgumentsFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix, parameterList: protocolFunctionDeclaration.signature.parameterClause.parameters ) diff --git a/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift index a4bcb71..3e04f58 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift @@ -132,6 +132,7 @@ final class UT_ReceivedInvocationsFactory: XCTestCase { let protocolFunctionDeclaration = try FunctionDeclSyntax("\(raw: functionDeclaration)") {} let result = try ReceivedInvocationsFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix, parameterList: protocolFunctionDeclaration.signature.parameterClause.parameters ) diff --git a/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift index ab668b4..afef36c 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift @@ -48,6 +48,7 @@ final class UT_ReturnValueFactory: XCTestCase { line: UInt = #line ) throws { let result = try ReturnValueFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix, functionReturnType: functionReturnType ) diff --git a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift index 000f596..535eb98 100644 --- a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift @@ -251,7 +251,7 @@ final class UT_SpyFactory: XCTestCase { underlyingData = newValue } } - var underlyingData: (Data)! + public var underlyingData: (Data)! } """ ) diff --git a/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift index 0cb9439..3264305 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift @@ -10,7 +10,8 @@ final class UT_ThrowableErrorFactory: XCTestCase { func testVariableDeclaration() throws { let variablePrefix = "functionName" - let result = try ThrowableErrorFactory().variableDeclaration(variablePrefix: variablePrefix) + let result = try ThrowableErrorFactory().variableDeclaration( + modifiers: [], variablePrefix: variablePrefix) assertBuildResult( result, @@ -20,6 +21,22 @@ final class UT_ThrowableErrorFactory: XCTestCase { ) } + func testVariableDeclarationWithAccess() throws { + let variablePrefix = "functionName" + + let result = try ThrowableErrorFactory().variableDeclaration( + modifiers: [.init(name: .init(stringLiteral: "public"))], + variablePrefix: variablePrefix + ) + + assertBuildResult( + result, + """ + public var functionNameThrowableError: (any Error)? + """ + ) + } + // MARK: - Throw Error Expression func testThrowErrorExpression() { diff --git a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift index a19002b..2f35209 100644 --- a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift @@ -51,7 +51,7 @@ final class UT_VariablesImplementationFactory: XCTestCase { func testVariableDelcarationsWithAccess() throws { try assertProtocolVariable( withVariableDeclaration: "public var foo: String? { get }", - expectingVariableDeclaration: "public var foo: String?" + expectingVariableDeclaration: "var foo: String?" ) } diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index c25235d..3854f43 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -58,7 +58,7 @@ final class UT_SpyableMacro: XCTestCase { underlyingName = newValue } } - var underlyingName: (String)! + public var underlyingName: (String)! public var anyProtocol: any Codable { get { underlyingAnyProtocol @@ -67,7 +67,8 @@ final class UT_SpyableMacro: XCTestCase { underlyingAnyProtocol = newValue } } - var underlyingAnyProtocol: (any Codable)! + public var underlyingAnyProtocol: (any Codable)! + public var secondName: String? public var added: () -> Void { get { @@ -77,37 +78,40 @@ final class UT_SpyableMacro: XCTestCase { underlyingAdded = newValue } } - var underlyingAdded: (() -> Void)! + public var underlyingAdded: (() -> Void)! + public var removed: (() -> Void)? - var logoutCallsCount = 0 - var logoutCalled: Bool { + public var logoutCallsCount = 0 + public var logoutCalled: Bool { return logoutCallsCount > 0 } - var logoutClosure: (() -> Void)? - func logout() { + public var logoutClosure: (() -> Void)? + public func logout() { logoutCallsCount += 1 logoutClosure?() } - var initializeNameSecondNameCallsCount = 0 - var initializeNameSecondNameCalled: Bool { + public var initializeNameSecondNameCallsCount = 0 + public var initializeNameSecondNameCalled: Bool { return initializeNameSecondNameCallsCount > 0 } - var initializeNameSecondNameReceivedArguments: (name: String, secondName: String?)? - var initializeNameSecondNameReceivedInvocations: [(name: String, secondName: String?)] = [] - var initializeNameSecondNameClosure: ((String, String?) -> Void)? + public var initializeNameSecondNameReceivedArguments: (name: String, secondName: String?)? + public var initializeNameSecondNameReceivedInvocations: [(name: String, secondName: String?)] = [] + public var initializeNameSecondNameClosure: ((String, String?) -> Void)? + public func initialize(name: String, secondName: String?) { initializeNameSecondNameCallsCount += 1 initializeNameSecondNameReceivedArguments = (name, secondName) initializeNameSecondNameReceivedInvocations.append((name, secondName)) initializeNameSecondNameClosure?(name, secondName) } - var fetchConfigCallsCount = 0 - var fetchConfigCalled: Bool { + public var fetchConfigCallsCount = 0 + public var fetchConfigCalled: Bool { return fetchConfigCallsCount > 0 } - var fetchConfigThrowableError: (any Error)? - var fetchConfigReturnValue: [String: String]! - var fetchConfigClosure: (() async throws -> [String: String])? + public var fetchConfigThrowableError: (any Error)? + public var fetchConfigReturnValue: [String: String]! + public var fetchConfigClosure: (() async throws -> [String: String])? + public func fetchConfig() async throws -> [String: String] { fetchConfigCallsCount += 1 if let fetchConfigThrowableError { @@ -119,14 +123,15 @@ final class UT_SpyableMacro: XCTestCase { return fetchConfigReturnValue } } - var fetchDataCallsCount = 0 - var fetchDataCalled: Bool { + public var fetchDataCallsCount = 0 + public var fetchDataCalled: Bool { return fetchDataCallsCount > 0 } - var fetchDataReceivedName: (String, count: Int)? - var fetchDataReceivedInvocations: [(String, count: Int)] = [] - var fetchDataReturnValue: (() -> Void)! - var fetchDataClosure: (((String, count: Int)) async -> (() -> Void))? + public var fetchDataReceivedName: (String, count: Int)? + public var fetchDataReceivedInvocations: [(String, count: Int)] = [] + public var fetchDataReturnValue: (() -> Void)! + public var fetchDataClosure: (((String, count: Int)) async -> (() -> Void))? + public func fetchData(_ name: (String, count: Int)) async -> (() -> Void) { fetchDataCallsCount += 1 fetchDataReceivedName = (name) @@ -137,42 +142,46 @@ final class UT_SpyableMacro: XCTestCase { return fetchDataReturnValue } } - var fetchUsernameContextCompletionCallsCount = 0 - var fetchUsernameContextCompletionCalled: Bool { + public var fetchUsernameContextCompletionCallsCount = 0 + public var fetchUsernameContextCompletionCalled: Bool { return fetchUsernameContextCompletionCallsCount > 0 } - var fetchUsernameContextCompletionReceivedArguments: (context: String, completion: (String) -> Void)? - var fetchUsernameContextCompletionReceivedInvocations: [(context: String, completion: (String) -> Void)] = [] - var fetchUsernameContextCompletionClosure: ((String, @escaping (String) -> Void) -> Void)? + public var fetchUsernameContextCompletionReceivedArguments: (context: String, completion: (String) -> Void)? + public var fetchUsernameContextCompletionReceivedInvocations: [(context: String, completion: (String) -> Void)] = [] + public var fetchUsernameContextCompletionClosure: ((String, @escaping (String) -> Void) -> Void)? + public func fetchUsername(context: String, completion: @escaping (String) -> Void) { fetchUsernameContextCompletionCallsCount += 1 fetchUsernameContextCompletionReceivedArguments = (context, completion) fetchUsernameContextCompletionReceivedInvocations.append((context, completion)) fetchUsernameContextCompletionClosure?(context, completion) } - var onTapBackContextActionCallsCount = 0 - var onTapBackContextActionCalled: Bool { + public var onTapBackContextActionCallsCount = 0 + public var onTapBackContextActionCalled: Bool { return onTapBackContextActionCallsCount > 0 } - var onTapBackContextActionClosure: ((String, () -> Void) -> Void)? + public var onTapBackContextActionClosure: ((String, () -> Void) -> Void)? + public func onTapBack(context: String, action: () -> Void) { onTapBackContextActionCallsCount += 1 onTapBackContextActionClosure?(context, action) } - var onTapNextContextActionCallsCount = 0 - var onTapNextContextActionCalled: Bool { + public var onTapNextContextActionCallsCount = 0 + public var onTapNextContextActionCalled: Bool { return onTapNextContextActionCallsCount > 0 } - var onTapNextContextActionClosure: ((String, @Sendable () -> Void) -> Void)? + public var onTapNextContextActionClosure: ((String, @Sendable () -> Void) -> Void)? + public func onTapNext(context: String, action: @Sendable () -> Void) { onTapNextContextActionCallsCount += 1 onTapNextContextActionClosure?(context, action) } - var assertCallsCount = 0 - var assertCalled: Bool { + public var assertCallsCount = 0 + public var assertCalled: Bool { return assertCallsCount > 0 } - var assertClosure: ((@autoclosure () -> String) -> Void)? + public var assertClosure: ((@autoclosure () -> String) -> Void)? + public func assert(_ message: @autoclosure () -> String) { assertCallsCount += 1 assertClosure?(message()) @@ -183,7 +192,31 @@ final class UT_SpyableMacro: XCTestCase { ) } - func testMacroWithFlag() { + func testMacroOtherAccessLevel() { + let protocolDeclaration = """ + package protocol ServiceProtocol { + var variable: Bool? { get set } + } + """ + assertMacroExpansion( + """ + @Spyable + \(protocolDeclaration) + """, + expandedSource: """ + + \(protocolDeclaration) + + package class ServiceProtocolSpy: ServiceProtocol { + package + var variable: Bool? + } + """, + macros: sut + ) + } + + func testMacroWithCustomFlag() { let protocolDeclaration = """ public protocol ServiceProtocol { var variable: Bool? { get set } @@ -200,6 +233,7 @@ final class UT_SpyableMacro: XCTestCase { #if CUSTOM public class ServiceProtocolSpy: ServiceProtocol { + public var variable: Bool? } #endif @@ -224,6 +258,7 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) public class ServiceProtocolSpy: ServiceProtocol { + public var variable: Bool? } """, From cafe0a0b11c0137bf56f41f7212d68a3ba3cdfb2 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Wed, 17 Jan 2024 12:38:55 +0000 Subject: [PATCH 04/12] Generate init for spies --- .../SpyableMacro/Factories/SpyFactory.swift | 6 ++++ .../Factories/UT_SpyFactory.swift | 28 +++++++++++++++++++ .../Macro/UT_SpyableMacro.swift | 10 +++++++ 3 files changed, 44 insertions(+) diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index 88a0928..25671c9 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -118,6 +118,12 @@ struct SpyFactory { ) }, memberBlockBuilder: { + InitializerDeclSyntax( + modifiers: modifiers, + signature: FunctionSignatureSyntax(parameterClause: .init(parameters: [])), + body: CodeBlockSyntax(statements: []) + ) + for variableDeclaration in variableDeclarations { try variablesImplementationFactory.variablesDeclarations( modifiers: modifiers, diff --git a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift index 535eb98..5870476 100644 --- a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift @@ -10,6 +10,8 @@ final class UT_SpyFactory: XCTestCase { withDeclaration: "protocol Foo {}", expectingClassDeclaration: """ class FooSpy: Foo { + init() { + } } """ ) @@ -24,6 +26,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceSpy: Service { + init() { + } var fetchCallsCount = 0 var fetchCalled: Bool { return fetchCallsCount > 0 @@ -47,6 +51,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ViewModelProtocolSpy: ViewModelProtocol { + init() { + } var fooTextCountCallsCount = 0 var fooTextCountCalled: Bool { return fooTextCountCallsCount > 0 @@ -74,6 +80,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ViewModelProtocolSpy: ViewModelProtocol { + init() { + } var fooActionCallsCount = 0 var fooActionCalled: Bool { return fooActionCallsCount > 0 @@ -101,6 +109,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ViewModelProtocolSpy: ViewModelProtocol { + init() { + } var fooActionCallsCount = 0 var fooActionCalled: Bool { return fooActionCallsCount > 0 @@ -124,6 +134,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class BarSpy: Bar { + init() { + } var printCallsCount = 0 var printCalled: Bool { return printCallsCount > 0 @@ -152,6 +164,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceProtocolSpy: ServiceProtocol { + init() { + } var fooTextCountCallsCount = 0 var fooTextCountCalled: Bool { return fooTextCountCallsCount > 0 @@ -184,6 +198,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceProtocolSpy: ServiceProtocol { + init() { + } var fooCallsCount = 0 var fooCalled: Bool { return fooCallsCount > 0 @@ -220,6 +236,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceProtocolSpy: ServiceProtocol { + init() { + } var data: Data { get { underlyingData @@ -243,6 +261,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ public class ServiceProtocolSpy: ServiceProtocol { + public init() { + } public var data: Data { get { underlyingData @@ -266,6 +286,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceProtocolSpy: ServiceProtocol { + init() { + } var data: Data? } """ @@ -281,6 +303,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class ServiceProtocolSpy: ServiceProtocol { + init() { + } var completion: () -> Void { get { underlyingCompletion @@ -306,6 +330,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class FooSpy: Foo { + init() { + } } """ ) @@ -321,6 +347,8 @@ final class UT_SpyFactory: XCTestCase { """, expectingClassDeclaration: """ class FooSpy: Foo { + init() { + } } """ ) diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index 3854f43..73a71c5 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -50,6 +50,8 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) public class ServiceProtocolSpy: ServiceProtocol { + public init() { + } public var name: String { get { underlyingName @@ -208,6 +210,8 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) package class ServiceProtocolSpy: ServiceProtocol { + package init() { + } package var variable: Bool? } @@ -233,6 +237,8 @@ final class UT_SpyableMacro: XCTestCase { #if CUSTOM public class ServiceProtocolSpy: ServiceProtocol { + public init() { + } public var variable: Bool? } @@ -258,6 +264,8 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) public class ServiceProtocolSpy: ServiceProtocol { + public init() { + } public var variable: Bool? } @@ -282,6 +290,8 @@ final class UT_SpyableMacro: XCTestCase { \(protocolDeclaration) class ServiceProtocolSpy: ServiceProtocol { + init() { + } var variable: Bool? } """, From ad6525dfdb30194eb6f8626ae755db71f73147b5 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:43:13 +0000 Subject: [PATCH 05/12] Simplify applying of modifiers in VariableDeclSyntax --- .../Factories/VariablesImplementationFactory.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift index a39e65d..4b75dc4 100644 --- a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift +++ b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift @@ -58,7 +58,7 @@ struct VariablesImplementationFactory { if let variableDecl = accessorRemovalVisitor.visit(protocolVariableDeclaration).as( VariableDeclSyntax.self) { - variableDecl.settingModifiers(modifiers: modifiers) + variableDecl.applying(modifiers: modifiers) } } else { try protocolVariableDeclarationWithGetterAndSetter(modifiers: modifiers, binding: binding) @@ -75,7 +75,7 @@ struct VariablesImplementationFactory { modifiers: DeclModifierListSyntax, binding: PatternBindingSyntax ) throws -> VariableDeclSyntax { - var decl = try VariableDeclSyntax( + try VariableDeclSyntax( """ var \(binding.pattern.trimmed)\(binding.typeAnnotation!.trimmed) { get { \(raw: underlyingVariableName(binding: binding)) } @@ -83,21 +83,19 @@ struct VariablesImplementationFactory { } """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } private func underlyingVariableDeclaration( modifiers: DeclModifierListSyntax, binding: PatternBindingListSyntax.Element ) throws -> VariableDeclSyntax { - var decl = try VariableDeclSyntax( + try VariableDeclSyntax( """ var \(raw: underlyingVariableName(binding: binding)): (\(binding.typeAnnotation!.type.trimmed))! """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } private func underlyingVariableName(binding: PatternBindingListSyntax.Element) throws -> String { @@ -113,7 +111,7 @@ struct VariablesImplementationFactory { } extension VariableDeclSyntax { - fileprivate func settingModifiers(modifiers: DeclModifierListSyntax) -> VariableDeclSyntax { + fileprivate func applying(modifiers: DeclModifierListSyntax) -> VariableDeclSyntax { var copy = self copy.modifiers = modifiers return copy From d0cb0365bd8505985333859e926971d31779a7b2 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:47:10 +0000 Subject: [PATCH 06/12] Simplify all variabledeclsyntax modifiers application --- .../Extensions/VariableDeclSyntax+Applying.swift | 9 +++++++++ Sources/SpyableMacro/Factories/CalledFactory.swift | 5 ++--- Sources/SpyableMacro/Factories/CallsCountFactory.swift | 5 ++--- Sources/SpyableMacro/Factories/ClosureFactory.swift | 5 ++--- .../Factories/ReceivedArgumentsFactory.swift | 5 ++--- .../Factories/ReceivedInvocationsFactory.swift | 5 ++--- Sources/SpyableMacro/Factories/ReturnValueFactory.swift | 5 ++--- .../SpyableMacro/Factories/ThrowableErrorFactory.swift | 5 ++--- .../Factories/VariablesImplementationFactory.swift | 8 -------- 9 files changed, 23 insertions(+), 29 deletions(-) create mode 100644 Sources/SpyableMacro/Extensions/VariableDeclSyntax+Applying.swift diff --git a/Sources/SpyableMacro/Extensions/VariableDeclSyntax+Applying.swift b/Sources/SpyableMacro/Extensions/VariableDeclSyntax+Applying.swift new file mode 100644 index 0000000..d6cc385 --- /dev/null +++ b/Sources/SpyableMacro/Extensions/VariableDeclSyntax+Applying.swift @@ -0,0 +1,9 @@ +import SwiftSyntax + +extension VariableDeclSyntax { + func applying(modifiers: DeclModifierListSyntax) -> VariableDeclSyntax { + var copy = self + copy.modifiers = modifiers + return copy + } +} diff --git a/Sources/SpyableMacro/Factories/CalledFactory.swift b/Sources/SpyableMacro/Factories/CalledFactory.swift index 88ffad6..ad75b62 100644 --- a/Sources/SpyableMacro/Factories/CalledFactory.swift +++ b/Sources/SpyableMacro/Factories/CalledFactory.swift @@ -28,14 +28,13 @@ struct CalledFactory { func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws -> VariableDeclSyntax { - var decl = try VariableDeclSyntax( + try VariableDeclSyntax( """ var \(raw: variablePrefix)Called: Bool { return \(raw: variablePrefix)CallsCount > 0 } """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } } diff --git a/Sources/SpyableMacro/Factories/CallsCountFactory.swift b/Sources/SpyableMacro/Factories/CallsCountFactory.swift index 06bc2cf..78fceaf 100644 --- a/Sources/SpyableMacro/Factories/CallsCountFactory.swift +++ b/Sources/SpyableMacro/Factories/CallsCountFactory.swift @@ -24,13 +24,12 @@ struct CallsCountFactory { func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws -> VariableDeclSyntax { - var decl = try VariableDeclSyntax( + try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)) = 0 """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } func incrementVariableExpression(variablePrefix: String) -> ExprSyntax { diff --git a/Sources/SpyableMacro/Factories/ClosureFactory.swift b/Sources/SpyableMacro/Factories/ClosureFactory.swift index 84522ed..89f5736 100644 --- a/Sources/SpyableMacro/Factories/ClosureFactory.swift +++ b/Sources/SpyableMacro/Factories/ClosureFactory.swift @@ -55,13 +55,12 @@ struct ClosureFactory { ) } - var decl = try VariableDeclSyntax( + return try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)): (\(elements))? """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } func callExpression( diff --git a/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift b/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift index 5a6d357..ac3292a 100644 --- a/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift +++ b/Sources/SpyableMacro/Factories/ReceivedArgumentsFactory.swift @@ -47,13 +47,12 @@ struct ReceivedArgumentsFactory { ) let type = variableType(parameterList: parameterList) - var decl = try VariableDeclSyntax( + return try VariableDeclSyntax( """ var \(identifier): \(type) """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } private func variableType(parameterList: FunctionParameterListSyntax) -> TypeSyntaxProtocol { diff --git a/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift b/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift index c39b97e..250c987 100644 --- a/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift +++ b/Sources/SpyableMacro/Factories/ReceivedInvocationsFactory.swift @@ -49,13 +49,12 @@ struct ReceivedInvocationsFactory { let identifier = variableIdentifier(variablePrefix: variablePrefix) let elementType = arrayElementType(parameterList: parameterList) - var decl = try VariableDeclSyntax( + return try VariableDeclSyntax( """ var \(identifier): [\(elementType)] = [] """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } private func arrayElementType(parameterList: FunctionParameterListSyntax) -> TypeSyntaxProtocol { diff --git a/Sources/SpyableMacro/Factories/ReturnValueFactory.swift b/Sources/SpyableMacro/Factories/ReturnValueFactory.swift index ba7b344..638f333 100644 --- a/Sources/SpyableMacro/Factories/ReturnValueFactory.swift +++ b/Sources/SpyableMacro/Factories/ReturnValueFactory.swift @@ -52,13 +52,12 @@ struct ReturnValueFactory { ) } - var decl = try VariableDeclSyntax( + return try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix))\(typeAnnotation) """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } func returnStatement(variablePrefix: String) -> StmtSyntax { diff --git a/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift b/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift index cbfe1e6..28ac906 100644 --- a/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift +++ b/Sources/SpyableMacro/Factories/ThrowableErrorFactory.swift @@ -32,13 +32,12 @@ struct ThrowableErrorFactory { func variableDeclaration(modifiers: DeclModifierListSyntax, variablePrefix: String) throws -> VariableDeclSyntax { - var decl = try VariableDeclSyntax( + try VariableDeclSyntax( """ var \(variableIdentifier(variablePrefix: variablePrefix)): (any Error)? """ ) - decl.modifiers = modifiers - return decl + .applying(modifiers: modifiers) } func throwErrorExpression(variablePrefix: String) -> ExprSyntax { diff --git a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift index 4b75dc4..ab54536 100644 --- a/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift +++ b/Sources/SpyableMacro/Factories/VariablesImplementationFactory.swift @@ -110,14 +110,6 @@ struct VariablesImplementationFactory { } } -extension VariableDeclSyntax { - fileprivate func applying(modifiers: DeclModifierListSyntax) -> VariableDeclSyntax { - var copy = self - copy.modifiers = modifiers - return copy - } -} - private class AccessorRemovalVisitor: SyntaxRewriter { override func visit(_ node: PatternBindingSyntax) -> PatternBindingSyntax { let superResult = super.visit(node) From 6222e7504e1ee906610830b6fe74400687c2bdc4 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:49:50 +0000 Subject: [PATCH 07/12] Test ReturnValueFactory --- .../Factories/UT_ReturnValueFactory.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift index afef36c..e3bc51d 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ReturnValueFactory.swift @@ -15,6 +15,15 @@ final class UT_ReturnValueFactory: XCTestCase { ) } + func testVariableDeclarationModifiers() throws { + try assert( + functionReturnType: "(text: String, count: UInt)", + prefixForVariable: "_prefix_", + modifiers: [.init(name: "public")], + expectingVariableDeclaration: "public var _prefix_ReturnValue: (text: String, count: UInt)!" + ) + } + func testVariableDeclarationOptionType() throws { try assert( functionReturnType: "String?", @@ -43,12 +52,13 @@ final class UT_ReturnValueFactory: XCTestCase { private func assert( functionReturnType: TypeSyntax, prefixForVariable variablePrefix: String, + modifiers: DeclModifierListSyntax = [], expectingVariableDeclaration expectedDeclaration: String, file: StaticString = #file, line: UInt = #line ) throws { let result = try ReturnValueFactory().variableDeclaration( - modifiers: [], + modifiers: modifiers, variablePrefix: variablePrefix, functionReturnType: functionReturnType ) From 7fc1c7317d55061ef9af2a4926cb111a063fb7a8 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:51:23 +0000 Subject: [PATCH 08/12] Test ReceivedInvocationsFactory --- .../Factories/UT_ReceivedInvocationsFactory.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift index 3e04f58..5669733 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ReceivedInvocationsFactory.swift @@ -15,6 +15,15 @@ final class UT_ReceivedInvocationsFactory: XCTestCase { ) } + func testVariableDeclarationWithModifiers() throws { + try assertProtocolFunction( + withFunctionDeclaration: "func foo(bar: String?)", + prefixForVariable: "_prefix_", + modifiers: [.init(name: "fileprivate")], + expectingVariableDeclaration: "fileprivate var _prefix_ReceivedInvocations: [String?] = []" + ) + } + func testVariableDeclarationSingleArgumentTupleType() throws { try assertProtocolFunction( withFunctionDeclaration: "func foo(_ tuple: (text: String, (Decimal?, date: Date))?)", @@ -125,6 +134,7 @@ final class UT_ReceivedInvocationsFactory: XCTestCase { private func assertProtocolFunction( withFunctionDeclaration functionDeclaration: String, prefixForVariable variablePrefix: String, + modifiers: DeclModifierListSyntax = [], expectingVariableDeclaration expectedDeclaration: String, file: StaticString = #file, line: UInt = #line @@ -132,7 +142,7 @@ final class UT_ReceivedInvocationsFactory: XCTestCase { let protocolFunctionDeclaration = try FunctionDeclSyntax("\(raw: functionDeclaration)") {} let result = try ReceivedInvocationsFactory().variableDeclaration( - modifiers: [], + modifiers: modifiers, variablePrefix: variablePrefix, parameterList: protocolFunctionDeclaration.signature.parameterClause.parameters ) From 9498b4e7281723afe5340a93db925efb7a3fa9c8 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:54:27 +0000 Subject: [PATCH 09/12] Simplify test decl --- .../SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift b/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift index 3264305..fb9c2ef 100644 --- a/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_ThrowableErrorFactory.swift @@ -25,7 +25,7 @@ final class UT_ThrowableErrorFactory: XCTestCase { let variablePrefix = "functionName" let result = try ThrowableErrorFactory().variableDeclaration( - modifiers: [.init(name: .init(stringLiteral: "public"))], + modifiers: [.init(name: "public")], variablePrefix: variablePrefix ) From 068977144e9faba4068f34a15e1c378663893d3d Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:54:37 +0000 Subject: [PATCH 10/12] Fix variable declaration test --- .../Factories/UT_VariablesImplementationFactory.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift index 2f35209..4588412 100644 --- a/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_VariablesImplementationFactory.swift @@ -48,10 +48,11 @@ final class UT_VariablesImplementationFactory: XCTestCase { ) } - func testVariableDelcarationsWithAccess() throws { + func testVariableDelcarationsWithModifiers() throws { try assertProtocolVariable( - withVariableDeclaration: "public var foo: String? { get }", - expectingVariableDeclaration: "var foo: String?" + withVariableDeclaration: "var foo: String? { get }", + modifiers: [.init(name: "fileprivate")], + expectingVariableDeclaration: "fileprivate var foo: String?" ) } @@ -91,6 +92,7 @@ final class UT_VariablesImplementationFactory: XCTestCase { private func assertProtocolVariable( withVariableDeclaration variableDeclaration: String, + modifiers: DeclModifierListSyntax = [], expectingVariableDeclaration expectedDeclaration: String, file: StaticString = #file, line: UInt = #line @@ -98,7 +100,7 @@ final class UT_VariablesImplementationFactory: XCTestCase { let protocolVariableDeclaration = try VariableDeclSyntax("\(raw: variableDeclaration)") let result = try VariablesImplementationFactory().variablesDeclarations( - modifiers: [], + modifiers: modifiers, protocolVariableDeclaration: protocolVariableDeclaration ) From b2d9e385c63e60e1a982304ad8369c07b05e5d1d Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 11:58:24 +0000 Subject: [PATCH 11/12] Remove macro tests for access level These should be in the spy factory tests. --- .../Macro/UT_SpyableMacro.swift | 53 +------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift index 73a71c5..b635277 100644 --- a/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift +++ b/Tests/SpyableMacroTests/Macro/UT_SpyableMacro.swift @@ -194,32 +194,6 @@ final class UT_SpyableMacro: XCTestCase { ) } - func testMacroOtherAccessLevel() { - let protocolDeclaration = """ - package protocol ServiceProtocol { - var variable: Bool? { get set } - } - """ - assertMacroExpansion( - """ - @Spyable - \(protocolDeclaration) - """, - expandedSource: """ - - \(protocolDeclaration) - - package class ServiceProtocolSpy: ServiceProtocol { - package init() { - } - package - var variable: Bool? - } - """, - macros: sut - ) - } - func testMacroWithCustomFlag() { let protocolDeclaration = """ public protocol ServiceProtocol { @@ -256,7 +230,7 @@ final class UT_SpyableMacro: XCTestCase { """ assertMacroExpansion( """ - @Spyable(behindPreprocessorFlag: nil) + @Spyable \(protocolDeclaration) """, expandedSource: """ @@ -273,29 +247,4 @@ final class UT_SpyableMacro: XCTestCase { macros: sut ) } - - func testMacroInternal() { - let protocolDeclaration = """ - protocol ServiceProtocol { - var variable: Bool? { get set } - } - """ - assertMacroExpansion( - """ - @Spyable(behindPreprocessorFlag: nil) - \(protocolDeclaration) - """, - expandedSource: """ - - \(protocolDeclaration) - - class ServiceProtocolSpy: ServiceProtocol { - init() { - } - var variable: Bool? - } - """, - macros: sut - ) - } } From 13b69a5066a2c1a7663285d8aea5831a506179b8 Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Fri, 19 Jan 2024 12:07:22 +0000 Subject: [PATCH 12/12] Private protocol members are fileprivate --- .../SpyableMacro/Factories/SpyFactory.swift | 38 ++++++--- .../Factories/UT_SpyFactory.swift | 84 +++++++++++++++---- 2 files changed, 94 insertions(+), 28 deletions(-) diff --git a/Sources/SpyableMacro/Factories/SpyFactory.swift b/Sources/SpyableMacro/Factories/SpyFactory.swift index 25671c9..1cee291 100644 --- a/Sources/SpyableMacro/Factories/SpyFactory.swift +++ b/Sources/SpyableMacro/Factories/SpyFactory.swift @@ -93,7 +93,6 @@ struct SpyFactory { private let functionImplementationFactory = FunctionImplementationFactory() func classDeclaration(for protocolDeclaration: ProtocolDeclSyntax) throws -> ClassDeclSyntax { - let modifiers = protocolDeclaration.modifiers let identifier = TokenSyntax.identifier(protocolDeclaration.name.text + "Spy") let assosciatedtypeDeclarations = protocolDeclaration.memberBlock.members.compactMap { @@ -108,8 +107,10 @@ struct SpyFactory { let functionDeclarations = protocolDeclaration.memberBlock.members .compactMap { $0.decl.as(FunctionDeclSyntax.self)?.removingLeadingSpaces } + let memberModifiers = protocolDeclaration.modifiers.replacingPrivateWithFileprivate + return try ClassDeclSyntax( - modifiers: modifiers, + modifiers: protocolDeclaration.modifiers, name: identifier, genericParameterClause: genericParameterClause, inheritanceClause: InheritanceClauseSyntax { @@ -119,14 +120,14 @@ struct SpyFactory { }, memberBlockBuilder: { InitializerDeclSyntax( - modifiers: modifiers, + modifiers: memberModifiers, signature: FunctionSignatureSyntax(parameterClause: .init(parameters: [])), body: CodeBlockSyntax(statements: []) ) for variableDeclaration in variableDeclarations { try variablesImplementationFactory.variablesDeclarations( - modifiers: modifiers, + modifiers: memberModifiers, protocolVariableDeclaration: variableDeclaration ) } @@ -136,18 +137,18 @@ struct SpyFactory { let parameterList = functionDeclaration.signature.parameterClause.parameters try callsCountFactory.variableDeclaration( - modifiers: modifiers, variablePrefix: variablePrefix) + modifiers: memberModifiers, variablePrefix: variablePrefix) try calledFactory.variableDeclaration( - modifiers: modifiers, variablePrefix: variablePrefix) + modifiers: memberModifiers, variablePrefix: variablePrefix) if parameterList.supportsParameterTracking { try receivedArgumentsFactory.variableDeclaration( - modifiers: modifiers, + modifiers: memberModifiers, variablePrefix: variablePrefix, parameterList: parameterList ) try receivedInvocationsFactory.variableDeclaration( - modifiers: modifiers, + modifiers: memberModifiers, variablePrefix: variablePrefix, parameterList: parameterList ) @@ -155,25 +156,25 @@ struct SpyFactory { if functionDeclaration.signature.effectSpecifiers?.throwsSpecifier != nil { try throwableErrorFactory.variableDeclaration( - modifiers: modifiers, variablePrefix: variablePrefix) + modifiers: memberModifiers, variablePrefix: variablePrefix) } if let returnType = functionDeclaration.signature.returnClause?.type { try returnValueFactory.variableDeclaration( - modifiers: modifiers, + modifiers: memberModifiers, variablePrefix: variablePrefix, functionReturnType: returnType ) } try closureFactory.variableDeclaration( - modifiers: modifiers, + modifiers: memberModifiers, variablePrefix: variablePrefix, functionSignature: functionDeclaration.signature ) functionImplementationFactory.declaration( - modifiers: modifiers, + modifiers: memberModifiers, variablePrefix: variablePrefix, protocolFunctionDeclaration: functionDeclaration ) @@ -202,3 +203,16 @@ extension SyntaxProtocol { ) } } + +extension DeclModifierListSyntax { + fileprivate var replacingPrivateWithFileprivate: Self { + DeclModifierListSyntax( + map { + if $0.name.text == TokenSyntax.keyword(.private).text { + return .init(name: .keyword(.fileprivate)) + } else { + return $0 + } + }) + } +} diff --git a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift index 5870476..7c7dc9b 100644 --- a/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift +++ b/Tests/SpyableMacroTests/Factories/UT_SpyFactory.swift @@ -252,7 +252,51 @@ final class UT_SpyFactory: XCTestCase { ) } - func testDeclarationVariablePublic() throws { + func testDeclarationOptionalVariable() throws { + try assertProtocol( + withDeclaration: """ + protocol ServiceProtocol { + var data: Data? { get set } + } + """, + expectingClassDeclaration: """ + class ServiceProtocolSpy: ServiceProtocol { + init() { + } + var data: Data? + } + """ + ) + } + + func testDeclarationClosureVariable() throws { + try assertProtocol( + withDeclaration: """ + protocol ServiceProtocol { + var completion: () -> Void { get set } + } + """, + expectingClassDeclaration: """ + class ServiceProtocolSpy: ServiceProtocol { + init() { + } + var completion: () -> Void { + get { + underlyingCompletion + } + set { + underlyingCompletion = newValue + } + } + var underlyingCompletion: (() -> Void)! + } + """ + ) + } + + // MARK: - Access Level + + func testDeclarationPublicAccess() throws { try assertProtocol( withDeclaration: """ public protocol ServiceProtocol { @@ -277,43 +321,51 @@ final class UT_SpyFactory: XCTestCase { ) } - func testDeclarationOptionalVariable() throws { + func testDeclarationPackageAccess() throws { try assertProtocol( withDeclaration: """ - protocol ServiceProtocol { - var data: Data? { get set } + package protocol ServiceProtocol { + var data: Data { get } } """, expectingClassDeclaration: """ - class ServiceProtocolSpy: ServiceProtocol { - init() { + package class ServiceProtocolSpy: ServiceProtocol { + package init() { } - var data: Data? + package var data: Data { + get { + underlyingData + } + set { + underlyingData = newValue + } + } + package var underlyingData: (Data)! } """ ) } - func testDeclarationClosureVariable() throws { + func testDeclarationPrivateAccess() throws { try assertProtocol( withDeclaration: """ - protocol ServiceProtocol { - var completion: () -> Void { get set } + private protocol ServiceProtocol { + var data: Data { get } } """, expectingClassDeclaration: """ - class ServiceProtocolSpy: ServiceProtocol { - init() { + private class ServiceProtocolSpy: ServiceProtocol { + fileprivate init() { } - var completion: () -> Void { + fileprivate var data: Data { get { - underlyingCompletion + underlyingData } set { - underlyingCompletion = newValue + underlyingData = newValue } } - var underlyingCompletion: (() -> Void)! + fileprivate var underlyingData: (Data)! } """ )