From 4716f59c54c74b9b61c74cdd4ba6323af3adae8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Tue, 3 Oct 2023 12:42:32 +0200 Subject: [PATCH] Add tests for RuleConfigurationMacros (#5256) --- .../RuleConfigurationMacros.swift | 2 +- Tests/MacroTests/AutoApplyTests.swift | 131 ++++++++++++++++++ ...cceptableByConfigurationElementTests.swift | 102 ++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 Tests/MacroTests/AutoApplyTests.swift create mode 100644 Tests/MacroTests/MakeAcceptableByConfigurationElementTests.swift diff --git a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift index 24aaa904e8..52d1b650e1 100644 --- a/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift +++ b/Source/SwiftLintCoreMacros/RuleConfigurationMacros.swift @@ -5,7 +5,7 @@ import SwiftSyntaxMacros private let configurationElementName = "ConfigurationElement" private let acceptableByConfigurationElementName = "AcceptableByConfigurationElement" -private enum RuleConfigurationMacroError: String, DiagnosticMessage { +enum RuleConfigurationMacroError: String, DiagnosticMessage { case notStruct = "Attribute can only be applied to structs" case notEnum = "Attribute can only be applied to enums" case noStringRawType = "Attribute can only be applied to enums with a 'String' raw type" diff --git a/Tests/MacroTests/AutoApplyTests.swift b/Tests/MacroTests/AutoApplyTests.swift new file mode 100644 index 0000000000..02d7910c4d --- /dev/null +++ b/Tests/MacroTests/AutoApplyTests.swift @@ -0,0 +1,131 @@ +@testable import SwiftLintCoreMacros +import SwiftSyntaxMacrosTestSupport +import XCTest + +private let macros = [ + "AutoApply": AutoApply.self +] + +final class AutoApplyTests: XCTestCase { + func testAttachToClass() { + assertMacroExpansion( + """ + @AutoApply + class C { + } + """, + expandedSource: + """ + class C { + } + """, + diagnostics: [ + DiagnosticSpec(message: RuleConfigurationMacroError.notStruct.message, line: 1, column: 1) + ], + macros: macros) + } + + func testNoConfigurationElements() { + assertMacroExpansion( + """ + @AutoApply + struct S { + } + """, + expandedSource: + """ + struct S { + + mutating func apply(configuration: Any) throws { + + guard let _ = configuration as? [String: Any] else { + throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + } + + if !supportedKeys.isSuperset(of: configuration.keys) { + let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) + throw Issue.invalidConfigurationKeys(unknownKeys.sorted()) + } + } + } + """, + macros: macros + ) + } + + func testConfigurationElementsWithoutKeys() { + assertMacroExpansion( + """ + @AutoApply + struct S { + @ConfigurationElement + var e1 = 1 + @ConfigurationElement(value: 7) + var e2 = 2 + } + """, + expandedSource: + """ + struct S { + @ConfigurationElement + var e1 = 1 + @ConfigurationElement(value: 7) + var e2 = 2 + + mutating func apply(configuration: Any) throws { + try e1.apply(configuration, ruleID: Parent.identifier) + try e2.apply(configuration, ruleID: Parent.identifier) + guard let _ = configuration as? [String: Any] else { + return + } + + if !supportedKeys.isSuperset(of: configuration.keys) { + let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) + throw Issue.invalidConfigurationKeys(unknownKeys.sorted()) + } + } + } + """, + macros: macros + ) + } + + func testConfigurationElementsWithKeys() { + assertMacroExpansion( + """ + @AutoApply + struct S { + @ConfigurationElement(key: "e1") + var e1 = 1 + @ConfigurationElement(key: "e2", other: 7) + var e2 = 2 + } + """, + expandedSource: + """ + struct S { + @ConfigurationElement(key: "e1") + var e1 = 1 + @ConfigurationElement(key: "e2", other: 7) + var e2 = 2 + + mutating func apply(configuration: Any) throws { + + guard let configuration = configuration as? [String: Any] else { + throw Issue.invalidConfiguration(ruleID: Parent.description.identifier) + } + try e1.apply(configuration[$e1.key], ruleID: Parent.identifier) + try $e1.performAfterParseOperations() + try e2.apply(configuration[$e2.key], ruleID: Parent.identifier) + try $e2.performAfterParseOperations() + if !supportedKeys.isSuperset(of: configuration.keys) { + let unknownKeys = Set(configuration.keys).subtracting(supportedKeys) + throw Issue.invalidConfigurationKeys(unknownKeys.sorted()) + } + } + } + """, + macros: macros + ) + } +} diff --git a/Tests/MacroTests/MakeAcceptableByConfigurationElementTests.swift b/Tests/MacroTests/MakeAcceptableByConfigurationElementTests.swift new file mode 100644 index 0000000000..87fe8f912b --- /dev/null +++ b/Tests/MacroTests/MakeAcceptableByConfigurationElementTests.swift @@ -0,0 +1,102 @@ +@testable import SwiftLintCoreMacros +import SwiftSyntaxMacrosTestSupport +import XCTest + +private let macros = [ + "MakeAcceptableByConfigurationElement": MakeAcceptableByConfigurationElement.self +] + +// swiftlint:disable:next type_name +final class MakeAcceptableByConfigurationElementTests: XCTestCase { + func testNoEnum() { + assertMacroExpansion( + """ + @MakeAcceptableByConfigurationElement + struct S { + } + """, + expandedSource: + """ + struct S { + } + """, + diagnostics: [ + DiagnosticSpec(message: RuleConfigurationMacroError.notEnum.message, line: 1, column: 1) + ], + macros: macros) + } + + func testNoStringRawType() { + assertMacroExpansion( + """ + @MakeAcceptableByConfigurationElement + enum E { + } + """, + expandedSource: + """ + enum E { + } + """, + diagnostics: [ + DiagnosticSpec(message: RuleConfigurationMacroError.noStringRawType.message, line: 1, column: 1) + ], + macros: macros) + } + + func testPrivateEnum() { + assertMacroExpansion( + """ + @MakeAcceptableByConfigurationElement + private enum E: String { + } + """, + expandedSource: + """ + private enum E: String { + } + + extension E: AcceptableByConfigurationElement { + private func asOption() -> OptionType { + .symbol(rawValue) + } + private init(fromAny value: Any, context ruleID: String) throws { + if let value = value as? String, let newSelf = Self (rawValue: value) { + self = newSelf + } else { + throw Issue.unknownConfiguration(ruleID: ruleID) + } + } + } + """, + macros: macros) + } + + func testPublicEnum() { + assertMacroExpansion( + """ + @MakeAcceptableByConfigurationElement + public enum E: String { + } + """, + expandedSource: + """ + public enum E: String { + } + + extension E: AcceptableByConfigurationElement { + public func asOption() -> OptionType { + .symbol(rawValue) + } + public init(fromAny value: Any, context ruleID: String) throws { + if let value = value as? String, let newSelf = Self (rawValue: value) { + self = newSelf + } else { + throw Issue.unknownConfiguration(ruleID: ruleID) + } + } + } + """, + macros: macros) + } +}