Skip to content

Commit 1764c54

Browse files
authored
Merge pull request #319 from stmontgomery/GenericEqualWithAccuracy
Make XCTAssertEqual with accuracy more generic
2 parents e24d665 + 1f2cd76 commit 1764c54

File tree

4 files changed

+189
-135
lines changed

4 files changed

+189
-135
lines changed

Sources/XCTest/Public/XCTAssert.swift

+29-4
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,29 @@ public func XCTAssertEqual<T: Equatable>(_ expression1: @autoclosure () throws -
171171
}
172172
}
173173

174+
private func areEqual<T: Numeric>(_ exp1: T, _ exp2: T, accuracy: T) -> Bool {
175+
// Test with equality first to handle comparing inf/-inf with itself.
176+
if exp1 == exp2 {
177+
return true
178+
} else {
179+
// NaN values are handled implicitly, since the <= operator returns false when comparing any value to NaN.
180+
let difference = (exp1.magnitude > exp2.magnitude) ? exp1 - exp2 : exp2 - exp1
181+
return difference.magnitude <= accuracy.magnitude
182+
}
183+
}
184+
174185
public func XCTAssertEqual<T: FloatingPoint>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
186+
_XCTAssertEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line)
187+
}
188+
189+
public func XCTAssertEqual<T: Numeric>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
190+
_XCTAssertEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line)
191+
}
192+
193+
private func _XCTAssertEqual<T: Numeric>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
175194
_XCTEvaluateAssertion(.equalWithAccuracy, message: message(), file: file, line: line) {
176195
let (value1, value2) = (try expression1(), try expression2())
177-
// Test with equality first to handle comparing inf/-inf with itself.
178-
if value1 == value2 ||
179-
abs(value1.distance(to: value2)) <= abs(accuracy.distance(to: T(0))) {
196+
if areEqual(value1, value2, accuracy: accuracy) {
180197
return .success
181198
} else {
182199
return .expectedFailure("(\"\(value1)\") is not equal to (\"\(value2)\") +/- (\"\(accuracy)\")")
@@ -267,9 +284,17 @@ public func XCTAssertNotEqual<T: Equatable>(_ expression1: @autoclosure () throw
267284
}
268285

269286
public func XCTAssertNotEqual<T: FloatingPoint>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
287+
_XCTAssertNotEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line)
288+
}
289+
290+
public func XCTAssertNotEqual<T: Numeric>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
291+
_XCTAssertNotEqual(try expression1(), try expression2(), accuracy: accuracy, message(), file: file, line: line)
292+
}
293+
294+
private func _XCTAssertNotEqual<T: Numeric>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, accuracy: T, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
270295
_XCTEvaluateAssertion(.notEqualWithAccuracy, message: message(), file: file, line: line) {
271296
let (value1, value2) = (try expression1(), try expression2())
272-
if abs(value1.distance(to: value2)) > abs(accuracy.distance(to: T(0))) {
297+
if !areEqual(value1, value2, accuracy: accuracy) {
273298
return .success
274299
} else {
275300
return .expectedFailure("(\"\(value1)\") is equal to (\"\(value2)\") +/- (\"\(accuracy)\")")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// RUN: %{swiftc} %s -o %T/EqualityWithAccuracy
2+
// RUN: %T/EqualityWithAccuracy > %t || true
3+
// RUN: %{xctest_checker} %t %s
4+
5+
#if os(macOS)
6+
import SwiftXCTest
7+
#else
8+
import XCTest
9+
#endif
10+
11+
// Regression test for https://github.com/apple/swift-corelibs-xctest/pull/7
12+
// and https://github.com/apple/swift-corelibs-xctest/pull/294
13+
14+
// CHECK: Test Suite 'All tests' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
15+
// CHECK: Test Suite '.*\.xctest' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
16+
17+
// CHECK: Test Suite 'EqualityWithAccuracyTests' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
18+
class EqualityWithAccuracyTests: XCTestCase {
19+
static var allTests = {
20+
return [
21+
("test_equalWithAccuracy_passes", test_equalWithAccuracy_passes),
22+
("test_equalWithAccuracy_fails", test_equalWithAccuracy_fails),
23+
("test_notEqualWithAccuracy_passes", test_notEqualWithAccuracy_passes),
24+
("test_notEqualWithAccuracy_fails", test_notEqualWithAccuracy_fails),
25+
("test_equalWithAccuracy_int_passes", test_equalWithAccuracy_int_passes),
26+
("test_equalWithAccuracy_int_fails", test_equalWithAccuracy_int_fails),
27+
("test_notEqualWithAccuracy_int_passes", test_notEqualWithAccuracy_int_passes),
28+
("test_notEqualWithAccuracy_int_fails", test_notEqualWithAccuracy_int_fails),
29+
("test_equalWithAccuracy_infinity_fails", test_equalWithAccuracy_infinity_fails),
30+
("test_notEqualWithAccuracy_infinity_fails", test_notEqualWithAccuracy_infinity_fails),
31+
("test_equalWithAccuracy_nan_fails", test_equalWithAccuracy_nan_fails),
32+
]
33+
}()
34+
35+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
36+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_passes' passed \(\d+\.\d+ seconds\)
37+
func test_equalWithAccuracy_passes() {
38+
XCTAssertEqual(0, 0.1, accuracy: -0.1)
39+
XCTAssertEqual(1, 1, accuracy: 0)
40+
XCTAssertEqual(0, 0, accuracy: 0)
41+
XCTAssertEqual(0, 1, accuracy: 1)
42+
XCTAssertEqual(0, 1, accuracy: 1.01)
43+
XCTAssertEqual(1, 1.09, accuracy: 0.1)
44+
XCTAssertEqual(1 as Float, 1.09, accuracy: 0.1)
45+
XCTAssertEqual(1 as Float32, 1.09, accuracy: 0.1)
46+
XCTAssertEqual(1 as Float64, 1.09, accuracy: 0.1)
47+
XCTAssertEqual(1 as CGFloat, 1.09, accuracy: 0.1)
48+
XCTAssertEqual(1 as Double, 1.09, accuracy: 0.1)
49+
XCTAssertEqual(1 as Int, 2, accuracy: 5)
50+
XCTAssertEqual(1 as UInt, 4, accuracy: 5)
51+
XCTAssertEqual(1, -1, accuracy: 2)
52+
XCTAssertEqual(-2, -4, accuracy: 2)
53+
XCTAssertEqual(Double.infinity, .infinity, accuracy: 0)
54+
XCTAssertEqual(Double.infinity, .infinity, accuracy: 1)
55+
XCTAssertEqual(Double.infinity, .infinity, accuracy: 1e-6)
56+
XCTAssertEqual(-Double.infinity, -.infinity, accuracy: 1)
57+
XCTAssertEqual(Double.infinity, .infinity, accuracy: 1e-6)
58+
XCTAssertEqual(Double.infinity, .infinity, accuracy: 1e6)
59+
XCTAssertEqual(Double.infinity, .infinity, accuracy: .infinity)
60+
XCTAssertEqual(Double.infinity, -.infinity, accuracy: .infinity)
61+
XCTAssertEqual(Float.infinity, .infinity, accuracy: 1e-6)
62+
XCTAssertEqual(Double.infinity, .infinity - 1, accuracy: 0)
63+
}
64+
65+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
66+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_equalWithAccuracy_fails : XCTAssertEqual failed: \("0\.0"\) is not equal to \("0\.2"\) \+\/- \("-0\.1"\) - $
67+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_fails' failed \(\d+\.\d+ seconds\)
68+
func test_equalWithAccuracy_fails() {
69+
XCTAssertEqual(0, 0.2, accuracy: -0.1)
70+
}
71+
72+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
73+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_passes' passed \(\d+\.\d+ seconds\)
74+
func test_notEqualWithAccuracy_passes() {
75+
XCTAssertNotEqual(1.0, 2.0, accuracy: -0.5)
76+
XCTAssertNotEqual(0, 1, accuracy: 0.1)
77+
XCTAssertNotEqual(1, 1.11, accuracy: 0.1)
78+
XCTAssertNotEqual(1 as Float, 1.11, accuracy: 0.1)
79+
XCTAssertNotEqual(1 as Float32, 1.11, accuracy: 0.1)
80+
XCTAssertNotEqual(1 as Float64, 1.11, accuracy: 0.1)
81+
XCTAssertNotEqual(1 as CGFloat, 1.11, accuracy: 0.1)
82+
XCTAssertNotEqual(1 as Double, 1.11, accuracy: 0.1)
83+
XCTAssertNotEqual(1 as Int, 10, accuracy: 5)
84+
XCTAssertNotEqual(1 as UInt, 10, accuracy: 5)
85+
XCTAssertNotEqual(1, -1, accuracy: 1)
86+
XCTAssertNotEqual(-2, -4, accuracy: 1)
87+
XCTAssertNotEqual(Double.nan, Double.nan, accuracy: 0)
88+
XCTAssertNotEqual(1, Double.nan, accuracy: 0)
89+
XCTAssertNotEqual(Double.nan, 1, accuracy: 0)
90+
XCTAssertNotEqual(Double.nan, 1, accuracy: .nan)
91+
XCTAssertNotEqual(Double.infinity, -.infinity, accuracy: 0)
92+
XCTAssertNotEqual(Double.infinity, -.infinity, accuracy: 1)
93+
XCTAssertNotEqual(Double.infinity, -.infinity, accuracy: 1e-6)
94+
XCTAssertNotEqual(Double.infinity, -.infinity, accuracy: 1e6)
95+
}
96+
97+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
98+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_notEqualWithAccuracy_fails : XCTAssertNotEqual failed: \("1\.0"\) is equal to \("2\.0"\) \+/- \("-1\.0"\) - $
99+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_fails' failed \(\d+\.\d+ seconds\)
100+
func test_notEqualWithAccuracy_fails() {
101+
XCTAssertNotEqual(1.0, 2.0, accuracy: -1.0)
102+
}
103+
104+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_int_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
105+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_int_passes' passed \(\d+\.\d+ seconds\)
106+
func test_equalWithAccuracy_int_passes() {
107+
XCTAssertEqual(10, 11, accuracy: 1)
108+
}
109+
110+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_int_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
111+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_equalWithAccuracy_int_fails : XCTAssertEqual failed: \("10"\) is not equal to \("8"\) \+\/- \("1"\) - $
112+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_int_fails' failed \(\d+\.\d+ seconds\)
113+
func test_equalWithAccuracy_int_fails() {
114+
XCTAssertEqual(10, 8, accuracy: 1)
115+
}
116+
117+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_int_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
118+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_int_passes' passed \(\d+\.\d+ seconds\)
119+
func test_notEqualWithAccuracy_int_passes() {
120+
XCTAssertNotEqual(-1, 5, accuracy: 5)
121+
}
122+
123+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_int_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
124+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_notEqualWithAccuracy_int_fails : XCTAssertNotEqual failed: \("0"\) is equal to \("5"\) \+/- \("5"\) - $
125+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_int_fails' failed \(\d+\.\d+ seconds\)
126+
func test_notEqualWithAccuracy_int_fails() {
127+
XCTAssertNotEqual(0, 5, accuracy: 5)
128+
}
129+
130+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_infinity_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
131+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_equalWithAccuracy_infinity_fails : XCTAssertEqual failed: \(\"-inf\"\) is not equal to \(\"inf\"\) \+\/- \(\"1e-06"\) - $
132+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_infinity_fails' failed \(\d+\.\d+ seconds\)
133+
func test_equalWithAccuracy_infinity_fails() {
134+
XCTAssertEqual(-Double.infinity, .infinity, accuracy: 1e-6)
135+
}
136+
137+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_infinity_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
138+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_notEqualWithAccuracy_infinity_fails : XCTAssertNotEqual failed: \("-inf"\) is equal to \("-inf"\) \+/- \("1e-06"\) - $
139+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_notEqualWithAccuracy_infinity_fails' failed \(\d+\.\d+ seconds\)
140+
func test_notEqualWithAccuracy_infinity_fails() {
141+
XCTAssertNotEqual(-Double.infinity, -.infinity, accuracy: 1e-6)
142+
}
143+
144+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_nan_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
145+
// CHECK: .*[/\\]EqualityWithAccuracy[/\\]main.swift:[[@LINE+3]]: error: EqualityWithAccuracyTests.test_equalWithAccuracy_nan_fails : XCTAssertEqual failed: \("nan"\) is not equal to \("nan"\) \+/- \("0.0"\) - $
146+
// CHECK: Test Case 'EqualityWithAccuracyTests.test_equalWithAccuracy_nan_fails' failed \(\d+\.\d+ seconds\)
147+
func test_equalWithAccuracy_nan_fails() {
148+
XCTAssertEqual(Double.nan, Double.nan, accuracy: 0)
149+
}
150+
151+
}
152+
// CHECK: Test Suite 'EqualityWithAccuracyTests' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
153+
// CHECK: \t Executed 11 tests, with 7 failures \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
154+
155+
XCTMain([testCase(EqualityWithAccuracyTests.allTests)])
156+
157+
// CHECK: Test Suite '.*\.xctest' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
158+
// CHECK: \t Executed 11 tests, with 7 failures \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
159+
// CHECK: Test Suite 'All tests' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
160+
// CHECK: \t Executed 11 tests, with 7 failures \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds

Tests/Functional/InfinityAccuracyTestCase/main.swift

-70
This file was deleted.

0 commit comments

Comments
 (0)