Skip to content

Commit 4cfedda

Browse files
committed
Add unit tests to all macro examples
1 parent b745d78 commit 4cfedda

21 files changed

+1453
-278
lines changed

Examples/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ let package = Package(
5252
.testTarget(
5353
name: "MacroExamplesImplementationTests",
5454
dependencies: [
55-
"MacroExamplesImplementation"
55+
"MacroExamplesImplementation",
56+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
57+
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
5658
],
5759
path: "Tests/MacroExamples/Implementation"
5860
),

Examples/Sources/MacroExamples/Implementation/Expression/FontLiteralMacro.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ private func replaceFirstLabel(
4040
}
4141

4242
var tuple = tuple
43-
tuple[tuple.startIndex] = firstElement
43+
tuple[tuple.startIndex] =
44+
firstElement
4445
.with(\.label, .identifier(newLabel))
4546
.with(\.colon, .colonToken())
4647
return tuple

Examples/Sources/MacroExamples/Interface/ExpressionMacros.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ public protocol ExpressibleByFontLiteral {
3535

3636
/// Font literal similar to, e.g., #colorLiteral.
3737
@freestanding(expression)
38-
public macro fontLiteral<T>(name: String, size: Int, weight: FontWeight) -> T = #externalMacro(
39-
module: "MacroExamplesImplementation",
40-
type: "FontLiteralMacro"
41-
) where T: ExpressibleByFontLiteral
38+
public macro fontLiteral<T>(name: String, size: Int, weight: FontWeight) -> T =
39+
#externalMacro(
40+
module: "MacroExamplesImplementation",
41+
type: "FontLiteralMacro"
42+
) where T: ExpressibleByFontLiteral
4243

4344
// MARK: - Stringify Macro
4445

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import MacroExamplesImplementation
14+
import SwiftSyntaxMacros
15+
import SwiftSyntaxMacrosTestSupport
16+
import XCTest
17+
18+
final class DictionaryStorageMacroTests: XCTestCase {
19+
private let macros: [String: Macro.Type] = [
20+
"DictionaryStorage": DictionaryStorageMacro.self,
21+
"DictionaryStorageProperty": DictionaryStoragePropertyMacro.self,
22+
]
23+
24+
func testExpansionConvertsStoredProperties() {
25+
assertMacroExpansion(
26+
"""
27+
@DictionaryStorage
28+
struct Point {
29+
var x: Int = 1
30+
var y: Int = 2
31+
}
32+
""",
33+
expandedSource: """
34+
struct Point {
35+
var x: Int = 1 {
36+
get {
37+
_storage["x", default: 1] as! Int
38+
}
39+
set {
40+
_storage["x"] = newValue
41+
}
42+
}
43+
var y: Int = 2 {
44+
get {
45+
_storage["y", default: 2] as! Int
46+
}
47+
set {
48+
_storage["y"] = newValue
49+
}
50+
}
51+
52+
var _storage: [String: Any] = [:]
53+
}
54+
""",
55+
macros: macros
56+
)
57+
}
58+
59+
func testExpansionWithoutInitializersEmitsError() {
60+
assertMacroExpansion(
61+
"""
62+
@DictionaryStorage
63+
class Point {
64+
let x: Int
65+
let y: Int
66+
}
67+
""",
68+
expandedSource: """
69+
class Point {
70+
let x: Int
71+
let y: Int
72+
73+
var _storage: [String: Any] = [:]
74+
}
75+
""",
76+
diagnostics: [
77+
DiagnosticSpec(message: "stored property must have an initializer", line: 3, column: 3),
78+
DiagnosticSpec(message: "stored property must have an initializer", line: 4, column: 3),
79+
],
80+
macros: macros
81+
)
82+
}
83+
84+
func testExpansionIgnoresComputedProperties() {
85+
assertMacroExpansion(
86+
"""
87+
@DictionaryStorage
88+
struct Test {
89+
var value: Int {
90+
get { return 0 }
91+
set {}
92+
}
93+
}
94+
""",
95+
expandedSource: """
96+
struct Test {
97+
var value: Int {
98+
get { return 0 }
99+
set {}
100+
}
101+
102+
var _storage: [String: Any] = [:]
103+
}
104+
""",
105+
macros: macros
106+
)
107+
}
108+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import MacroExamplesImplementation
14+
import SwiftSyntaxMacros
15+
import SwiftSyntaxMacrosTestSupport
16+
import XCTest
17+
18+
final class ObservableMacroTests: XCTestCase {
19+
private let macros: [String: Macro.Type] = [
20+
"Observable": ObservableMacro.self,
21+
"ObservableProperty": ObservablePropertyMacro.self,
22+
]
23+
24+
func testExpansion() {
25+
assertMacroExpansion(
26+
"""
27+
@Observable
28+
final class Dog {
29+
var name: String?
30+
var treat: Treat?
31+
32+
var isHappy: Bool = true
33+
34+
init() {}
35+
36+
func bark() {
37+
print("bork bork")
38+
}
39+
}
40+
""",
41+
expandedSource: #"""
42+
final class Dog {
43+
var name: String? {
44+
get {
45+
_registrar.beginAccess(\.name)
46+
defer {
47+
_registrar.endAccess()
48+
}
49+
return _storage.name
50+
}
51+
set {
52+
_registrar.beginAccess(\.name)
53+
_registrar.register(observable: self, willSet: \.name, to: newValue)
54+
defer {
55+
_registrar.register(observable: self, didSet: \.name)
56+
_registrar.endAccess()
57+
}
58+
_storage.name = newValue
59+
}
60+
}
61+
var treat: Treat? {
62+
get {
63+
_registrar.beginAccess(\.treat)
64+
defer {
65+
_registrar.endAccess()
66+
}
67+
return _storage.treat
68+
}
69+
set {
70+
_registrar.beginAccess(\.treat)
71+
_registrar.register(observable: self, willSet: \.treat, to: newValue)
72+
defer {
73+
_registrar.register(observable: self, didSet: \.treat)
74+
_registrar.endAccess()
75+
}
76+
_storage.treat = newValue
77+
}
78+
}
79+
80+
var isHappy: Bool = true {
81+
get {
82+
_registrar.beginAccess(\.isHappy)
83+
defer {
84+
_registrar.endAccess()
85+
}
86+
return _storage.isHappy
87+
}
88+
set {
89+
_registrar.beginAccess(\.isHappy)
90+
_registrar.register(observable: self, willSet: \.isHappy, to: newValue)
91+
defer {
92+
_registrar.register(observable: self, didSet: \.isHappy)
93+
_registrar.endAccess()
94+
}
95+
_storage.isHappy = newValue
96+
}
97+
}
98+
99+
init() {}
100+
101+
func bark() {
102+
print("bork bork")
103+
}
104+
105+
let _registrar = ObservationRegistrar<Dog >()
106+
107+
public nonisolated func addObserver(_ observer: some Observer<Dog >) {
108+
_registrar.addObserver(observer)
109+
}
110+
111+
public nonisolated func removeObserver(_ observer: some Observer<Dog >) {
112+
_registrar.removeObserver(observer)
113+
}
114+
115+
private func withTransaction<T>(_ apply: () throws -> T) rethrows -> T {
116+
_registrar.beginAccess()
117+
defer {
118+
_registrar.endAccess()
119+
}
120+
return try apply()
121+
}
122+
123+
private struct Storage {
124+
125+
var name: String?
126+
var treat: Treat?
127+
128+
var isHappy: Bool = true
129+
}
130+
131+
private var _storage = Storage()
132+
}
133+
134+
extension Dog: Observable {
135+
}
136+
"""#,
137+
macros: macros
138+
)
139+
}
140+
}

0 commit comments

Comments
 (0)