From fbc87b69e7c08fa8788000282c79f6952929e6c5 Mon Sep 17 00:00:00 2001 From: Lily Vulcano Date: Thu, 29 Aug 2019 12:57:55 -0700 Subject: [PATCH] Parity: NSCoding stragglers: NSMeasurement - Add implementation and fixture tests. - NSUnitConverterReciprocal may be decoded from Darwin archives; make sure its name matches. --- Foundation/NSMeasurement.swift | 34 ++++++++++++++++-- Foundation/Unit.swift | 23 ++++++------ TestFoundation/FixtureValues.swift | 23 ++++++++++++ .../macOS-10.14/NSMeasurement-Angle.archive | Bin 0 -> 582 bytes .../NSMeasurement-Frequency.archive | Bin 0 -> 595 bytes .../macOS-10.14/NSMeasurement-Length.archive | Bin 0 -> 584 bytes .../macOS-10.14/NSMeasurement-Zero.archive | Bin 0 -> 306 bytes TestFoundation/TestMeasurement.swift | 30 ++++++++++++---- TestFoundation/TestUnitConverter.swift | 2 +- 9 files changed, 92 insertions(+), 20 deletions(-) create mode 100644 TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Angle.archive create mode 100644 TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Frequency.archive create mode 100644 TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Length.archive create mode 100644 TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Zero.archive diff --git a/Foundation/NSMeasurement.swift b/Foundation/NSMeasurement.swift index 35233833cc..7f0f1742c7 100644 --- a/Foundation/NSMeasurement.swift +++ b/Foundation/NSMeasurement.swift @@ -81,9 +81,39 @@ open class NSMeasurement : NSObject, NSCopying, NSSecureCoding { open class var supportsSecureCoding: Bool { return true } - open func encode(with aCoder: NSCoder) { NSUnimplemented() } + private enum NSCodingKeys { + static let value = "NS.value" + static let unit = "NS.unit" + } - public required init?(coder aDecoder: NSCoder) { NSUnimplemented() } + public required init?(coder aDecoder: NSCoder) { + let value = aDecoder.decodeDouble(forKey: NSCodingKeys.value) + guard let unit = aDecoder.decodeObject(of: Unit.self, forKey: NSCodingKeys.unit) else { + aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: CocoaError.coderReadCorrupt.rawValue, userInfo: [NSLocalizedDescriptionKey: "Unit class object has been corrupted!"])) + return nil + } + + self.doubleValue = value + self.unit = unit + } + + open func encode(with aCoder: NSCoder) { + guard aCoder.allowsKeyedCoding else { + fatalError("NSMeasurement cannot be encoded by non-keyed archivers") + } + + aCoder.encode(doubleValue, forKey: "NS.value") + aCoder.encode(unit, forKey: "NS.unit") + } + + open override func isEqual(_ object: Any?) -> Bool { + guard let measurement = object as? NSMeasurement else { return false } + return measurement.unit.isEqual(self.unit) && doubleValue == measurement.doubleValue + } + + open override var hash: Int { + return Int(doubleValue) ^ unit.hash + } } extension NSMeasurement : _StructTypeBridgeable { diff --git a/Foundation/Unit.swift b/Foundation/Unit.swift index 1baa48c447..f35e36ab34 100644 --- a/Foundation/Unit.swift +++ b/Foundation/Unit.swift @@ -94,25 +94,26 @@ open class UnitConverterLinear : UnitConverter, NSSecureCoding { } } -private class UnitConverterReciprocal : UnitConverter, NSSecureCoding { +// This must be named with a NS prefix because it can be sometimes encoded by Darwin, and we need to match the name in the archive. +internal class NSUnitConverterReciprocal : UnitConverter, NSSecureCoding { private var reciprocal: Double - fileprivate init(reciprocal: Double) { + init(reciprocal: Double) { self.reciprocal = reciprocal } - fileprivate override func baseUnitValue(fromValue value: Double) -> Double { + override func baseUnitValue(fromValue value: Double) -> Double { return reciprocal / value } - fileprivate override func value(fromBaseUnitValue baseUnitValue: Double) -> Double { + override func value(fromBaseUnitValue baseUnitValue: Double) -> Double { return reciprocal / baseUnitValue } - fileprivate required convenience init?(coder aDecoder: NSCoder) { + required convenience init?(coder aDecoder: NSCoder) { guard aDecoder.allowsKeyedCoding else { preconditionFailure("Unkeyed coding is unsupported.") } @@ -120,17 +121,17 @@ private class UnitConverterReciprocal : UnitConverter, NSSecureCoding { self.init(reciprocal: reciprocal) } - fileprivate func encode(with aCoder: NSCoder) { + func encode(with aCoder: NSCoder) { guard aCoder.allowsKeyedCoding else { preconditionFailure("Unkeyed coding is unsupported.") } aCoder.encode(self.reciprocal, forKey:"NS.reciprocal") } - fileprivate static var supportsSecureCoding: Bool { return true } + static var supportsSecureCoding: Bool { return true } open override func isEqual(_ object: Any?) -> Bool { - guard let other = object as? UnitConverterReciprocal else { + guard let other = object as? NSUnitConverterReciprocal else { return false } @@ -215,8 +216,8 @@ open class Dimension : Unit { preconditionFailure("Unkeyed coding is unsupported.") } guard - let symbol = aDecoder.decodeObject(forKey: "NS.symbol") as? String, - let converter = aDecoder.decodeObject(forKey: "NS.converter") as? UnitConverter + let symbol = aDecoder.decodeObject(of: NSString.self, forKey: "NS.symbol")?._swiftObject, + let converter = aDecoder.decodeObject(of: [UnitConverterLinear.self, NSUnitConverterReciprocal.self], forKey: "NS.converter") as? UnitConverter else { return nil } self.converter = converter super.init(symbol: symbol) @@ -1168,7 +1169,7 @@ public final class UnitFuelEfficiency : Dimension { } private convenience init(symbol: String, reciprocal: Double) { - self.init(symbol: symbol, converter: UnitConverterReciprocal(reciprocal: reciprocal)) + self.init(symbol: symbol, converter: NSUnitConverterReciprocal(reciprocal: reciprocal)) } public class var litersPer100Kilometers: UnitFuelEfficiency { diff --git a/TestFoundation/FixtureValues.swift b/TestFoundation/FixtureValues.swift index 53642b6c61..ebcdc88b1c 100644 --- a/TestFoundation/FixtureValues.swift +++ b/TestFoundation/FixtureValues.swift @@ -301,6 +301,25 @@ enum Fixtures { return NSMutableOrderedSet() } + // ===== NSMeasurement ===== + + static let zeroMeasurement = TypedFixture("NSMeasurement-Zero") { + let noUnit = Unit(symbol: "") + return NSMeasurement(doubleValue: 0, unit: noUnit) + } + + static let lengthMeasurement = TypedFixture("NSMeasurement-Length") { + return NSMeasurement(doubleValue: 45, unit: UnitLength.miles) + } + + static let frequencyMeasurement = TypedFixture("NSMeasurement-Frequency") { + return NSMeasurement(doubleValue: 1400, unit: UnitFrequency.megahertz) + } + + static let angleMeasurement = TypedFixture("NSMeasurement-Angle") { + return NSMeasurement(doubleValue: 90, unit: UnitAngle.degrees) + } + // ===== Fixture list ===== static let _listOfAllFixtures: [AnyFixture] = [ @@ -341,6 +360,10 @@ enum Fixtures { AnyFixture(Fixtures.orderedSetEmpty), AnyFixture(Fixtures.mutableOrderedSetOfNumbers), AnyFixture(Fixtures.mutableOrderedSetEmpty), + AnyFixture(Fixtures.zeroMeasurement), + AnyFixture(Fixtures.lengthMeasurement), + AnyFixture(Fixtures.frequencyMeasurement), + AnyFixture(Fixtures.angleMeasurement), ] // This ensures that we do not have fixtures with duplicate identifiers: diff --git a/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Angle.archive b/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Angle.archive new file mode 100644 index 0000000000000000000000000000000000000000..e9f4f3c6cfbc9b021c9bd819b796f83fb7a78543 GIT binary patch literal 582 zcmYc)$jK}&F)+Bo$i&RT%4T2~p;DGwRGgWg7on1$l$DxXQXHw0Sd^TR2^0=dDakJo zVqk1ru!5a~UtB^?O(#?(uQVs;GA9=|4=-Q1U$9%IkS`C zAnwRZ%uS7eFjI@;1w{RVLxHY!hFR;AnU|Vav{+e16_;GR06&@@pjZ9D!34BNT?6PC zkcp0Y={c!OG_|xFUM$1^cEZ7MB*K0wqfp>8qj&fbH~7 ztxQdEghsw{QQyzMg|TBK?W(HcXb(z7|ahU l8Os^#7@HX%IkSYm=K{A!ON%=W~0!$4o z4a^N}!M+|^#mXwGxa8sm_|fzL{pt@6C!js* z8gO~HqSV6D)V$=%C7N2=K;xj=;KD#xyJY63=7B;w3}PhE0A0OUzhK|g#NyJTRG?(Z zB7Id<0kA>dsgSq60l5+5SqaRN<%5e(*XOCu%iF~ literal 0 HcmV?d00001 diff --git a/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Length.archive b/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Length.archive new file mode 100644 index 0000000000000000000000000000000000000000..5c0fb5c04774f4d432436a66826316045975c6e3 GIT binary patch literal 584 zcmYc)$jK}&F)+Bo$i&RT%4T2~p;DGwRGgWg7on1$l$DxXQXHw0Sd^TR2^0=dDakJo zVqk1ru!5a~UtB^?O(#?(uQVs;GA9=|4=-Q1U$9%IkS^ z3V?0(POVH$afC+1MI+;oqWt`l21W)B20;cX24x0a1|tS@1_uT=2499yhB$^)hIEDk zh9ZV)h7N|Q4D%TlF)U$N#;}56HNz%`gA8XGt})zac*yXK;UmK@Mj=KqMmtG+7?&_^W8BHO4-%kEjL?8Jfdw#xW;_4@W)7~9 literal 0 HcmV?d00001 diff --git a/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Zero.archive b/TestFoundation/Fixtures/macOS-10.14/NSMeasurement-Zero.archive new file mode 100644 index 0000000000000000000000000000000000000000..d4a44980c47d8ac3ce778778ab4bb1c5a2f4c5cd GIT binary patch literal 306 zcmYc)$jK}&F)+Bo$i&RT$|fftp;DGwRGgWg7on1$l$DxXQXHw0Sd^TR2^0=dDakJo zVqk1ru#BC9UsyycR3)!8C+9LJ7dHhOySiXlb$`6UgE9D-8H wx<=-9F5ba0$*Fk-rS*-?EuB;5E?K@|>)~Tpp1phraVjGt)X7Rv8b*Bp018xH)&Kwi literal 0 HcmV?d00001 diff --git a/TestFoundation/TestMeasurement.swift b/TestFoundation/TestMeasurement.swift index 77eb701237..97143b4014 100644 --- a/TestFoundation/TestMeasurement.swift +++ b/TestFoundation/TestMeasurement.swift @@ -23,12 +23,6 @@ class CustomUnit: Unit { #endif class TestMeasurement: XCTestCase { - static var allTests: [(String, (TestMeasurement) -> () throws -> Void)] { - return [ - ("testHashing", testHashing), - ] - } - func testHashing() { let lengths: [[Measurement]] = [ [ @@ -75,5 +69,29 @@ class TestMeasurement: XCTestCase { checkHashable(custom, equalityOracle: { $0 == $1 }) #endif } + + let fixtures = [ + Fixtures.zeroMeasurement, + Fixtures.lengthMeasurement, + Fixtures.frequencyMeasurement, + Fixtures.angleMeasurement, + ] + func testCodingRoundtrip() throws { + for fixture in fixtures { + try fixture.assertValueRoundtripsInCoder() + } + } + + func testLoadedValuesMatch() throws { + for fixture in fixtures { + try fixture.assertLoadedValuesMatch() + } + } + + static let allTests = [ + ("testHashing", testHashing), + ("testCodingRoundtrip", testCodingRoundtrip), + ("testLoadedValuesMatch", testLoadedValuesMatch), + ] } diff --git a/TestFoundation/TestUnitConverter.swift b/TestFoundation/TestUnitConverter.swift index 01545c5cdf..f5a3990a38 100644 --- a/TestFoundation/TestUnitConverter.swift +++ b/TestFoundation/TestUnitConverter.swift @@ -273,7 +273,7 @@ class TestUnitConverter: XCTestCase { XCTAssertNotEqual(u1, u4) XCTAssertNotEqual(u4, u1) - // Cannot test UnitConverterReciprocal due to no support for @testable import. + // Cannot test NSUnitConverterReciprocal due to no support for @testable import. } }