Skip to content

Commit f4333dc

Browse files
committed
Additional conformance changes for Measurement and unit tests to verify comparison and equality
After speaking with the current owner of Measurement, the most sensible path is to at runtime verifty the conformance of Units to a specific dimension since we cannot directly check at compile time. If at a future point in time a more specific comparitor can be added that can restrict the comparison to measurements in a specific dimension without causing equatable failures we may want to revisit this. However as it stands this preserves the most reasonable implementation of comparison of disperate measurement unit types while preserving the expected logic of conversions within that dimension.
1 parent 80f204d commit f4333dc

File tree

2 files changed

+47
-23
lines changed

2 files changed

+47
-23
lines changed

stdlib/public/SDK/Foundation/Measurement.swift

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,6 @@ extension Measurement where UnitType : Dimension {
110110
return Measurement(value: lhsValueInTermsOfBase - rhsValueInTermsOfBase, unit: type(of: lhs.unit).baseUnit())
111111
}
112112
}
113-
114-
/// Compare two measurements of the same `Dimension`.
115-
///
116-
/// If `lhs.unit == rhs.unit`, returns `lhs.value < rhs.value`. Otherwise, converts `rhs` to the same unit as `lhs` and then compares the resulting values.
117-
/// - returns: `true` if `lhs` is less than `rhs`.
118-
public static func <(lhs: Measurement<UnitType>, rhs: Measurement<UnitType>) -> Bool {
119-
if lhs.unit == rhs.unit {
120-
return lhs.value < rhs.value
121-
} else {
122-
let rhsInLhs = rhs.converted(to: lhs.unit)
123-
return lhs.value < rhsInLhs.value
124-
}
125-
}
126113
}
127114

128115
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
@@ -177,24 +164,38 @@ extension Measurement {
177164
///
178165
/// If `lhs.unit == rhs.unit`, returns `lhs.value == rhs.value`. Otherwise, converts `rhs` to the same unit as `lhs` and then compares the resulting values.
179166
/// - returns: `true` if the measurements are equal.
180-
public static func ==(_ lhs: Measurement<UnitType>, _ rhs: Measurement<UnitType>) -> Bool {
167+
public static func ==<LeftHandSideType : Unit, RightHandSideType : Unit>(_ lhs: Measurement<LeftHandSideType>, _ rhs: Measurement<RightHandSideType>) -> Bool {
181168
if lhs.unit == rhs.unit {
182169
return lhs.value == rhs.value
183170
} else {
184-
let rhsM = rhs as NSMeasurement
185-
if rhsM.canBeConverted(to: lhs.unit) {
186-
return lhs.value == rhsM.converting(to: lhs.unit).value
187-
} else {
188-
return false
171+
if let lhsDimensionalUnit = lhs.unit as? Dimension,
172+
let rhsDimensionalUnit = rhs.unit as? Dimension {
173+
if type(of: lhsDimensionalUnit).baseUnit() == type(of: rhsDimensionalUnit).baseUnit() {
174+
let lhsValueInTermsOfBase = lhsDimensionalUnit.converter.baseUnitValue(fromValue: lhs.value)
175+
let rhsValueInTermsOfBase = rhsDimensionalUnit.converter.baseUnitValue(fromValue: rhs.value)
176+
return lhsValueInTermsOfBase == rhsValueInTermsOfBase
177+
}
189178
}
179+
return false
190180
}
191181
}
192182

193183
/// Compare two measurements of the same `Unit`.
194-
/// - note: This function does not check `==` for the `unit` property of `lhs` and `rhs`.
195-
/// - returns: `lhs.value < rhs.value`
196-
public static func <(lhs: Measurement<UnitType>, rhs: Measurement<UnitType>) -> Bool {
197-
return lhs.value < rhs.value
184+
/// - returns: `true` if the measurements can be compared and the `lhs` is less than the `rhs` converted value.
185+
public static func <<LeftHandSideType : Unit, RightHandSideType : Unit>(lhs: Measurement<LeftHandSideType>, rhs: Measurement<RightHandSideType>) -> Bool {
186+
if lhs.unit == rhs.unit {
187+
return lhs.value < rhs.value
188+
} else {
189+
if let lhsDimensionalUnit = lhs.unit as? Dimension,
190+
let rhsDimensionalUnit = rhs.unit as? Dimension {
191+
if type(of: lhsDimensionalUnit).baseUnit() == type(of: rhsDimensionalUnit).baseUnit() {
192+
let lhsValueInTermsOfBase = lhsDimensionalUnit.converter.baseUnitValue(fromValue: lhs.value)
193+
let rhsValueInTermsOfBase = rhsDimensionalUnit.converter.baseUnitValue(fromValue: rhs.value)
194+
return lhsValueInTermsOfBase < rhsValueInTermsOfBase
195+
}
196+
}
197+
fatalError("Attempt to compare measurements with non-equal dimensions")
198+
}
198199
}
199200
}
200201

test/1_stdlib/TestMeasurement.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,27 @@ class TestMeasurement : TestMeasurementSuper {
126126
// Just make sure we get a result at all here
127127
expectFalse(result.isEmpty)
128128
}
129+
130+
func testEquality() {
131+
let fiveKM = Measurement(value: 5, unit: UnitLength.kilometers)
132+
let fiveSeconds = Measurement(value: 5, unit: UnitDuration.seconds)
133+
let fiveThousandM = Measurement(value: 5000, unit: UnitLength.meters)
134+
135+
expectTrue(fiveKM == fiveThousandM)
136+
expectEqual(fiveKM, fiveThousandM)
137+
expectFalse(fiveKM == fiveSeconds)
138+
}
139+
140+
func testComparison() {
141+
let fiveKM = Measurement(value: 5, unit: UnitLength.kilometers)
142+
let fiveThousandM = Measurement(value: 5000, unit: UnitLength.meters)
143+
let sixKM = Measurement(value: 6, unit: UnitLength.kilometers)
144+
let sevenThousandM = Measurement(value: 7000, unit: UnitLength.meters)
145+
146+
expectTrue(fiveKM < sixKM)
147+
expectTrue(fiveKM < sevenThousandM)
148+
expectTrue(fiveKM <= fiveThousandM)
149+
}
129150
}
130151

131152
#if !FOUNDATION_XCTEST
@@ -136,6 +157,8 @@ if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
136157
MeasurementTests.test("testOperators") { TestMeasurement().testOperators() }
137158
MeasurementTests.test("testUnits") { TestMeasurement().testUnits() }
138159
MeasurementTests.test("testMeasurementFormatter") { TestMeasurement().testMeasurementFormatter() }
160+
MeasurementTests.test("testEquality") { TestMeasurement().testEquality() }
161+
MeasurementTests.test("testComparison") { TestMeasurement().testComparison() }
139162
runAllTests()
140163
}
141164
#endif

0 commit comments

Comments
 (0)