Skip to content

Commit d237304

Browse files
Swift Testing support (#124)
* Swift testing * wip * wip * wip * test * wip * wip * wip * wip --------- Co-authored-by: Brandon Williams <[email protected]>
1 parent e6a364d commit d237304

File tree

16 files changed

+389
-167
lines changed

16 files changed

+389
-167
lines changed

CustomDump.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 11 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.resolved

Lines changed: 11 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ let package = Package(
1717
)
1818
],
1919
dependencies: [
20-
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0")
20+
.package(url: "https://github.com/pointfreeco/swift-issue-reporting", from: "1.2.0")
2121
],
2222
targets: [
2323
.target(
2424
name: "CustomDump",
2525
dependencies: [
26-
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay")
26+
.product(name: "IssueReporting", package: "swift-issue-reporting"),
27+
.product(name: "XCTestDynamicOverlay", package: "swift-issue-reporting"),
2728
],
2829
swiftSettings: [
2930
.enableExperimentalFeature("StrictConcurrency")

[email protected]

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ let package = Package(
1717
)
1818
],
1919
dependencies: [
20-
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0")
20+
.package(url: "https://github.com/pointfreeco/swift-issue-reporting", from: "1.2.0")
2121
],
2222
targets: [
2323
.target(
2424
name: "CustomDump",
2525
dependencies: [
26-
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay")
26+
.product(name: "IssueReporting", package: "swift-issue-reporting"),
27+
.product(name: "XCTestDynamicOverlay", package: "swift-issue-reporting"),
2728
]
2829
),
2930
.testTarget(

Sources/CustomDump/Conformances/Foundation.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,10 @@ extension Calendar: CustomDumpReflectable {
3434
#if !os(WASI)
3535
extension Data: CustomDumpStringConvertible {
3636
public var customDumpDescription: String {
37-
"Data(\(Self.formatter.string(fromByteCount: .init(self.count))))"
38-
}
39-
40-
private static let formatter: ByteCountFormatter = {
4137
let formatter = ByteCountFormatter()
4238
formatter.allowedUnits = .useBytes
43-
return formatter
44-
}()
39+
return "Data(\(formatter.string(fromByteCount: .init(self.count))))"
40+
}
4541
}
4642
#endif
4743

Sources/CustomDump/Documentation.docc/CustomDump.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,11 +446,13 @@ customDump(ID(rawValue: "deadbeef")
446446

447447
### Test support
448448

449-
- ``XCTAssertNoDifference(_:_:_:file:line:)``
450-
- ``XCTAssertDifference(_:_:operation:changes:file:line:)-8xfxw``
449+
- ``expectNoDifference(_:_:_:fileID:filePath:line:column:)``
450+
- ``expectDifference(_:_:operation:changes:fileID:filePath:line:column:)-5fu8q``
451451

452452
### Customizing output
453453

454454
- ``CustomDumpStringConvertible``
455455
- ``CustomDumpRepresentable``
456456
- ``CustomDumpReflectable``
457+
458+
### Deprecations
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Deprecations
2+
3+
Review unsupported APIs and their replacements.
4+
5+
## Overview
6+
7+
Avoid using deprecated APIs in your app. Select a method to see the replacement that you should use
8+
instead.
9+
10+
## Topics
11+
12+
### Test support
13+
14+
- ``XCTAssertNoDifference(_:_:_:file:line:)``
15+
- ``XCTAssertDifference(_:_:operation:changes:file:line:)-8xfxw``
16+
- ``XCTAssertDifference(_:_:operation:changes:file:line:)-3c9r9``

Sources/CustomDump/Documentation.docc/XCTAssertDifference.md

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# ``CustomDump/expectDifference(_:_:operation:changes:fileID:filePath:line:column:)-5fu8q``
2+
3+
## Topics
4+
5+
### Async
6+
7+
- ``expectDifference(_:_:operation:changes:fileID:filePath:line:column:)-1xg1y``
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import IssueReporting
2+
3+
/// Expects that a value has a set of changes.
4+
///
5+
/// This function evaluates a given expression before and after a given operation and then compares
6+
/// the results. The comparison is done by invoking the `changes` closure with a mutable version of
7+
/// the initial value, and then asserting that the modifications made match the final value using
8+
/// ``expectNoDifference``.
9+
///
10+
/// For example, given a very simple counter structure, we can write a test against its incrementing
11+
/// functionality:
12+
/// `
13+
/// ```swift
14+
/// struct Counter {
15+
/// var count = 0
16+
/// var isOdd = false
17+
/// mutating func increment() {
18+
/// self.count += 1
19+
/// self.isOdd.toggle()
20+
/// }
21+
/// }
22+
///
23+
/// var counter = Counter()
24+
/// expectDifference(counter) {
25+
/// counter.increment()
26+
/// } changes: {
27+
/// $0.count = 1
28+
/// $0.isOdd = true
29+
/// }
30+
/// ```
31+
///
32+
/// If the `changes` does not exhaustively describe all changed fields, the assertion will fail.
33+
///
34+
/// By omitting the operation you can write a "non-exhaustive" assertion against a value by
35+
/// describing just the fields you want to assert against in the `changes` closure:
36+
///
37+
/// ```swift
38+
/// counter.increment()
39+
/// expectDifference(counter) {
40+
/// $0.count = 1
41+
/// // Don't need to further describe how `isOdd` has changed
42+
/// }
43+
/// ```
44+
///
45+
/// - Parameters:
46+
/// - expression: An expression that is evaluated before and after `operation`, and then compared.
47+
/// - message: An optional description of a failure.
48+
/// - operation: An optional operation that is performed in between an initial and final
49+
/// evaluation of `operation`. By omitting this operation, you can write a "non-exhaustive"
50+
/// assertion against an already-changed value by describing just the fields you want to assert
51+
/// against in the `changes` closure.
52+
/// - updateExpectingResult: A closure that asserts how the expression changed by supplying a
53+
/// mutable version of the initial value. This value must be modified to match the final value.
54+
public func expectDifference<T: Equatable>(
55+
_ expression: @autoclosure () throws -> T,
56+
_ message: @autoclosure () -> String? = nil,
57+
operation: () throws -> Void = {},
58+
changes updateExpectingResult: (inout T) throws -> Void,
59+
fileID: StaticString = #fileID,
60+
filePath: StaticString = #filePath,
61+
line: UInt = #line,
62+
column: UInt = #column
63+
) {
64+
do {
65+
var expression1 = try expression()
66+
try updateExpectingResult(&expression1)
67+
try operation()
68+
let expression2 = try expression()
69+
guard expression1 != expression2 else { return }
70+
let format = DiffFormat.proportional
71+
guard let difference = diff(expression1, expression2, format: format)
72+
else {
73+
reportIssue(
74+
"""
75+
("\(expression1)" is not equal to ("\(expression2)"), but no difference was detected.
76+
""",
77+
fileID: fileID,
78+
filePath: filePath,
79+
line: line,
80+
column: column
81+
)
82+
return
83+
}
84+
reportIssue(
85+
"""
86+
\(message()?.appending(" - ") ?? "")Difference: …
87+
88+
\(difference.indenting(by: 2))
89+
90+
(Expected: \(format.first), Actual: \(format.second))
91+
""",
92+
fileID: fileID,
93+
filePath: filePath,
94+
line: line,
95+
column: column
96+
)
97+
} catch {
98+
reportIssue(
99+
error,
100+
fileID: fileID,
101+
filePath: filePath,
102+
line: line,
103+
column: column
104+
)
105+
}
106+
}
107+
108+
/// Expect that two values have no difference.
109+
///
110+
/// An async version of
111+
/// ``expectDifference(_:_:operation:changes:fileID:filePath:line:column:)-5fu8q``.
112+
public func expectDifference<T: Equatable & Sendable>(
113+
_ expression: @autoclosure @Sendable () throws -> T,
114+
_ message: @autoclosure @Sendable () -> String? = nil,
115+
operation: @Sendable () async throws -> Void = {},
116+
changes updateExpectingResult: @Sendable (inout T) throws -> Void,
117+
fileID: StaticString = #fileID,
118+
filePath: StaticString = #filePath,
119+
line: UInt = #line,
120+
column: UInt = #column
121+
) async {
122+
do {
123+
var expression1 = try expression()
124+
try updateExpectingResult(&expression1)
125+
try await operation()
126+
let expression2 = try expression()
127+
guard expression1 != expression2 else { return }
128+
let format = DiffFormat.proportional
129+
guard let difference = diff(expression1, expression2, format: format)
130+
else {
131+
reportIssue(
132+
"""
133+
("\(expression1)" is not equal to ("\(expression2)"), but no difference was detected.
134+
""",
135+
fileID: fileID,
136+
filePath: filePath,
137+
line: line,
138+
column: column
139+
)
140+
return
141+
}
142+
reportIssue(
143+
"""
144+
\(message()?.appending(" - ") ?? "")Difference: …
145+
146+
\(difference.indenting(by: 2))
147+
148+
(Expected: \(format.first), Actual: \(format.second))
149+
""",
150+
fileID: fileID,
151+
filePath: filePath,
152+
line: line,
153+
column: column
154+
)
155+
} catch {
156+
reportIssue(
157+
error,
158+
fileID: fileID,
159+
filePath: filePath,
160+
line: line,
161+
column: column
162+
)
163+
}
164+
}

0 commit comments

Comments
 (0)