Skip to content

[EnergyFormatter] Implementation and tests #791

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

Merged
merged 4 commits into from
Feb 10, 2017
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions Foundation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@
7900433C1CACD33E00ECCBF1 /* TestNSPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7900433A1CACD33E00ECCBF1 /* TestNSPredicate.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 */; };
BDFDF0A71DFF5B3E00C04CC5 /* TestNSPersonNameComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFDF0A61DFF5B3E00C04CC5 /* TestNSPersonNameComponents.swift */; };
BF8E65311DC3B3CB005AB5C3 /* TestNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8E65301DC3B3CB005AB5C3 /* TestNotification.swift */; };
CC5249C01D341D23007CB54D /* TestUnitConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5249BF1D341D23007CB54D /* TestUnitConverter.swift */; };
Expand Down Expand Up @@ -755,6 +756,7 @@
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>"; };
BDBB658F1E256BFA001A7286 /* TestNSEnergyFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSEnergyFormatter.swift; sourceTree = "<group>"; };
BDFDF0A61DFF5B3E00C04CC5 /* TestNSPersonNameComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSPersonNameComponents.swift; sourceTree = "<group>"; };
BF8E65301DC3B3CB005AB5C3 /* TestNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNotification.swift; sourceTree = "<group>"; };
C2A9D75B1C15C08B00993803 /* TestNSUUID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSUUID.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1346,6 +1348,7 @@
2EBE67A31C77BF05006583D5 /* TestNSDateFormatter.swift */,
231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */,
EA66F63D1BF1619600136161 /* TestNSDictionary.swift */,
BDBB658F1E256BFA001A7286 /* TestNSEnergyFormatter.swift */,
D512D17B1CD883F00032E6A5 /* TestNSFileHandle.swift */,
525AECEB1BF2C96400D15BB0 /* TestNSFileManager.swift */,
88D28DE61C13AE9000494606 /* TestNSGeometry.swift */,
Expand Down Expand Up @@ -2267,6 +2270,7 @@
5B13B3291C582D4C00651CE2 /* TestNSCalendar.swift in Sources */,
5B13B34F1C582D4C00651CE2 /* TestNSXMLParser.swift in Sources */,
D5C40F331CDA1D460005690C /* TestNSOperationQueue.swift in Sources */,
BDBB65901E256BFA001A7286 /* TestNSEnergyFormatter.swift in Sources */,
5B13B32F1C582D4C00651CE2 /* TestNSGeometry.swift in Sources */,
EA08126C1DA810BE00651B70 /* ProgressFraction.swift in Sources */,
5B13B3351C582D4C00651CE2 /* TestNSKeyedUnarchiver.swift in Sources */,
Expand Down
194 changes: 174 additions & 20 deletions Foundation/NSEnergyFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,195 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//


extension EnergyFormatter {
public enum Unit : Int {
public enum Unit: Int {

case joule = 11
case kilojoule = 14
case calorie = 1793 // chemistry "calories", abbr "cal"
case kilocalorie = 1794 // kilocalories in general, abbr “kcal”, or “C” in some locales (e.g. US) when usesFoodEnergy is set to YES

// Map Unit to UnitEnergy class to aid with conversions
fileprivate var unitEnergy: UnitEnergy {
switch self {
case .joule:
return UnitEnergy.joules
case .kilojoule:
return UnitEnergy.kilojoules
case .calorie:
return UnitEnergy.calories
case .kilocalorie:
return UnitEnergy.kilocalories
}
}

// Reuse symbols defined in UnitEnergy, except for kilocalories, which is defined as "kCal"
fileprivate var symbol: String {
switch self {
case .kilocalorie:
return "kcal"
default:
return unitEnergy.symbol
}
}

// Return singular, full string representation of the energy unit
fileprivate var singularString: String {
switch self {
case .joule:
return "joule"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is missing the meat of what EnergyFormatter provides on Apple platforms: namely, formatting energy values in a locale-appropriate way. On macOS:

let formatter = EnergyFormatter()
formatter.numberFormatter.locale = Locale(identifier: "de_DE")
formatter.unitStyle = .long
formatter.string(fromValue: 0, unit: .joule) // "0 Joule"
formatter.numberFormatter.locale = Locale(identifier: "fr_FR")
formatter.string(fromValue: 0, unit: .joule) // "0 joule"
formatter.numberFormatter.locale = Locale(identifier: "zh_CN")
formatter.string(fromValue: 0, unit: .joule) // "0焦耳"

Copy link
Contributor

Choose a reason for hiding this comment

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

We still need to identify a roadmap forward to handle localization bundles and the content of those for swift-corelibs-foundation. Since the environment is different (there is no "system" version of Foundation) we have to consider a methodology to store the data without relying on a common path (the bundle of the Foundation.framework). So I would say that the localization data is probably more an additional task than just the formatters. Thankfully code is mutable and we can change it after additions like this to accommodate for improvements in localizations.

Copy link
Contributor

Choose a reason for hiding this comment

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

Certainly. In the meantime, however, is it appropriate for a formatter that is unable to localize for some particular locales and unit styles offer known incorrect output when asked, or should the code check for supported locales and otherwise bail with NSUnimplemented()?

Copy link
Contributor

Choose a reason for hiding this comment

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

fallback localizations should go to en_US_POSIX so even though the current implementations may not have parity they are technically "correct" in their fallback behavior. It might be nice however to somehow have a non-failing version that would report these cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Surely, that means that the whole thing should fall back to en_US_POSIX and not that the number should be localized and an English word appended?

Copy link
Author

@kweinmeister kweinmeister Jan 24, 2017

Choose a reason for hiding this comment

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

In summary, the current implementation:

  • Formats the number based on the locale
  • Selects the appropriate unit based on the locale
  • Does not currently use a translated unit symbol

In writing this, I referenced Unit.swift, and it currently also provides English-only symbols at the moment.

Shall we proceed with this initial implementation? If not, please let me know which of the above behaviors and anything else that should be changed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm ok with that, and maybe we should reflect that status in our status markdown file. Do the tests pass on macOS now?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, the test pass on macOS with the 2nd commit.

That's a good idea to reflect the translation status in the markdown file. It would support transparency and put more attention on resolving this technical debt.

case .kilojoule:
return "kilojoule"
case .calorie:
return "calorie"
case .kilocalorie:
return "kilocalorie"
}
}

case joule
case kilojoule
case calorie // chemistry "calories", abbr "cal"
case kilocalorie // kilocalories in general, abbr “kcal”, or “C” in some locales (e.g. US) when usesFoodEnergy is set to YES
// Return plural, full string representation of the energy unit
fileprivate var pluralString: String {
return "\(self.singularString)s"
}
}
}

open class EnergyFormatter : Formatter {

open class EnergyFormatter: Formatter {

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

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

/*@NSCopying*/ open var numberFormatter: NumberFormatter! // default is NSNumberFormatter with NSNumberFormatterDecimalStyle
open var unitStyle: UnitStyle // default is NSFormattingUnitStyleMedium
open var isForFoodEnergyUse: Bool // default is NO; if it is set to YES, NSEnergyFormatterUnitKilocalorie may be “C” instead of “kcal"

// 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 {
guard let formattedValue = numberFormatter.string(from:NSNumber(value: value)) else {
fatalError("Cannot format \(value) as string")
}

let separator = unitStyle == EnergyFormatter.UnitStyle.short ? "" : " "
return "\(formattedValue)\(separator)\(unitString(fromValue: value, unit: unit))"
}

// Format a number in joules to a localized string with the locale-appropriate unit and an appropriate scale (e.g. 10.3J = 2.46cal in the US locale).
open func string(fromJoules numberInJoules: Double) -> String { NSUnimplemented() }

open func string(fromJoules numberInJoules: Double) -> String {

//Convert to the locale-appropriate unit
var unitFromJoules: EnergyFormatter.Unit = .joule
_ = self.unitString(fromJoules: numberInJoules, usedUnit: &unitFromJoules)

//Map the unit to UnitLength type for conversion later
let unitEnergyFromJoules = unitFromJoules.unitEnergy

//Create a measurement object based on the value in joules
let joulesMeasurement = Measurement<UnitEnergy>(value:numberInJoules, unit: .joules)

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

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

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

// 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 {

//Special case when isForFoodEnergyUse is true
if isForFoodEnergyUse && unit == .kilocalorie {
if unitStyle == .short {
return "C"
} else if unitStyle == .medium {
return "Cal"
} else {
return "Calories"
}
}

if unitStyle == .short || unitStyle == .medium {
return unit.symbol
} else if value == 1.0 {
return unit.singularString
} else {
return unit.pluralString
}
}

// Return the locale-appropriate unit, the same unit used by -stringFromJoules:.
open func unitString(fromJoules numberInJoules: Double, usedUnit unitp: UnsafeMutablePointer<Unit>?) -> String { NSUnimplemented() }


open func unitString(fromJoules numberInJoules: Double, usedUnit unitp: UnsafeMutablePointer<Unit>?) -> String {

//Convert to the locale-appropriate unit
let unitFromJoules: Unit

if numberFormatter.locale.usesCalories {
if numberInJoules > 0 && numberInJoules <= 4184 {
unitFromJoules = .calorie
} else {
unitFromJoules = .kilocalorie
}
} else {
if numberInJoules > 0 && numberInJoules <= 1000 {
unitFromJoules = .joule
} else {
unitFromJoules = .kilojoule
}
}
unitp?.pointee = unitFromJoules

//Map the unit to UnitEnergy type for conversion later
let unitEnergyFromJoules = unitFromJoules.unitEnergy

//Create a measurement object based on the value in joules
let joulesMeasurement = Measurement<UnitEnergy>(value:numberInJoules, unit: .joules)

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

//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: unitFromJoules)
}

/// - 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 }
}

/// TODO: Replace calls to the below function to use Locale.regionCode
/// Temporary workaround due to unpopulated Locale attributes
/// See https://bugs.swift.org/browse/SR-3202
extension Locale {
public var usesCalories: Bool {

switch self.identifier {
case "en_US": return true
case "en_US_POSIX": return true
case "haw_US": return true
case "es_US": return true
case "chr_US": return true
case "en_GB": return true
case "kw_GB": return true
case "cy_GB": return true
case "gv_GB": return true
default: return false
}
}
}
148 changes: 148 additions & 0 deletions TestFoundation/TestNSEnergyFormatter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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 TestNSEnergyFormatter: XCTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

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

Running these tests against the objective-c foundation give a few errors:

Test Suite 'TestNSEnergyFormatter' started at 2017-01-12 10:26:10.618
Test Case '-[TestNSEnergyFormatter test_unitStringFromJoules]' started.
TestNSEnergyFormatter.swift:88: error: -[TestNSEnergyFormatter test_unitStringFromJoules] : XCTAssertEqual failed: ("kcal") is not equal to ("kCal") - 
TestNSEnergyFormatter.swift:91: error: -[TestNSEnergyFormatter test_unitStringFromJoules] : XCTAssertEqual failed: ("kcal") is not equal to ("kCal") - 
TestNSEnergyFormatter.swift:100: error: -[TestNSEnergyFormatter test_unitStringFromJoules] : XCTAssertEqual failed: ("kcal") is not equal to ("kCal") - 
TestNSEnergyFormatter.swift:103: error: -[TestNSEnergyFormatter test_unitStringFromJoules] : XCTAssertEqual failed: ("kcal") is not equal to ("kCal") - 
Test Case '-[TestNSEnergyFormatter test_unitStringFromJoules]' failed (0.051 seconds).
Test Case '-[TestNSEnergyFormatter test_stringFromValue]' started.
TestNSEnergyFormatter.swift:72: error: -[TestNSEnergyFormatter test_stringFromValue] : XCTAssertEqual failed: ("0kcal") is not equal to ("0kCal") - 
TestNSEnergyFormatter.swift:79: error: -[TestNSEnergyFormatter test_stringFromValue] : XCTAssertEqual failed: ("987,654,321 Cal") is not equal to ("987,654,321 cal") - 
TestNSEnergyFormatter.swift:82: error: -[TestNSEnergyFormatter test_stringFromValue] : XCTAssertEqual failed: ("5.3 kcal") is not equal to ("5.3 kCal") - 
Test Case '-[TestNSEnergyFormatter test_stringFromValue]' failed (0.001 seconds).
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesJoulesRegion]' started.
TestNSEnergyFormatter.swift:42: error: -[TestNSEnergyFormatter test_stringFromJoulesJoulesRegion] : XCTAssertEqual failed: ("0 J") is not equal to ("0 J") - 
TestNSEnergyFormatter.swift:43: error: -[TestNSEnergyFormatter test_stringFromJoulesJoulesRegion] : XCTAssertEqual failed: ("1 J") is not equal to ("1 J") - 
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesJoulesRegion]' failed (0.003 seconds).
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegion]' started.
TestNSEnergyFormatter.swift:49: error: -[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegion] : XCTAssertEqual failed: ("-2.39 kcal") is not equal to ("-2.39 kCal") - 
TestNSEnergyFormatter.swift:53: error: -[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegion] : XCTAssertEqual failed: ("2.39 kcal") is not equal to ("2.39 kCal") - 
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegion]' failed (0.002 seconds).
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegionFoodEnergyUse]' started.
TestNSEnergyFormatter.swift:58: error: -[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegionFoodEnergyUse] : XCTAssertEqual failed: ("-0 Cal") is not equal to ("-0 cal") - 
TestNSEnergyFormatter.swift:62: error: -[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegionFoodEnergyUse] : XCTAssertEqual failed: ("2.39 Cal") is not equal to ("2.39 cal") - 
Test Case '-[TestNSEnergyFormatter test_stringFromJoulesCaloriesRegionFoodEnergyUse]' failed (0.001 seconds).
Test Case '-[TestNSEnergyFormatter test_unitStringFromValue]' started.
TestNSEnergyFormatter.swift:134: error: -[TestNSEnergyFormatter test_unitStringFromValue] : XCTAssertEqual failed: ("Cal") is not equal to ("cal") - 
TestNSEnergyFormatter.swift:135: error: -[TestNSEnergyFormatter test_unitStringFromValue] : XCTAssertEqual failed: ("Cal") is not equal to ("cal") - 
Test Case '-[TestNSEnergyFormatter test_unitStringFromValue]' failed (0.001 seconds).
Test Suite 'TestNSEnergyFormatter' failed at 2017-01-12 10:26:10.844.
	 Executed 6 tests, with 15 failures (0 unexpected) in 0.060 (0.227) seconds
Test Suite 'xctest' failed at 2017-01-12 10:26:10.915.
	 Executed 6 tests, with 15 failures (0 unexpected) in 0.060 (0.298) seconds
Test Suite 'Selected tests' failed at 2017-01-12 10:26:10.916.
	 Executed 6 tests, with 15 failures (0 unexpected) in 0.060 (0.299) seconds

let formatter: EnergyFormatter = EnergyFormatter()

static var allTests: [(String, (TestNSEnergyFormatter) -> () throws -> Void)] {
return [
("test_stringFromJoulesJoulesRegion", test_stringFromJoulesJoulesRegion),
("test_stringFromJoulesCaloriesRegion", test_stringFromJoulesCaloriesRegion),
("test_stringFromJoulesCaloriesRegionFoodEnergyUse", test_stringFromJoulesCaloriesRegionFoodEnergyUse),
("test_stringFromValue", test_stringFromValue),
("test_unitStringFromValue", test_unitStringFromValue),
("test_unitStringFromJoules", test_unitStringFromJoules)
]
}

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

func test_stringFromJoulesJoulesRegion() {
formatter.numberFormatter.locale = Locale(identifier: "de_DE")
XCTAssertEqual(formatter.string(fromJoules: -100000), "-100 kJ")
XCTAssertEqual(formatter.string(fromJoules: -1), "-0,001 kJ")
XCTAssertEqual(formatter.string(fromJoules: 100000000), "100.000 kJ")
}


func test_stringFromJoulesCaloriesRegion() {
XCTAssertEqual(formatter.string(fromJoules: -10000), "-2.39 kcal")
XCTAssertEqual(formatter.string(fromJoules: 0.00001), "0 cal")
XCTAssertEqual(formatter.string(fromJoules: 0.0001), "0 cal")
XCTAssertEqual(formatter.string(fromJoules: 1), "0.239 cal")
XCTAssertEqual(formatter.string(fromJoules: 10000), "2.39 kcal")
}

func test_stringFromJoulesCaloriesRegionFoodEnergyUse() {
formatter.isForFoodEnergyUse = true
XCTAssertEqual(formatter.string(fromJoules: -1), "-0 Cal")
XCTAssertEqual(formatter.string(fromJoules: 0.001), "0 cal")
XCTAssertEqual(formatter.string(fromJoules: 0.1), "0.024 cal")
XCTAssertEqual(formatter.string(fromJoules: 1), "0.239 cal")
XCTAssertEqual(formatter.string(fromJoules: 10000), "2.39 Cal")
}

func test_stringFromValue() {
formatter.unitStyle = Formatter.UnitStyle.long
XCTAssertEqual(formatter.string(fromValue: 0.002, unit: EnergyFormatter.Unit.kilojoule),"0.002 kilojoules")
XCTAssertEqual(formatter.string(fromValue:0, unit:EnergyFormatter.Unit.joule), "0 joules")
XCTAssertEqual(formatter.string(fromValue:1, unit:EnergyFormatter.Unit.joule), "1 joule")

formatter.unitStyle = Formatter.UnitStyle.short
XCTAssertEqual(formatter.string(fromValue: 0.00000001, unit:EnergyFormatter.Unit.kilocalorie), "0kcal")
XCTAssertEqual(formatter.string(fromValue: 2.4, unit: EnergyFormatter.Unit.calorie), "2.4cal")
XCTAssertEqual(formatter.string(fromValue: 123456, unit: EnergyFormatter.Unit.calorie), "123,456cal")

formatter.unitStyle = Formatter.UnitStyle.medium
formatter.isForFoodEnergyUse = true
XCTAssertEqual(formatter.string(fromValue: 0.00000001, unit: EnergyFormatter.Unit.calorie), "0 cal")
XCTAssertEqual(formatter.string(fromValue: 987654321, unit: EnergyFormatter.Unit.kilocalorie), "987,654,321 Cal")

formatter.isForFoodEnergyUse = false
XCTAssertEqual(formatter.string(fromValue: 5.3, unit: EnergyFormatter.Unit.kilocalorie), "5.3 kcal")
XCTAssertEqual(formatter.string(fromValue: 873.2345, unit: EnergyFormatter.Unit.calorie), "873.234 cal")
}

func test_unitStringFromJoules() {
var unit = EnergyFormatter.Unit.joule
XCTAssertEqual(formatter.unitString(fromJoules: -100000, usedUnit: &unit), "kcal")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilocalorie)

XCTAssertEqual(formatter.unitString(fromJoules: 0, usedUnit: &unit), "kcal")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilocalorie)

XCTAssertEqual(formatter.unitString(fromJoules: 0.0001, usedUnit: &unit), "cal")
XCTAssertEqual(unit, EnergyFormatter.Unit.calorie)

XCTAssertEqual(formatter.unitString(fromJoules: 4184, usedUnit: &unit), "cal")
XCTAssertEqual(unit, EnergyFormatter.Unit.calorie)

XCTAssertEqual(formatter.unitString(fromJoules: 4185, usedUnit: &unit), "kcal")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilocalorie)

XCTAssertEqual(formatter.unitString(fromJoules: 100000, usedUnit: &unit), "kcal")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilocalorie)

formatter.numberFormatter.locale = Locale(identifier: "de_DE")
XCTAssertEqual(formatter.unitString(fromJoules: -100000, usedUnit: &unit), "kJ")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilojoule)

XCTAssertEqual(formatter.unitString(fromJoules: 0, usedUnit: &unit), "kJ")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilojoule)

XCTAssertEqual(formatter.unitString(fromJoules: 0.0001, usedUnit: &unit), "J")
XCTAssertEqual(unit, EnergyFormatter.Unit.joule)

XCTAssertEqual(formatter.unitString(fromJoules: 1000, usedUnit: &unit), "J")
XCTAssertEqual(unit, EnergyFormatter.Unit.joule)

XCTAssertEqual(formatter.unitString(fromJoules: 1000.01, usedUnit: &unit), "kJ")
XCTAssertEqual(unit, EnergyFormatter.Unit.kilojoule)
}

func test_unitStringFromValue() {
formatter.isForFoodEnergyUse = true
formatter.unitStyle = Formatter.UnitStyle.long
XCTAssertEqual(formatter.unitString(fromValue: 1, unit: EnergyFormatter.Unit.kilocalorie), "Calories")
XCTAssertEqual(formatter.unitString(fromValue: 2, unit: EnergyFormatter.Unit.kilocalorie), "Calories")

formatter.unitStyle = Formatter.UnitStyle.medium
XCTAssertEqual(formatter.unitString(fromValue: 0.00000001, unit: EnergyFormatter.Unit.kilocalorie), "Cal")
XCTAssertEqual(formatter.unitString(fromValue: 987654321, unit: EnergyFormatter.Unit.kilocalorie), "Cal")

formatter.unitStyle = Formatter.UnitStyle.short
XCTAssertEqual(formatter.unitString(fromValue: 0.00000001, unit: EnergyFormatter.Unit.calorie), "cal")
XCTAssertEqual(formatter.unitString(fromValue: 123456, unit: EnergyFormatter.Unit.kilocalorie), "C")

formatter.isForFoodEnergyUse = false
formatter.unitStyle = Formatter.UnitStyle.long
XCTAssertEqual(formatter.unitString(fromValue: 0.002, unit: EnergyFormatter.Unit.kilojoule), "kilojoules")

formatter.unitStyle = Formatter.UnitStyle.medium
XCTAssertEqual(formatter.unitString(fromValue: 0.00000001, unit: EnergyFormatter.Unit.kilocalorie), "kcal")
XCTAssertEqual(formatter.unitString(fromValue: 987654321, unit: EnergyFormatter.Unit.kilocalorie), "kcal")

formatter.unitStyle = Formatter.UnitStyle.short
XCTAssertEqual(formatter.unitString(fromValue: 0.00000001, unit: EnergyFormatter.Unit.calorie), "cal")
XCTAssertEqual(formatter.unitString(fromValue: 123456, unit: EnergyFormatter.Unit.joule), "J")
}

}
Loading