Skip to content

Refactor MacroArgumentValue, reorganize files, and add unit tests #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension MockedMacro {
let argument = arguments.first(where: { argument in
argument.label?.text == name
}),
let value = ArgumentValue(argument: argument)
let value = try? ArgumentValue(argument: argument)
else {
return `default`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ protocol MacroArgumentValue {
///
/// - Parameter argument: The argument syntax from which to parse the
/// macro argument value.
init?(argument: LabeledExprSyntax)
/// - Throws: An error if unable to parse the macro argument value.
init(argument: LabeledExprSyntax) throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// MockCompilationCondition+ParsingError.swift
//
// Copyright © 2025 Fetch.
//

import Foundation

extension MockCompilationCondition {

/// A parsing error generated by ``MockCompilationCondition``.
enum ParsingError: CaseIterable, CustomStringConvertible, Error {

// MARK: Cases

/// An error indicating that ``MockCompilationCondition`` was unable to
/// parse a valid instance from the provided macro argument.
case unableToParseCompilationCondition

// MARK: Properties

/// The description of the error.
var description: String {
switch self {
case .unableToParseCompilationCondition:
"Unable to parse compilation condition."
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@
///
/// - Parameter argument: The argument syntax from which to parse a
/// compilation condition.
init?(argument: LabeledExprSyntax) {
/// - Throws: An error if a valid compilation condition cannot be parsed
/// from the provided `argument`.
init(argument: LabeledExprSyntax) throws {
let (
memberAccessExpression,
arguments
Expand Down Expand Up @@ -103,7 +105,7 @@
}

guard let memberAccessExpression else {
return nil
throw ParsingError.unableToParseCompilationCondition

Check warning on line 108 in Sources/MockingMacros/Models/MacroArguments/MockCompilationCondition/MockCompilationCondition.swift

View check run for this annotation

Codecov / codecov/patch

Sources/MockingMacros/Models/MacroArguments/MockCompilationCondition/MockCompilationCondition.swift#L108

Added line #L108 was not covered by tests
}

let declarationNameTokenKind = memberAccessExpression.declName.baseName.tokenKind
Expand All @@ -125,7 +127,7 @@
{
self = .custom(condition)
} else {
return nil
throw ParsingError.unableToParseCompilationCondition
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// MockSendableConformance+ParsingError.swift
//
// Copyright © 2025 Fetch.
//

import Foundation

extension MockSendableConformance {

/// A parsing error generated by ``MockSendableConformance``.
enum ParsingError: CaseIterable, CustomStringConvertible, Error {

// MARK: Cases

/// An error indicating that ``MockSendableConformance`` was unable to
/// parse a valid instance from the provided macro argument.
case unableToParseSendableConformance

// MARK: Properties

/// The description of the error.
var description: String {
switch self {
case .unableToParseSendableConformance:
"Unable to parse Sendable conformance."
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ enum MockSendableConformance: String, MacroArgumentValue {
///
/// - Parameter argument: The argument syntax from which to parse a
/// `Sendable` conformance.
init?(argument: LabeledExprSyntax) {
init(argument: LabeledExprSyntax) throws {
guard
let memberAccessExpression = argument.expression.as(
MemberAccessExprSyntax.self
),
let identifier = memberAccessExpression.declName.baseName.identifier
let identifier = memberAccessExpression.declName.baseName.identifier,
let sendableConformance = MockSendableConformance(rawValue: identifier.name)
else {
return nil
throw ParsingError.unableToParseSendableConformance
}

self.init(rawValue: identifier.name)
self = sendableConformance
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import SwiftSyntax

/// The type of property being mocked.
enum MockedPropertyType {
enum MockedPropertyType: MacroArgumentValue {

// MARK: Cases

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
//
// MockCompilationConditionTests.swift
//
// Copyright © 2025 Fetch.
//

import SwiftSyntax
import Testing
@testable import MockingMacros

struct MockCompilationConditionTests {

// MARK: Typealiases

typealias SUT = MockCompilationCondition

// MARK: Raw Value Tests

@Test(
arguments: [
SUT.none,
SUT.debug,
SUT.swiftMockingEnabled,
SUT.custom("!RELEASE"),
]
)
func rawValue(sut: SUT) {
let expectedRawValue: String? = switch sut {
case .none:
nil
case .debug:
"DEBUG"
case .swiftMockingEnabled:
"SWIFT_MOCKING_ENABLED"
case let .custom(condition):
condition
}

#expect(sut.rawValue == expectedRawValue)
}

// MARK: Init With Raw Value Tests

@Test
func initWithRawValueNone() {
let sut = SUT(rawValue: nil)

guard case .none = sut else {
Issue.record("Expected sut to be `.none`.")
return
}
}

@Test
func initWithRawValueDebug() {
let sut = SUT(rawValue: "DEBUG")

guard case .debug = sut else {
Issue.record("Expected sut to be `.debug`.")
return
}
}

@Test
func initWithRawValueSwiftMockingEnabled() {
let sut = SUT(rawValue: "SWIFT_MOCKING_ENABLED")

guard case .swiftMockingEnabled = sut else {
Issue.record("Expected sut to be `.swiftMockingEnabled`.")
return
}
}

@Test
func initWithRawValueCustom() {
let sut = SUT(rawValue: "!RELEASE")

guard case .custom("!RELEASE") = sut else {
Issue.record(#"Expected sut to be `.custom("!RELEASE")`."#)
return
}
}

// MARK: Init With Argument Tests

@Test("Initializes as .none from a valid argument with base.")
func initNoneFromArgumentWithBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: "MockCompilationCondition",
name: "none"
)
)

#expect(sut == .none)
}

@Test("Initializes as .none from a valid argument without base.")
func initNoneFromArgumentWithoutBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: nil,
name: "none"
)
)

#expect(sut == .none)
}

@Test("Initializes as .debug from a valid argument with base.")
func initDebugFromArgumentWithBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: "MockCompilationCondition",
name: "debug"
)
)

#expect(sut == .debug)
}

@Test("Initializes as .debug from a valid argument without base.")
func initDebugFromArgumentWithoutBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: nil,
name: "debug"
)
)

#expect(sut == .debug)
}

@Test("Initializes as .swiftMockingEnabled from a valid argument with base.")
func initSwiftMockingEnabledFromArgumentWithBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: "MockCompilationCondition",
name: "swiftMockingEnabled"
)
)

#expect(sut == .swiftMockingEnabled)
}

@Test("Initializes as .swiftMockingEnabled from a valid argument without base.")
func initSwiftMockingEnabledFromArgumentWithoutBase() throws {
let sut = try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: nil,
name: "swiftMockingEnabled"
)
)

#expect(sut == .swiftMockingEnabled)
}

@Test("Initializes as .custom from a valid argument with base.")
func initCustomFromArgumentWithBase() throws {
let sut = try SUT(
argument: LabeledExprSyntax(
label: "compilationCondition",
colon: .colonToken(),
expression: FunctionCallExprSyntax(
calledExpression: MemberAccessExprSyntax(
base: DeclReferenceExprSyntax(baseName: "MockCompilationCondition"),
period: .periodToken(),
declName: DeclReferenceExprSyntax(baseName: "custom")
),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax {
LabeledExprSyntax(
expression: StringLiteralExprSyntax(content: "!RELEASE")
)
},
rightParen: .rightParenToken()
)
)
)

#expect(sut == .custom("!RELEASE"))
}

@Test("Initializes as .custom from a valid argument without base.")
func initCustomFromArgumentWithoutBase() throws {
let sut = try SUT(
argument: LabeledExprSyntax(
label: "compilationCondition",
colon: .colonToken(),
expression: FunctionCallExprSyntax(
calledExpression: MemberAccessExprSyntax(
period: .periodToken(),
declName: DeclReferenceExprSyntax(baseName: "custom")
),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax {
LabeledExprSyntax(
expression: StringLiteralExprSyntax(content: "!RELEASE")
)
},
rightParen: .rightParenToken()
)
)
)

#expect(sut == .custom("!RELEASE"))
}

@Test("Initializes as nil from an invalid argument with base.")
func initNilFromInvalidArgumentWithBase() {
#expect(throws: SUT.ParsingError.unableToParseCompilationCondition) {
try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: "MockCompilationCondition",
name: "invalid"
)
)
}
}

@Test("Initializes as nil from an invalid argument without base.")
func initNilFromInvalidArgumentWithoutBase() {
#expect(throws: SUT.ParsingError.unableToParseCompilationCondition) {
try SUT(
argument: .macroArgumentSyntax(
label: "compilationCondition",
base: nil,
name: "invalid"
)
)
}
}

@Test("Initializes as nil from an argument with an invalid name token.")
func initNilFromNamelessArgument() {
#expect(throws: SUT.ParsingError.unableToParseCompilationCondition) {
try SUT(
argument: LabeledExprSyntax(
label: .identifier("compilationCondition"),
colon: .colonToken(),
expression: MemberAccessExprSyntax(
period: .periodToken(),
declName: DeclReferenceExprSyntax(baseName: .commaToken())
)
)
)
}
}
}
Loading