From 6d3c566c4b122a55eded5811b1185c5c63e6c7a4 Mon Sep 17 00:00:00 2001 From: Nayanda Haberty Date: Sat, 20 Apr 2024 11:28:59 +0700 Subject: [PATCH] Protocol stub and instance adjustment (#11) * Fix member macro not working * Make stub instance as computed property * Remove given name argument that violate Swift Macros spec * Update unit test * update README * Refactor to satisfy linter --- README.md | 6 +-- Sources/SwiftEnvironment/Macros.swift | 9 +++- .../Model/StubInitializer.swift | 6 +-- .../StubGeneratorMacro+PeerMacro.swift | 12 +----- .../DummyDependency.swift | 12 +++++- .../IntegrationTests.swift | 5 +++ .../StubFromProtocolGeneratorMacroTests.swift | 43 ++++--------------- .../StubFromTypeGeneratorTests.swift | 24 ++++++++--- 8 files changed, 56 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 97cfdf1..80b723d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ This library is designed to allow easier dependency management, using exisiting ```swift // protocol with stub -@Stubbed +@Stubbed(type: .class) protocol MyProtocol { func doSomething() } @@ -139,7 +139,7 @@ The `Stubbed` macro simplifies the creation of stubs from protocols, reducing bo ```swift import SwiftEnvironment -@Stubbed +@Stubbed(type: .struct) protocol MyProtocol { var someValue: Int { get } func calculate(someValue: Int) -> Int @@ -161,7 +161,7 @@ If the return type of the protocol's methods or variables is unknown or you want ```swift import SwiftEnvironment -@Stubbed(.value(for: MyType.self, MyType())) +@Stubbed(type: .class, .value(for: MyType.self, MyType())) protocol MyProtocol { var someValue: MyType { get } func calculate(someValue: Int) -> MyType diff --git a/Sources/SwiftEnvironment/Macros.swift b/Sources/SwiftEnvironment/Macros.swift index 47d0746..902f34c 100644 --- a/Sources/SwiftEnvironment/Macros.swift +++ b/Sources/SwiftEnvironment/Macros.swift @@ -12,13 +12,18 @@ public macro EnvironmentValue() = #externalMacro( module: "SwiftEnvironmentMacro", type: "EnvironmentValueMacro" ) +@attached(member, names: arbitrary) +public macro Stubbed() = #externalMacro( + module: "SwiftEnvironmentMacro", type: "StubGeneratorMacro" +) + @attached(peer, names: suffixed(Stub)) -public macro Stubbed(name: String? = nil, type: StubType = .struct) = #externalMacro( +public macro Stubbed(type: StubType) = #externalMacro( module: "SwiftEnvironmentMacro", type: "StubGeneratorMacro" ) @attached(peer, names: suffixed(Stub)) -public macro Stubbed(name: String? = nil, type: StubType = .struct, _ values: DefaultType...) = #externalMacro( +public macro Stubbed(type: StubType, _ values: DefaultType...) = #externalMacro( module: "SwiftEnvironmentMacro", type: "StubGeneratorMacro" ) diff --git a/Sources/SwiftEnvironmentMacro/Model/StubInitializer.swift b/Sources/SwiftEnvironmentMacro/Model/StubInitializer.swift index f863136..cf1baa5 100644 --- a/Sources/SwiftEnvironmentMacro/Model/StubInitializer.swift +++ b/Sources/SwiftEnvironmentMacro/Model/StubInitializer.swift @@ -13,19 +13,19 @@ struct StubInitializer: CustomStringConvertible { let argumentPairs: [InitArgumentPairs] var description: String { - let singletonClause = "static var stub: \(name) = \(name)(" + let singletonClause = "static var stub: \(name) { \(name)(" let arguments = argumentPairs.compactMap { $0.asArguments }.joined(separator: ", ") if generateInit { let initArguments = argumentPairs.compactMap { $0.asInitDeclaration }.joined(separator: ", ") let body = argumentPairs.compactMap { $0.asInitBodyInitializer }.joined(separator: "\n ") return """ - \(singletonClause)\(arguments)) + \(singletonClause)\(arguments)) } init(\(initArguments)) { \(body) } """ } - return " \(singletonClause)\(arguments))" + return " \(singletonClause)\(arguments)) }" } } diff --git a/Sources/SwiftEnvironmentMacro/Stubbed/StubGeneratorMacro+PeerMacro.swift b/Sources/SwiftEnvironmentMacro/Stubbed/StubGeneratorMacro+PeerMacro.swift index 07e6f14..53f21a7 100644 --- a/Sources/SwiftEnvironmentMacro/Stubbed/StubGeneratorMacro+PeerMacro.swift +++ b/Sources/SwiftEnvironmentMacro/Stubbed/StubGeneratorMacro+PeerMacro.swift @@ -26,7 +26,6 @@ extension StubGeneratorMacro: PeerMacro { for: protocolSyntax.name.text, isObjectProtocol: isObjectProtocol ), - givenName: node.nameArgument, with: DefaultValueGenerator(defaultValues: defaultValues) ) return [ "\(raw: result.description)" ] @@ -36,13 +35,6 @@ extension StubGeneratorMacro: PeerMacro { // MARK: Private extensions private extension AttributeSyntax { - var nameArgument: String? { - let argument = arguments?.as(LabeledExprListSyntax.self)? - .first { $0.label?.trimmedDescription == "name" } - guard let argument else { return nil } - return argument.expression.as(StringLiteralExprSyntax.self)? - .segments.first?.trimmedDescription - } func typeArgument(for protocolName: String, isObjectProtocol: Bool) throws -> StubDeclaration.InstanceType { let baseArgument = arguments?.as(LabeledExprListSyntax.self)? @@ -132,9 +124,9 @@ private extension FunctionDeclSyntax { } private extension ProtocolDeclSyntax { - func defaultInstance(_ instanceType: StubDeclaration.InstanceType, givenName: String?, with defaultValues: DefaultValueGenerator) throws -> StubDeclaration { + func defaultInstance(_ instanceType: StubDeclaration.InstanceType, with defaultValues: DefaultValueGenerator) throws -> StubDeclaration { try StubDeclaration( - instanceType: instanceType, name: "\(givenName ?? name.text)Stub", + instanceType: instanceType, name: "\(name.text)Stub", variables: variables(with: defaultValues), methods: methods(with: defaultValues) ) diff --git a/Tests/SwiftEnvironmentTests/DummyDependency.swift b/Tests/SwiftEnvironmentTests/DummyDependency.swift index d10a0af..c724312 100644 --- a/Tests/SwiftEnvironmentTests/DummyDependency.swift +++ b/Tests/SwiftEnvironmentTests/DummyDependency.swift @@ -9,7 +9,17 @@ import Foundation import SwiftEnvironment -@Stubbed +@Stubbed(type: .class) protocol DummyDependency: AnyObject { var id: UUID { get } } + +@Stubbed +struct DummyStruct { + let id: UUID +} + +@Stubbed +struct DummyClass { + let id: UUID +} diff --git a/Tests/SwiftEnvironmentTests/IntegrationTests.swift b/Tests/SwiftEnvironmentTests/IntegrationTests.swift index b745758..4e0e54d 100644 --- a/Tests/SwiftEnvironmentTests/IntegrationTests.swift +++ b/Tests/SwiftEnvironmentTests/IntegrationTests.swift @@ -96,6 +96,11 @@ final class IntegrationTests: XCTestCase { XCTAssertEqual(dummy, "dummy") } + func test_givenClassAndStruct_whenStubbed_shouldHaveStub() { + _ = DummyClass.stub + _ = DummyStruct.stub + } + } typealias DummyEnvironmentKey = EnvironmentValues.DummySwiftEnvironmentKey diff --git a/Tests/SwiftEnvironmentTests/StubFromProtocolGeneratorMacroTests.swift b/Tests/SwiftEnvironmentTests/StubFromProtocolGeneratorMacroTests.swift index a566491..9d94e17 100644 --- a/Tests/SwiftEnvironmentTests/StubFromProtocolGeneratorMacroTests.swift +++ b/Tests/SwiftEnvironmentTests/StubFromProtocolGeneratorMacroTests.swift @@ -44,14 +44,6 @@ final class StubFromProtocolGeneratorMacroTests: XCTestCase { ) } - func test_givenNamedProtocol_shouldGenerateDefault() { - assertMacroExpansion( - namedProtocol, - expandedSource: namedProtocolExpansion, - macros: ["Stubbed": StubGeneratorMacro.self] - ) - } - func test_givenClassProtocol_shouldGenerateDefault() { assertMacroExpansion( classProtocol, @@ -102,7 +94,7 @@ final class StubFromProtocolGeneratorMacroTests: XCTestCase { } private let anyObjectProtocol: String = """ -@Stubbed +@Stubbed(type: .class) protocol Some: AnyObject { var int: Int { get } } @@ -121,7 +113,7 @@ final class SomeStub: Some { """ private let allArgsProtocol: String = """ -@Stubbed(name: "SomeName", type: .subclass(Super.self), .value(for: Int.self, 1), .value(for: Unknown.self, Unknown())) +@Stubbed(type: .subclass(Super.self), .value(for: Int.self, 1), .value(for: Unknown.self, Unknown())) protocol Some { var int: Int { get } var unknown: Unknown { get } @@ -134,7 +126,7 @@ protocol Some { var unknown: Unknown { get } } -final class SomeNameStub: Super, Some { +final class SomeStub: Super, Some { let int: Int = 1 let unknown: Unknown = Unknown() init() { @@ -162,7 +154,7 @@ struct SomeStub: Some { """ private let injectValueProtocol: String = """ -@Stubbed(.value(for: Int.self, 1), .value(for: Unknown.self, Unknown())) +@Stubbed(type: .struct, .value(for: Int.self, 1), .value(for: Unknown.self, Unknown())) protocol Some { var int: Int { get } var unknown: Unknown { get } @@ -221,27 +213,8 @@ final class SomeStub: Some { } """ -private let namedProtocol: String = """ -@Stubbed(name: "SomeName") -protocol Some { - var int: Int { get } -} -""" - -private let namedProtocolExpansion: String = """ -protocol Some { - var int: Int { get } -} - -struct SomeNameStub: Some { - let int: Int = 0 - init() { - } -} -""" - private let simpleProtocol: String = """ -@Stubbed +@Stubbed(type: .struct) protocol Some { var int: Int { get } var double: Double { get set } @@ -303,7 +276,7 @@ struct SomeStub: Some { """ private let optionalProtocol: String = """ -@Stubbed +@Stubbed(type: .struct) protocol Some { var int: Int? { get } var double: Optional { get set } @@ -356,7 +329,7 @@ struct SomeStub: Some { """ private let arrayProtocol: String = """ -@Stubbed +@Stubbed(type: .struct) protocol Some { var int: [Int] { get } var double: Array { get set } @@ -409,7 +382,7 @@ struct SomeStub: Some { """ private let dictionaryProtocol: String = """ -@Stubbed +@Stubbed(type: .struct) protocol Some { var int: [Int: Unknown] { get } var double: Dictionary { get set } diff --git a/Tests/SwiftEnvironmentTests/StubFromTypeGeneratorTests.swift b/Tests/SwiftEnvironmentTests/StubFromTypeGeneratorTests.swift index 0465fe4..87be1aa 100644 --- a/Tests/SwiftEnvironmentTests/StubFromTypeGeneratorTests.swift +++ b/Tests/SwiftEnvironmentTests/StubFromTypeGeneratorTests.swift @@ -62,7 +62,7 @@ class Some { init(double: Double) { self.double = double - self.int = int + self.int = 0 } } """ @@ -75,10 +75,12 @@ class Some { init(double: Double) { self.double = double - self.int = int + self.int = 0 } - static var stub: Some = Some(int: 0, double: 0.0) + static var stub: Some { + Some(int: 0, double: 0.0) + } init(int: Int, double: Double) { self.int = int self.double = double @@ -111,7 +113,9 @@ class Some { self.int = int } - static var stub: Some = Some(double: 0.0, int: 0) + static var stub: Some { + Some(double: 0.0, int: 0) + } } """ @@ -140,7 +144,9 @@ class Some { self.double = double } - static var stub: Some = Some(int: 0, double: 0.0) + static var stub: Some { + Some(int: 0, double: 0.0) + } } """ @@ -159,7 +165,9 @@ class Some { var double: Double let string: String = "" - static var stub: Some = Some(int: 0, double: 0.0) + static var stub: Some { + Some(int: 0, double: 0.0) + } init(int: Int, double: Double) { self.int = int self.double = double @@ -182,6 +190,8 @@ struct Some { var double: Double let string: String = "" - static var stub: Some = Some(int: 0, double: 0.0) + static var stub: Some { + Some(int: 0, double: 0.0) + } } """