Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of MassFormatter. #883

Merged
merged 1 commit into from
Jun 2, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Docs/Status.md
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ There is no _Complete_ status for test coverage because there are always additio
| `EnergyFormatter` | Unimplemented | None | |
| `ISO8601DateFormatter` | Unimplemented | None | |
| `LengthFormatter` | Complete | Substantial | |
| `MassFormatter` | Unimplemented | None | |
| `MassFormatter` | Complete | Substantial | Needs localization |
| `NumberFormatter` | Mostly Complete | Substantial | `objectValue(_:range:)` remains unimplemented |
| `PersonNameComponentsFormatter` | Unimplemented | None | |
| `ByteCountFormatter` | Mostly Complete | Substantial | `init?(coder:)` remains unimplemented |
6 changes: 6 additions & 0 deletions Foundation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -315,6 +315,7 @@
7900433B1CACD33E00ECCBF1 /* TestNSCompoundPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790043391CACD33E00ECCBF1 /* TestNSCompoundPredicate.swift */; };
7900433C1CACD33E00ECCBF1 /* TestNSPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7900433A1CACD33E00ECCBF1 /* TestNSPredicate.swift */; };
90E645DF1E4C89A400D0D47C /* TestNSCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E645DE1E4C89A400D0D47C /* TestNSCache.swift */; };
A058C2021E529CF100B07AA1 /* TestMassFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A058C2011E529CF100B07AA1 /* TestMassFormatter.swift */; };
AE35A1861CBAC85E0042DB84 /* SwiftFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = AE35A1851CBAC85E0042DB84 /* SwiftFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
BD8042161E09857800487EB8 /* TestNSLengthFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD8042151E09857800487EB8 /* TestNSLengthFormatter.swift */; };
BDBB65901E256BFA001A7286 /* TestNSEnergyFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBB658F1E256BFA001A7286 /* TestNSEnergyFormatter.swift */; };
@@ -763,6 +764,7 @@
84BA558D1C16F90900F48C54 /* TestNSTimeZone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSTimeZone.swift; sourceTree = "<group>"; };
88D28DE61C13AE9000494606 /* TestNSGeometry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSGeometry.swift; sourceTree = "<group>"; };
90E645DE1E4C89A400D0D47C /* TestNSCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSCache.swift; sourceTree = "<group>"; };
A058C2011E529CF100B07AA1 /* TestMassFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMassFormatter.swift; sourceTree = "<group>"; };
A5A34B551C18C85D00FD972B /* TestNSByteCountFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSByteCountFormatter.swift; sourceTree = "<group>"; };
AE35A1851CBAC85E0042DB84 /* SwiftFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftFoundation.h; sourceTree = "<group>"; };
BD8042151E09857800487EB8 /* TestNSLengthFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSLengthFormatter.swift; sourceTree = "<group>"; };
@@ -1404,6 +1406,7 @@
D3047AEB1C38BC3300295652 /* TestNSValue.swift */,
5B6F17951C48631C00935030 /* TestNSXMLDocument.swift */,
5B40F9F11C125187000E72E3 /* TestNSXMLParser.swift */,
A058C2011E529CF100B07AA1 /* TestMassFormatter.swift */,
CC5249BF1D341D23007CB54D /* TestUnitConverter.swift */,
5B6F17961C48631C00935030 /* TestUtils.swift */,
0383A1741D2E558A0052E5D1 /* TestNSStream.swift */,
@@ -2251,6 +2254,9 @@
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */,
5B13B3471C582D4C00651CE2 /* TestThread.swift in Sources */,
5B13B32E1C582D4C00651CE2 /* TestFileManager.swift in Sources */,
5B13B3471C582D4C00651CE2 /* TestNSThread.swift in Sources */,
5B13B32E1C582D4C00651CE2 /* TestNSFileManager.swift in Sources */,
A058C2021E529CF100B07AA1 /* TestMassFormatter.swift in Sources */,
5B13B3381C582D4C00651CE2 /* TestNSNotificationQueue.swift in Sources */,
CC5249C01D341D23007CB54D /* TestUnitConverter.swift in Sources */,
5B13B3331C582D4C00651CE2 /* TestNSJSONSerialization.swift in Sources */,
20 changes: 2 additions & 18 deletions Foundation/NSLengthFormatter.swift
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ open class LengthFormatter : Formatter {
//Extract the number from the measurement
let numberInUnit = unitMeasurement.value

if isForPersonHeightUse && !LengthFormatter.isMetricSystemLocale(numberFormatter.locale) {
if isForPersonHeightUse && !numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
let feet = numberInUnit.rounded(.towardZero)
let feetString = string(fromValue: feet, unit: .foot)

@@ -123,7 +123,7 @@ open class LengthFormatter : Formatter {
/// - Parameter numberInMeters: the magnitude in terms of meters
/// - Returns: Returns the appropriate unit
private func unit(fromMeters numberInMeters: Double) -> Unit {
if LengthFormatter.isMetricSystemLocale(numberFormatter.locale) {
if numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
//Person height is always returned in cm for metric system
if isForPersonHeightUse { return .centimeter }

@@ -152,22 +152,6 @@ open class LengthFormatter : Formatter {
}
}

/// TODO: Replace calls to the below function to use Locale.usesMetricSystem
/// Temporary workaround due to unpopulated Locale attributes
/// See https://bugs.swift.org/browse/SR-3202
private static func isMetricSystemLocale(_ locale: Locale) -> Bool {
switch locale.identifier {
case "en_US": return false
case "en_US_POSIX": return false
case "haw_US": return false
case "es_US": return false
case "chr_US": return false
case "my_MM": return false
case "en_LR": return false
case "vai_LR": return false
default: return true
}
}

/// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative
/// - Note: Since this API is under consideration it may be either removed or revised in the near future
221 changes: 215 additions & 6 deletions Foundation/NSMassFormatter.swift
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@

extension MassFormatter {
public enum Unit : Int {

case gram
case kilogram
case ounce
@@ -21,28 +20,238 @@ extension MassFormatter {

open class MassFormatter : Formatter {

public override init() {
numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
unitStyle = .medium
isForPersonMassUse = false
super.init()
}

public required init?(coder: NSCoder) {
NSUnimplemented()
numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
unitStyle = .medium
isForPersonMassUse = false
super.init(coder:coder)
}

/*@NSCopying*/ open var numberFormatter: NumberFormatter! // default is NSNumberFormatter with NSNumberFormatterDecimalStyle
open var unitStyle: UnitStyle // default is NSFormattingUnitStyleMedium

open var isForPersonMassUse: Bool // default is NO; if it is set to YES, the number argument for -stringFromKilograms: and -unitStringFromKilograms: is considered as a person’s mass

// Format a combination of a number and an unit to a localized string.
open func string(fromValue value: Double, unit: Unit) -> String { NSUnimplemented() }
open func string(fromValue value: Double, unit: Unit) -> String {
// special case: stone shows fractional values in pounds
if unit == .stone {
let stone = value.rounded(.towardZero)
let stoneString = singlePartString(fromValue: stone, unit: unit) // calling `string(fromValue: stone, unit: .stone)` would infinitely recur
let pounds = abs(value.truncatingRemainder(dividingBy: 1.0)) * MassFormatter.poundsPerStone

// if we don't have any fractional component, don't append anything
if pounds == 0 {
return stoneString
} else {
let poundsString = string(fromValue: pounds, unit: .pound)
let separator = unitStyle == MassFormatter.UnitStyle.short ? " " : ", "

return ("\(stoneString)\(separator)\(poundsString)")
}
}

// normal case: kilograms and pounds
return singlePartString(fromValue: value, unit: unit)
}

// Format a number in kilograms to a localized string with the locale-appropriate unit and an appropriate scale (e.g. 1.2kg = 2.64lb in the US locale).
open func string(fromKilograms numberInKilograms: Double) -> String { NSUnimplemented() }
open func string(fromKilograms numberInKilograms: Double) -> String {
//Convert to the locale-appropriate unit
let unitFromKilograms = convertedUnit(fromKilograms: numberInKilograms)

//Map the unit to UnitMass type for conversion later
let unitMassFromKilograms = MassFormatter.unitMass[unitFromKilograms]!

//Create a measurement object based on the value in kilograms
let kilogramMeasurement = Measurement<UnitMass>(value:numberInKilograms, unit: .kilograms)

//Convert the object to the locale-appropriate unit determined above
let unitMeasurement = kilogramMeasurement.converted(to: unitMassFromKilograms)

//Extract the number from the measurement
let numberInUnit = unitMeasurement.value

return string(fromValue: numberInUnit, unit: unitFromKilograms)
}

// Return a localized string of the given unit, and if the unit is singular or plural is based on the given number.
open func unitString(fromValue value: Double, unit: Unit) -> String { NSUnimplemented() }
open func unitString(fromValue value: Double, unit: Unit) -> String {
if unitStyle == .short {
return MassFormatter.shortSymbol[unit]!
} else if unitStyle == .medium {
return MassFormatter.mediumSymbol[unit]!
} else if unit == .stone { // special case, see `unitStringDisplayedAdjacent(toValue:, unit:)`
return MassFormatter.largeSingularSymbol[unit]!
} else if value == 1.0 {
return MassFormatter.largeSingularSymbol[unit]!
} else {
return MassFormatter.largePluralSymbol[unit]!
}
}

// Return the locale-appropriate unit, the same unit used by -stringFromKilograms:.
open func unitString(fromKilograms numberInKilograms: Double, usedUnit unitp: UnsafeMutablePointer<Unit>?) -> String { NSUnimplemented() }
open func unitString(fromKilograms numberInKilograms: Double, usedUnit unitp: UnsafeMutablePointer<Unit>?) -> String {
//Convert to the locale-appropriate unit
let unitFromKilograms = convertedUnit(fromKilograms: numberInKilograms)
unitp?.pointee = unitFromKilograms

//Map the unit to UnitMass type for conversion later
let unitMassFromKilograms = MassFormatter.unitMass[unitFromKilograms]!

//Create a measurement object based on the value in kilograms
let kilogramMeasurement = Measurement<UnitMass>(value:numberInKilograms, unit: .kilograms)

//Convert the object to the locale-appropriate unit determined above
let unitMeasurement = kilogramMeasurement.converted(to: unitMassFromKilograms)

//Extract the number from the measurement
let numberInUnit = unitMeasurement.value

//Return the appropriate representation of the unit based on the selected unit style
return unitString(fromValue: numberInUnit, unit: unitFromKilograms)
}

/// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative
/// - Note: Since this API is under consideration it may be either removed or revised in the near future
open override func objectValue(_ string: String) throws -> Any? { return nil }


// MARK: - Private

/// This method selects the appropriate unit based on the formatter’s locale,
/// the magnitude of the value, and isForPersonMassUse property.
///
/// - Parameter numberInKilograms: the magnitude in terms of kilograms
/// - Returns: Returns the appropriate unit
private func convertedUnit(fromKilograms numberInKilograms: Double) -> Unit {
if numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
if numberInKilograms > 1.0 || numberInKilograms <= 0.0 {
return .kilogram
} else {
return .gram
}
} else {
let metricMeasurement = Measurement<UnitMass>(value:numberInKilograms, unit: .kilograms)
let imperialMeasurement = metricMeasurement.converted(to: .pounds)
let numberInPounds = imperialMeasurement.value

if numberInPounds >= 1.0 || numberInPounds <= 0.0 {
return .pound
} else {
return .ounce
}
}
}

/// Formats the given value and unit into a string containing one logical
/// value. This is intended for units like kilogram and pound where
/// fractional values are represented as a decimal instead of converted
/// values in another unit.
///
/// - Parameter value: The mass's value in the given unit.
/// - Parameter unit: The unit used in the resulting mass string.
/// - Returns: A properly formatted mass string for the given value and unit.
private func singlePartString(fromValue value: Double, unit: Unit) -> String {
guard let formattedValue = numberFormatter.string(from:NSNumber(value: value)) else {
fatalError("Cannot format \(value) as string")
}

let separator = unitStyle == MassFormatter.UnitStyle.short ? "" : " "

return "\(formattedValue)\(separator)\(unitStringDisplayedAdjacent(toValue: value, unit: unit))"
}

/// Return the locale-appropriate unit to be shown adjacent to the given
/// value. In most cases this will match `unitStringDisplayedAdjacent(toValue:, unit:)`
/// however there are a few special cases:
/// - Imperial pounds with a short representation use "lb" in the
/// abstract and "#" only when shown with a numeral.
/// - Stones are are singular in the abstract and only plural when
/// shown with a numeral.
///
/// - Parameter value: The mass's value in the given unit.
/// - Parameter unit: The unit used in the resulting mass string.
/// - Returns: The locale-appropriate unit
open func unitStringDisplayedAdjacent(toValue value: Double, unit: Unit) -> String {
if unit == .pound && unitStyle == .short {
return "#"
} else if unit == .stone && unitStyle == .long {
if value == 1.0 {
return MassFormatter.largeSingularSymbol[unit]!
} else {
return MassFormatter.largePluralSymbol[unit]!
}
} else {
return unitString(fromValue: value, unit: unit)
}
}



/// The number of pounds in 1 stone
private static let poundsPerStone = 14.0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal was to use Measurement<UnitMass>(value:1, unit:.stones).converted(to:.pounds) here, but that ends up being 0.3471688213196 lb instead of the expected 14 lb.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I use stones regularly, but this seems like an issue :-)
Coefficient should be 6.35029? https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Unit.swift#L1330

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@e78l Those are kilograms not pounds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was referring to the 1 stone=0.347 lb conversion problem by 'this' - changing 0.157473 on L1330 of Unit.swift to 6.35029 should solve that problem.
Appears macOS 10.12.3's Foundation has the stone conversion issue too.
Hard-coding poundsPerStone = 14.0 looks great (and perhaps is the better way)!


/// Maps MassFormatter.Unit enum to UnitMass class. Used for measurement conversion.
private static let unitMass: [Unit: UnitMass] = [.gram: .grams,
.kilogram: .kilograms,
.ounce: .ounces,
.pound: .pounds,
.stone: .stones]

/// Maps a unit to its short symbol. Reuses strings from UnitMass.
private static let shortSymbol: [Unit: String] = [.gram: UnitMass.grams.symbol,
.kilogram: UnitMass.kilograms.symbol,
.ounce: UnitMass.ounces.symbol,
.pound: UnitMass.pounds.symbol, // see `unitStringDisplayedAdjacent(toValue:, unit:)`
.stone: UnitMass.stones.symbol]

/// Maps a unit to its medium symbol. Reuses strings from UnitMass.
private static let mediumSymbol: [Unit: String] = [.gram: UnitMass.grams.symbol,
.kilogram: UnitMass.kilograms.symbol,
.ounce: UnitMass.ounces.symbol,
.pound: UnitMass.pounds.symbol,
.stone: UnitMass.stones.symbol]

/// Maps a unit to its large, singular symbol.
private static let largeSingularSymbol: [Unit: String] = [.gram: "gram",
.kilogram: "kilogram",
.ounce: "ounce",
.pound: "pound",
.stone: "stone"]

/// Maps a unit to its large, plural symbol.
private static let largePluralSymbol: [Unit: String] = [.gram: "grams",
.kilogram: "kilograms",
.ounce: "ounces",
.pound: "pounds",
.stone: "stones"]
}

internal extension Locale {
/// TODO: Replace calls to the below function to use Locale.usesMetricSystem
/// Temporary workaround due to unpopulated Locale attributes
/// See https://bugs.swift.org/browse/SR-3202
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SR-3202 is fixed by #1020 - can this workaround be removed @jszumski ?

internal func sr3202_fix_isMetricSystemLocale() -> Bool {
switch self.identifier {
case "en_US": return false
case "en_US_POSIX": return false
case "haw_US": return false
case "es_US": return false
case "chr_US": return false
case "my_MM": return false
case "en_LR": return false
case "vai_LR": return false
default: return true
}
}
}
140 changes: 140 additions & 0 deletions TestFoundation/TestMassFormatter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//

#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
import Foundation
import XCTest
#else
import SwiftFoundation
import SwiftXCTest
#endif

class TestMassFormatter: XCTestCase {
let formatter: MassFormatter = MassFormatter()

static var allTests: [(String, (TestMassFormatter) -> () throws -> Void)] {
return [
("test_stringFromKilogramsImperialRegion", test_stringFromKilogramsImperialRegion),
("test_stringFromKilogramsMetricRegion", test_stringFromKilogramsMetricRegion),
("test_stringFromKilogramsMetricRegionPersonMassUse", test_stringFromKilogramsMetricRegionPersonMassUse),
("test_stringFromValue", test_stringFromValue),
("test_unitStringFromKilograms", test_unitStringFromKilograms),
("test_unitStringFromValue", test_unitStringFromValue),
]
}

override func setUp() {
formatter.numberFormatter.locale = Locale(identifier: "en_US")
formatter.isForPersonMassUse = false
super.setUp()
}

func test_stringFromKilogramsImperialRegion() {
XCTAssertEqual(formatter.string(fromKilograms: -100), "-220.462 lb")
XCTAssertEqual(formatter.string(fromKilograms: 0.00001), "0 oz")
XCTAssertEqual(formatter.string(fromKilograms: 0.0001), "0.004 oz")
XCTAssertEqual(formatter.string(fromKilograms: 1), "2.205 lb")
XCTAssertEqual(formatter.string(fromKilograms: 100), "220.462 lb")
}

func test_stringFromKilogramsMetricRegion() {
formatter.numberFormatter.locale = Locale(identifier: "de_DE")
XCTAssertEqual(formatter.string(fromKilograms: -100), "-100 kg")
XCTAssertEqual(formatter.string(fromKilograms: -1), "-1 kg")
XCTAssertEqual(formatter.string(fromKilograms: 1000), "1.000 kg")
}

func test_stringFromKilogramsMetricRegionPersonMassUse() {
formatter.numberFormatter.locale = Locale(identifier: "en_GB")
formatter.isForPersonMassUse = true
XCTAssertEqual(formatter.string(fromKilograms: -100), "-100 kg")
XCTAssertEqual(formatter.string(fromKilograms: -1), "-1 kg")
XCTAssertEqual(formatter.string(fromKilograms: 1000), "1,000 kg")
}

func test_stringFromValue() {
formatter.unitStyle = Formatter.UnitStyle.long
XCTAssertEqual(formatter.string(fromValue: 0.002, unit: MassFormatter.Unit.kilogram),"0.002 kilograms")
XCTAssertEqual(formatter.string(fromValue: 0, unit:MassFormatter.Unit.stone), "0 stones")
XCTAssertEqual(formatter.string(fromValue: 1, unit:MassFormatter.Unit.stone), "1 stone")
XCTAssertEqual(formatter.string(fromValue: 2.4, unit: MassFormatter.Unit.stone), "2 stones, 5.6 pounds")

formatter.unitStyle = Formatter.UnitStyle.short
XCTAssertEqual(formatter.string(fromValue: 0.00000001, unit:MassFormatter.Unit.kilogram), "0kg")
XCTAssertEqual(formatter.string(fromValue: 6, unit:MassFormatter.Unit.pound), "6#")
XCTAssertEqual(formatter.string(fromValue: 2.4, unit: MassFormatter.Unit.stone), "2st 5.6#")
XCTAssertEqual(formatter.string(fromValue: 123456, unit: MassFormatter.Unit.stone), "123,456st")

formatter.unitStyle = Formatter.UnitStyle.medium
XCTAssertEqual(formatter.string(fromValue: 0.00000001, unit:MassFormatter.Unit.kilogram), "0 kg")
XCTAssertEqual(formatter.string(fromValue: 2.4, unit: MassFormatter.Unit.stone), "2 st, 5.6 lb")
XCTAssertEqual(formatter.string(fromValue: 2.0, unit: MassFormatter.Unit.stone), "2 st")
XCTAssertEqual(formatter.string(fromValue: 123456.78, unit: MassFormatter.Unit.stone), "123,456 st, 10.92 lb")
}

func test_unitStringFromKilograms() {
var unit = MassFormatter.Unit.kilogram

// imperial
XCTAssertEqual(formatter.unitString(fromKilograms: -100000, usedUnit: &unit), "lb")
XCTAssertEqual(unit, MassFormatter.Unit.pound)

XCTAssertEqual(formatter.unitString(fromKilograms: 0, usedUnit: &unit), "lb")
XCTAssertEqual(unit, MassFormatter.Unit.pound)

XCTAssertEqual(formatter.unitString(fromKilograms: 0.0001, usedUnit: &unit), "oz")
XCTAssertEqual(unit, MassFormatter.Unit.ounce)

XCTAssertEqual(formatter.unitString(fromKilograms: 0.4535, usedUnit: &unit), "oz")
XCTAssertEqual(unit, MassFormatter.Unit.ounce)

XCTAssertEqual(formatter.unitString(fromKilograms: 0.4536, usedUnit: &unit), "lb")
XCTAssertEqual(unit, MassFormatter.Unit.pound)

// metric
formatter.numberFormatter.locale = Locale(identifier: "de_DE")
XCTAssertEqual(formatter.unitString(fromKilograms: -100000, usedUnit: &unit), "kg")
XCTAssertEqual(unit, MassFormatter.Unit.kilogram)

XCTAssertEqual(formatter.unitString(fromKilograms: 0, usedUnit: &unit), "kg")
XCTAssertEqual(unit, MassFormatter.Unit.kilogram)

XCTAssertEqual(formatter.unitString(fromKilograms: 0.0001, usedUnit: &unit), "g")
XCTAssertEqual(unit, MassFormatter.Unit.gram)

XCTAssertEqual(formatter.unitString(fromKilograms: 1.000, usedUnit: &unit), "g")
XCTAssertEqual(unit, MassFormatter.Unit.gram)

XCTAssertEqual(formatter.unitString(fromKilograms: 1.001, usedUnit: &unit), "kg")
XCTAssertEqual(unit, MassFormatter.Unit.kilogram)
}

func test_unitStringFromValue() {
formatter.unitStyle = Formatter.UnitStyle.long
XCTAssertEqual(formatter.unitString(fromValue: 0.002, unit: MassFormatter.Unit.kilogram), "kilograms")
XCTAssertEqual(formatter.unitString(fromValue: 0.100, unit: MassFormatter.Unit.gram), "grams")
XCTAssertEqual(formatter.unitString(fromValue: 2.000, unit: MassFormatter.Unit.pound), "pounds")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.ounce), "ounces")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.stone), "stone")

formatter.unitStyle = Formatter.UnitStyle.medium
XCTAssertEqual(formatter.unitString(fromValue: 0.002, unit: MassFormatter.Unit.kilogram), "kg")
XCTAssertEqual(formatter.unitString(fromValue: 0.100, unit: MassFormatter.Unit.gram), "g")
XCTAssertEqual(formatter.unitString(fromValue: 2.000, unit: MassFormatter.Unit.pound), "lb")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.ounce), "oz")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.stone), "st")

formatter.unitStyle = Formatter.UnitStyle.short
XCTAssertEqual(formatter.unitString(fromValue: 0.002, unit: MassFormatter.Unit.kilogram), "kg")
XCTAssertEqual(formatter.unitString(fromValue: 0.100, unit: MassFormatter.Unit.gram), "g")
XCTAssertEqual(formatter.unitString(fromValue: 2.000, unit: MassFormatter.Unit.pound), "lb")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.ounce), "oz")
XCTAssertEqual(formatter.unitString(fromValue: 2.002, unit: MassFormatter.Unit.stone), "st")
}
}
1 change: 1 addition & 0 deletions TestFoundation/main.swift
Original file line number Diff line number Diff line change
@@ -96,4 +96,5 @@ XCTMain([
testCase(TestObjCRuntime.allTests),
testCase(TestNotification.allTests),
testCase(TestNSISO8601DateFormatter.allTests),
testCase(TestMassFormatter.allTests),
])