Skip to content

Commit 1c25df6

Browse files
authored
Merge pull request #879 from Quick/becloseto-with-floatingpoint-protocol
[BREAKING] Use FloatingPoint protocol in `beCloseTo` matcher
2 parents dcf7d9a + 4e26051 commit 1c25df6

File tree

2 files changed

+91
-50
lines changed

2 files changed

+91
-50
lines changed

Sources/Nimble/Matchers/BeCloseTo.swift

Lines changed: 91 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,66 @@
11
import Foundation
22

33
// swiftlint:disable:next identifier_name
4-
public let DefaultDelta = 0.0001
5-
6-
internal func isCloseTo(_ actualValue: NMBDoubleConvertible?,
7-
expectedValue: NMBDoubleConvertible,
8-
delta: Double)
9-
-> PredicateResult {
10-
let errorMessage = "be close to <\(stringify(expectedValue))> (within \(stringify(delta)))"
11-
return PredicateResult(
12-
bool: actualValue != nil &&
13-
abs(actualValue!.doubleValue - expectedValue.doubleValue) < delta,
14-
message: .expectedCustomValueTo(errorMessage, actual: "<\(stringify(actualValue))>")
15-
)
4+
public let DefaultDelta: Double = 0.0001
5+
6+
public func defaultDelta<F: FloatingPoint>() -> F { 1/10000 /* 0.0001 */ }
7+
8+
internal func isCloseTo<Value: FloatingPoint>(
9+
_ actualValue: Value?,
10+
expectedValue: Value,
11+
delta: Value
12+
) -> PredicateResult {
13+
let errorMessage = "be close to <\(stringify(expectedValue))> (within \(stringify(delta)))"
14+
return PredicateResult(
15+
bool: actualValue != nil &&
16+
abs(actualValue! - expectedValue) < delta,
17+
message: .expectedCustomValueTo(errorMessage, actual: "<\(stringify(actualValue))>")
18+
)
19+
}
20+
21+
internal func isCloseTo(
22+
_ actualValue: NMBDoubleConvertible?,
23+
expectedValue: NMBDoubleConvertible,
24+
delta: Double
25+
) -> PredicateResult {
26+
let errorMessage = "be close to <\(stringify(expectedValue))> (within \(stringify(delta)))"
27+
return PredicateResult(
28+
bool: actualValue != nil &&
29+
abs(actualValue!.doubleValue - expectedValue.doubleValue) < delta,
30+
message: .expectedCustomValueTo(errorMessage, actual: "<\(stringify(actualValue))>")
31+
)
1632
}
1733

1834
/// A Nimble matcher that succeeds when a value is close to another. This is used for floating
1935
/// point values which can have imprecise results when doing arithmetic on them.
2036
///
2137
/// @see equal
22-
public func beCloseTo<Value: NMBDoubleConvertible>(_ expectedValue: Value, within delta: Double = DefaultDelta) -> Predicate<Value> {
38+
public func beCloseTo<Value: FloatingPoint>(
39+
_ expectedValue: Value,
40+
within delta: Value = defaultDelta()
41+
) -> Predicate<Value> {
2342
return Predicate.define { actualExpression in
2443
return isCloseTo(try actualExpression.evaluate(), expectedValue: expectedValue, delta: delta)
2544
}
2645
}
2746

28-
private func beCloseTo(_ expectedValue: NMBDoubleConvertible, within delta: Double = DefaultDelta) -> Predicate<NMBDoubleConvertible> {
47+
/// A Nimble matcher that succeeds when a value is close to another. This is used for floating
48+
/// point values which can have imprecise results when doing arithmetic on them.
49+
///
50+
/// @see equal
51+
public func beCloseTo<Value: NMBDoubleConvertible>(
52+
_ expectedValue: Value,
53+
within delta: Double = DefaultDelta
54+
) -> Predicate<Value> {
55+
return Predicate.define { actualExpression in
56+
return isCloseTo(try actualExpression.evaluate(), expectedValue: expectedValue, delta: delta)
57+
}
58+
}
59+
60+
private func beCloseTo(
61+
_ expectedValue: NMBDoubleConvertible,
62+
within delta: Double = DefaultDelta
63+
) -> Predicate<NMBDoubleConvertible> {
2964
return Predicate.define { actualExpression in
3065
return isCloseTo(try actualExpression.evaluate(), expectedValue: expectedValue, delta: delta)
3166
}
@@ -61,48 +96,68 @@ extension NMBPredicate {
6196
}
6297
#endif
6398

64-
public func beCloseTo(_ expectedValues: [Double], within delta: Double = DefaultDelta) -> Predicate<[Double]> {
99+
public func beCloseTo<Value: FloatingPoint, Values: Collection>(
100+
_ expectedValues: Values,
101+
within delta: Value = defaultDelta()
102+
) -> Predicate<Values> where Values.Element == Value {
65103
let errorMessage = "be close to <\(stringify(expectedValues))> (each within \(stringify(delta)))"
66104
return Predicate.simple(errorMessage) { actualExpression in
67-
if let actual = try actualExpression.evaluate() {
68-
if actual.count != expectedValues.count {
105+
guard let actualValues = try actualExpression.evaluate() else {
106+
return .doesNotMatch
107+
}
108+
109+
if actualValues.count != expectedValues.count {
110+
return .doesNotMatch
111+
}
112+
113+
for index in actualValues.indices {
114+
if abs(actualValues[index] - expectedValues[index]) > delta {
69115
return .doesNotMatch
70-
} else {
71-
for (index, actualItem) in actual.enumerated() {
72-
if fabs(actualItem - expectedValues[index]) > delta {
73-
return .doesNotMatch
74-
}
75-
}
76-
return .matches
77116
}
78117
}
79-
return .doesNotMatch
118+
return .matches
80119
}
81120
}
82121

83122
// MARK: - Operators
84123

85124
infix operator : ComparisonPrecedence
86125

87-
extension Expectation where T == [Double] {
126+
extension Expectation where T: Collection, T.Element: FloatingPoint {
88127
// swiftlint:disable:next identifier_name
89-
public static func (lhs: Expectation, rhs: [Double]) {
128+
public static func (lhs: Expectation, rhs: T) {
90129
lhs.to(beCloseTo(rhs))
91130
}
92131
}
93132

94-
extension Expectation where T == NMBDoubleConvertible {
133+
extension Expectation where T: FloatingPoint {
95134
// swiftlint:disable:next identifier_name
96-
public static func (lhs: Expectation, rhs: NMBDoubleConvertible) {
135+
public static func (lhs: Expectation, rhs: T) {
97136
lhs.to(beCloseTo(rhs))
98137
}
99138

100139
// swiftlint:disable:next identifier_name
101-
public static func (lhs: Expectation, rhs: (expected: NMBDoubleConvertible, delta: Double)) {
140+
public static func (lhs: Expectation, rhs: (expected: T, delta: T)) {
102141
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
103142
}
104143

105-
public static func == (lhs: Expectation, rhs: (expected: NMBDoubleConvertible, delta: Double)) {
144+
public static func == (lhs: Expectation, rhs: (expected: T, delta: T)) {
145+
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
146+
}
147+
}
148+
149+
extension Expectation where T: NMBDoubleConvertible {
150+
// swiftlint:disable:next identifier_name
151+
public static func (lhs: Expectation, rhs: T) {
152+
lhs.to(beCloseTo(rhs))
153+
}
154+
155+
// swiftlint:disable:next identifier_name
156+
public static func (lhs: Expectation, rhs: (expected: T, delta: Double)) {
157+
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
158+
}
159+
160+
public static func == (lhs: Expectation, rhs: (expected: T, delta: Double)) {
106161
lhs.to(beCloseTo(rhs.expected, within: rhs.delta))
107162
}
108163
}
@@ -115,6 +170,10 @@ precedencegroup PlusMinusOperatorPrecedence {
115170

116171
infix operator ± : PlusMinusOperatorPrecedence
117172
// swiftlint:disable:next identifier_name
118-
public func ±(lhs: NMBDoubleConvertible, rhs: Double) -> (expected: NMBDoubleConvertible, delta: Double) {
173+
public func ±<Value: FloatingPoint>(lhs: Value, rhs: Value) -> (expected: Value, delta: Value) {
174+
return (expected: lhs, delta: rhs)
175+
}
176+
// swiftlint:disable:next identifier_name
177+
public func ±<Value: NMBDoubleConvertible>(lhs: Value, rhs: Double) -> (expected: Value, delta: Double) {
119178
return (expected: lhs, delta: rhs)
120179
}

Sources/Nimble/Matchers/MatcherProtocols.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,6 @@ public protocol NMBDoubleConvertible {
4343
var doubleValue: CDouble { get }
4444
}
4545

46-
extension Double: NMBDoubleConvertible {
47-
public var doubleValue: CDouble {
48-
return self
49-
}
50-
}
51-
52-
extension Float: NMBDoubleConvertible {
53-
public var doubleValue: CDouble {
54-
return CDouble(self)
55-
}
56-
}
57-
58-
extension CGFloat: NMBDoubleConvertible {
59-
public var doubleValue: CDouble {
60-
return CDouble(self)
61-
}
62-
}
63-
6446
extension NSNumber: NMBDoubleConvertible {
6547
}
6648

0 commit comments

Comments
 (0)