diff options
author | Jonathan Buchanan <jonathan.russ.buchanan@gmail.com> | 2022-06-15 16:38:23 -0400 |
---|---|---|
committer | Jonathan Buchanan <jonathan.russ.buchanan@gmail.com> | 2022-06-15 16:38:23 -0400 |
commit | 9da8903e826aea2ff2d780b975d085512f0b21fa (patch) | |
tree | 1a4eba45a1581850493ad944a148e93c282e22f6 | |
parent | 00e03ddb0cc3ae1192f312c0f547097e9eeb5d17 (diff) | |
download | taler-ios-9da8903e826aea2ff2d780b975d085512f0b21fa.tar.gz taler-ios-9da8903e826aea2ff2d780b975d085512f0b21fa.tar.bz2 taler-ios-9da8903e826aea2ff2d780b975d085512f0b21fa.zip |
fix amount parsing and add tests to reflect kotlin api tests
-rw-r--r-- | Taler.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | Taler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate | bin | 30487 -> 35145 bytes | |||
-rw-r--r-- | TalerTests/TalerTests.swift | 42 | ||||
-rw-r--r-- | taler-swift/Sources/taler-swift/Amount.swift | 380 | ||||
-rw-r--r-- | taler-swift/Sources/taler-swift/taler_swift.swift | 6 | ||||
-rw-r--r-- | taler-swift/Tests/taler-swiftTests/AmountTests.swift | 145 | ||||
-rw-r--r-- | taler-swift/Tests/taler-swiftTests/taler_swiftTests.swift | 11 |
7 files changed, 266 insertions, 322 deletions
diff --git a/Taler.xcodeproj/project.pbxproj b/Taler.xcodeproj/project.pbxproj index a1be70f..d88fb8e 100644 --- a/Taler.xcodeproj/project.pbxproj +++ b/Taler.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ D112510026B12E3200D02E00 /* taler-wallet-embedded.js in CopyFiles */ = {isa = PBXBuildFile; fileRef = D11250FF26B12E3200D02E00 /* taler-wallet-embedded.js */; }; D14AFD2124D232B300C51073 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14AFD2024D232B300C51073 /* AppDelegate.swift */; }; D14AFD2324D232B300C51073 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14AFD2224D232B300C51073 /* SceneDelegate.swift */; }; - D14AFD3824D232B500C51073 /* TalerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14AFD3724D232B500C51073 /* TalerTests.swift */; }; D14AFD4324D232B500C51073 /* TalerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14AFD4224D232B500C51073 /* TalerUITests.swift */; }; D14CE1B226C39E5D00612DBE /* BalanceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14CE1B126C39E5D00612DBE /* BalanceRow.swift */; }; D14CE1B426C3A2D400612DBE /* BalanceList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14CE1B326C3A2D400612DBE /* BalanceList.swift */; }; @@ -122,7 +121,6 @@ D14AFD2C24D232B500C51073 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; D14AFD2E24D232B500C51073 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; D14AFD3324D232B500C51073 /* TalerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TalerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D14AFD3724D232B500C51073 /* TalerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalerTests.swift; sourceTree = "<group>"; }; D14AFD3924D232B500C51073 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; D14AFD3E24D232B500C51073 /* TalerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TalerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D14AFD4224D232B500C51073 /* TalerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalerUITests.swift; sourceTree = "<group>"; }; @@ -286,7 +284,6 @@ D14AFD3624D232B500C51073 /* TalerTests */ = { isa = PBXGroup; children = ( - D14AFD3724D232B500C51073 /* TalerTests.swift */, D14AFD3924D232B500C51073 /* Info.plist */, D18DBB5D26DF160D00A4480D /* TimestampTests.swift */, ); @@ -604,7 +601,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D14AFD3824D232B500C51073 /* TalerTests.swift in Sources */, D18DBB5E26DF160D00A4480D /* TimestampTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Taler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate b/Taler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate Binary files differindex f65f460..74ac5da 100644 --- a/Taler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate +++ b/Taler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/TalerTests/TalerTests.swift b/TalerTests/TalerTests.swift deleted file mode 100644 index 3200278..0000000 --- a/TalerTests/TalerTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2021 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import XCTest -@testable import Taler - -class TalerTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/taler-swift/Sources/taler-swift/Amount.swift b/taler-swift/Sources/taler-swift/Amount.swift index e775a65..b2f005f 100644 --- a/taler-swift/Sources/taler-swift/Amount.swift +++ b/taler-swift/Sources/taler-swift/Amount.swift @@ -1,6 +1,6 @@ /* * This file is part of GNU Taler - * (C) 2021 Taler Systems S.A. + * (C) 2022 Taler Systems S.A. * * GNU Taler is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -16,73 +16,48 @@ import Foundation -/** - Errors for `Amount`. - */ +/// Errors for `Amount`. enum AmountError: Error { - /** - The string cannot be parsed to create an `Amount`. - */ + /// The string cannot be parsed to create an `Amount`. case invalidStringRepresentation - /** - Could not compare or operate on two `Amount`s of different currencies. - */ + /// Could not compare or operate on two `Amount`s of different currencies. case incompatibleCurrency - /** - The amount is invalid. The value is either greater than the maximum, or the currency is the empty string. - */ + /// The amount is invalid. The value is either greater than the maximum, or the currency string is not 1-12 characters long. case invalidAmount - /** - The result of the operation would yield a negative amount. - */ + /// The result of the operation would yield a negative amount. case negativeAmount - /** - The operation was division by zero. - */ + /// The operation was division by zero. case divideByZero } -/** - A value of a currency. - */ +/// A value of some currency. public class Amount: Codable, CustomStringConvertible { - /** - The largest possible value that can be represented. - */ + /// Format that a currency must match. + private static let currencyRegex = #"^[-_*A-Za-z0-9]{1,12}$"# + + /// The largest possible value that can be represented. private static let maxValue: UInt64 = 1 << 52 - /** - The size of `value` in relation to `fraction`. - */ + /// The size of `value` in relation to `fraction`. private static let fractionalBase: UInt32 = 100000000 - /** - The greatest number of decimal digits that can be represented. - */ + /// The greatest number of decimal digits that can be represented. private static let fractionalBaseDigits: UInt = 8 - /** - The currency of the amount. - */ + /// The currency of the amount. var currency: String - /** - The value of the amount (number to the left of the decimal point). - */ + /// The value of the amount (number to the left of the decimal point). var value: UInt64 - /** - The fractional value of the amount (number to the right of the decimal point). - */ + /// The fractional value of the amount (number to the right of the decimal point). var fraction: UInt32 - /** - The string representation of the amount, formatted as "`currency`:`value`.`fraction`". - */ + /// The string representation of the amount, formatted as "`currency`:`value`.`fraction`". public var description: String { if fraction == 0 { return "\(currency):\(value)" @@ -97,135 +72,105 @@ public class Amount: Codable, CustomStringConvertible { } } - /** - Whether the value is valid. An amount is valid if and only if the currency is not empty and the value is less than the maximum allowed value. - */ + /// Whether the value is valid. An amount is valid if and only if the currency is not empty and the value is less than the maximum allowed value. var valid: Bool { + if currency.range(of: Amount.currencyRegex, options: .regularExpression) == nil { + return false + } return (value <= Amount.maxValue && currency != "") } - /** - Initializes an amount by parsing a string representing the amount. The string should be formatted as "`currency`:`value`.`fraction`". - - - Parameters: - - fromString: The string to parse. - - - Throws: - - `AmountError.invalidStringRepresentation` if the string cannot be parsed. - - `AmountError.invalidAmount` if the string can be parsed, but the resulting amount is not valid. - */ + /// Whether this amount is zero or not. + var isZero: Bool { + return value == 0 && fraction == 0 + } + + /// Initializes an amount by parsing a string representing the amount. The string should be formatted as "`currency`:`value`.`fraction`". + /// - Parameters: + /// - fromString: The string to parse. + /// - Throws: + /// - `AmountError.invalidStringRepresentation` if the string cannot be parsed. + /// - `AmountError.invalidAmount` if the string can be parsed, but the resulting amount is not valid. public init(fromString string: String) throws { - guard let separatorIndex = string.firstIndex(of: ":") else { throw AmountError.invalidStringRepresentation } - self.currency = String(string[..<separatorIndex]) - let amountStr = String(string[string.index(separatorIndex, offsetBy: 1)...]) - if let dotIndex = amountStr.firstIndex(of: ".") { - let valueStr = String(amountStr[..<dotIndex]) - let fractionStr = String(amountStr[string.index(dotIndex, offsetBy: 1)...]) - guard let _value = UInt64(valueStr) else { throw AmountError.invalidStringRepresentation } - self.value = _value - self.fraction = 0 - var digitValue = Amount.fractionalBase / 10 - for char in fractionStr { - guard let digit = char.wholeNumberValue else { throw AmountError.invalidStringRepresentation } - self.fraction += digitValue * UInt32(digit) - digitValue /= 10 + if let separatorIndex = string.firstIndex(of: ":") { + self.currency = String(string[..<separatorIndex]) + let amountStr = String(string[string.index(separatorIndex, offsetBy: 1)...]) + if let dotIndex = amountStr.firstIndex(of: ".") { + let valueStr = String(amountStr[..<dotIndex]) + let fractionStr = String(amountStr[string.index(dotIndex, offsetBy: 1)...]) + if (fractionStr.count > Amount.fractionalBaseDigits) { + throw AmountError.invalidStringRepresentation + } + guard let _value = UInt64(valueStr) else { throw AmountError.invalidStringRepresentation } + self.value = _value + self.fraction = 0 + var digitValue = Amount.fractionalBase / 10 + for char in fractionStr { + guard let digit = char.wholeNumberValue else { throw AmountError.invalidStringRepresentation } + self.fraction += digitValue * UInt32(digit) + digitValue /= 10 + } + } else { + guard let _value = UInt64(amountStr) else { throw AmountError.invalidStringRepresentation } + self.value = _value + self.fraction = 0 } } else { - guard let _value = UInt64(amountStr) else { throw AmountError.invalidStringRepresentation } - self.value = _value + self.currency = string + self.value = 0 self.fraction = 0 } guard self.valid else { throw AmountError.invalidAmount } } - /** - Initializes an amount with the specified currency, value, and fraction. - - - Parameters: - - currency: The currency of the amount. - - value: The value of the amount (number to the left of the decimal point). - - fraction: The fractional value of the amount (number to the right of the decimal point). - */ - init(currency: String, value: UInt64, fraction: UInt32) { + /// Initializes an amount with the specified currency, value, and fraction. + /// - Parameters: + /// - currency: The currency of the amount. + /// - value: The value of the amount (number to the left of the decimal point). + /// - fraction: The fractional value of the amount (number to the right of the decimal point). + public init(currency: String, value: UInt64, fraction: UInt32) { self.currency = currency self.value = value self.fraction = fraction } - /** - Initializes an amount from a decoder. - - - Parameters: - - fromDecoder: The decoder to extract the amount from. - - - Throws: - - `AmountError.invalidStringRepresentation` if the string cannot be parsed. - - `AmountError.invalidAmount` if the string can be parsed, but the resulting amount is not valid. - */ - init(fromDecoder decoder: Decoder) throws { + /// Initializes an amount from a decoder. + /// - Parameters: + /// - from: The decoder to extract the amount from. + /// - Throws: + /// - `AmountError.invalidStringRepresentation` if the string cannot be parsed. + /// - `AmountError.invalidAmount` if the string can be parsed, but the resulting amount is not valid. + required public convenience init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let string = try container.decode(String.self) - /* TODO: de-duplicate */ - guard let separatorIndex = string.firstIndex(of: ":") else { throw AmountError.invalidStringRepresentation } - self.currency = String(string[..<separatorIndex]) - let amountStr = String(string[string.index(separatorIndex, offsetBy: 1)...]) - if let dotIndex = amountStr.firstIndex(of: ".") { - let valueStr = String(amountStr[..<dotIndex]) - let fractionStr = String(amountStr[string.index(dotIndex, offsetBy: 1)...]) - guard let _value = UInt64(valueStr) else { throw AmountError.invalidStringRepresentation } - self.value = _value - self.fraction = 0 - var digitValue = Amount.fractionalBase / 10 - for char in fractionStr { - guard let digit = char.wholeNumberValue else { throw AmountError.invalidStringRepresentation } - self.fraction += digitValue * UInt32(digit) - digitValue /= 10 - } - } else { - guard let _value = UInt64(amountStr) else { throw AmountError.invalidStringRepresentation } - self.value = _value - self.fraction = 0 - } - guard self.valid else { throw AmountError.invalidAmount } + try self.init(fromString: string) } - /** - Copies an amount. - - - Returns: A copy of the amount. - */ + /// Copies an amount. + /// - Returns: A copy of the amount. func copy() -> Amount { return Amount(currency: self.currency, value: self.value, fraction: self.fraction) } - /** - Creates a normalized copy of an amount. - - - Returns: A copy of the amount that has been normalized - */ + /// Creates a normalized copy of an amount (the fractional part is strictly less than one unit of currency). + /// - Returns: A copy of the amount that has been normalized func normalizedCopy() throws -> Amount { let amount = self.copy() try amount.normalize() return amount } - /** - Encodes an amount. - - - Parameters: - - to: The encoder to encode the amount with. - */ + /// Encodes an amount. + /// - Parameters: + /// - to: The encoder to encode the amount with. public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.description) } - /** - Normalizes an amount by reducing `fraction` until it is less than `Amount.fractionalBase`, increasing `value` appropriately. - - - Throws: - - `AmountError.invalidAmount` if the amount is invalid either before or after normalization. - */ + /// Normalizes an amount by reducing `fraction` until it is less than `Amount.fractionalBase`, increasing `value` appropriately. + /// - Throws: + /// - `AmountError.invalidAmount` if the amount is invalid either before or after normalization. func normalize() throws { if !valid { throw AmountError.invalidAmount @@ -237,18 +182,13 @@ public class Amount: Codable, CustomStringConvertible { } } - /** - Adds two amounts together. - - - Parameters: - - left: The amount on the left. - - right: The amount on the right. - - - Throws: - - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. - - - Returns: The sum of `left` and `right`, normalized. - */ + /// Adds two amounts together. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: The sum of `left` and `right`, normalized. static func + (left: Amount, right: Amount) throws -> Amount { if left.currency != right.currency { throw AmountError.incompatibleCurrency @@ -262,18 +202,13 @@ public class Amount: Codable, CustomStringConvertible { return result } - /** - Subtracts one amount from another. - - - Parameters: - - left: The amount on the left. - - right: The amount on the right. - - - Throws: - - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. - - - Returns: The difference of `left` and `right`, normalized. - */ + /// Subtracts one amount from another. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: The difference of `left` and `right`, normalized. static func - (left: Amount, right: Amount) throws -> Amount { if left.currency != right.currency { throw AmountError.incompatibleCurrency @@ -293,15 +228,11 @@ public class Amount: Codable, CustomStringConvertible { return diff } - /** - Divides an amount by a scalar, possibly introducing rounding error. - - - Parameters: - - dividend: The amount to divide. - - divisor: The scalar dividing `dividend`. - - - Returns: The quotient of `dividend` and `divisor`, normalized. - */ + /// Divides an amount by a scalar, possibly introducing rounding error. + /// - Parameters: + /// - dividend: The amount to divide. + /// - divisor: The scalar dividing `dividend`. + /// - Returns: The quotient of `dividend` and `divisor`, normalized. static func / (dividend: Amount, divisor: UInt32) throws -> Amount { guard divisor != 0 else { throw AmountError.divideByZero } let result = try dividend.normalizedCopy() @@ -316,15 +247,11 @@ public class Amount: Codable, CustomStringConvertible { return result } - /** - Multiply an amount by a scalar. - - - Parameters: - - amount: The amount to multiply. - - factor: The scalar multiplying `amount`. - - - Returns: The product of `amount` and `factor`, normalized. - */ + /// Multiply an amount by a scalar. + /// - Parameters: + /// - amount: The amount to multiply. + /// - factor: The scalar multiplying `amount`. + /// - Returns: The product of `amount` and `factor`, normalized. static func * (amount: Amount, factor: UInt32) throws -> Amount { let result = try amount.normalizedCopy() result.value = result.value * UInt64(factor) @@ -334,19 +261,14 @@ public class Amount: Codable, CustomStringConvertible { return result } - /** - Compares two amounts. - - - Parameters: - - left: The first amount. - - right: The second amount. - - - Throws: - - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. - - - Returns: `true` if and only if the amounts have the same `value` and `fraction` after normalization, `false` otherwise. - */ - static func == (left: Amount, right: Amount) throws -> Bool { + /// Compares two amounts. + /// - Parameters: + /// - left: The first amount. + /// - right: The second amount. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: `true` if and only if the amounts have the same `value` and `fraction` after normalization, `false` otherwise. + public static func == (left: Amount, right: Amount) throws -> Bool { if left.currency != right.currency { throw AmountError.incompatibleCurrency } @@ -355,19 +277,14 @@ public class Amount: Codable, CustomStringConvertible { return (leftNormalized.value == rightNormalized.value && leftNormalized.fraction == rightNormalized.fraction) } - /** - Compares two amounts. - - - Parameters: - - left: The amount on the left. - - right: The amount on the right. - - - Throws: - - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. - - - Returns: `true` if and only if `left` is smaller than `right` after normalization, `false` otherwise. - */ - static func < (left: Amount, right: Amount) throws -> Bool { + /// Compares two amounts. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: `true` if and only if `left` is lesser than `right` after normalization, `false` otherwise. + public static func < (left: Amount, right: Amount) throws -> Bool { if left.currency != right.currency { throw AmountError.incompatibleCurrency } @@ -380,30 +297,43 @@ public class Amount: Codable, CustomStringConvertible { } } - /** - Compares two amounts. - - - Parameters: - - left: The amount on the left. - - right: The amount on the right. - - - Throws: - - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. - - - Returns: `true` if and only if `left` is bigger than `right` after normalization, `false` otherwise. - */ - static func > (left: Amount, right: Amount) throws -> Bool { + /// Compares two amounts. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: `true` if and only if `left` is lesser than or equal to `right` after normalization, `false` otherwise. + public static func <= (left: Amount, right: Amount) throws -> Bool { + return try left < right || left == right + } + + /// Compares two amounts. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: `true` if and only if `left` is greater than `right` after normalization, `false` otherwise. + public static func > (left: Amount, right: Amount) throws -> Bool { return try right < left } - /** - Creates the amount representing zero in a given currency. - - - Parameters: - - currency: The currency to use. - - - Returns: The zero amount for `currency`. - */ + /// Compares two amounts. + /// - Parameters: + /// - left: The amount on the left. + /// - right: The amount on the right. + /// - Throws: + /// - `AmountError.incompatibleCurrency` if `left` and `right` do not share the same currency. + /// - Returns: `true` if and only if `left` is greater than or equal `right` after normalization, `false` otherwise. + public static func >= (left: Amount, right: Amount) throws -> Bool { + return try left > right || left == right + } + + /// Creates the amount representing zero in a given currency. + /// - Parameters: + /// - currency: The currency to use. + /// - Returns: The zero amount for `currency`. static func zero(currency: String) -> Amount { return Amount(currency: currency, value: 0, fraction: 0) } diff --git a/taler-swift/Sources/taler-swift/taler_swift.swift b/taler-swift/Sources/taler-swift/taler_swift.swift deleted file mode 100644 index 79438a4..0000000 --- a/taler-swift/Sources/taler-swift/taler_swift.swift +++ /dev/null @@ -1,6 +0,0 @@ -public struct taler_swift { - public private(set) var text = "Hello, World!" - - public init() { - } -} diff --git a/taler-swift/Tests/taler-swiftTests/AmountTests.swift b/taler-swift/Tests/taler-swiftTests/AmountTests.swift index 6cbbf77..58f981f 100644 --- a/taler-swift/Tests/taler-swiftTests/AmountTests.swift +++ b/taler-swift/Tests/taler-swiftTests/AmountTests.swift @@ -15,10 +15,9 @@ */ import XCTest -@testable import Taler +@testable import taler_swift class AmountTests: XCTestCase { - override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } @@ -27,41 +26,119 @@ class AmountTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testAmounts() throws { - var amount: Amount = try! Amount(fromString: "EUR:633.59") - XCTAssert(amount.currency == "EUR") - XCTAssert(amount.value == 633) - XCTAssert(amount.fraction == 59000000) - XCTAssert(amount.description == "EUR:633.59") - XCTAssert(try amount == Amount(currency: "EUR", value: 633, fraction: 59000000)) - XCTAssert(try amount == amount) - - amount = try! Amount(fromString: "EUR:883") - XCTAssert(amount.currency == "EUR") - XCTAssert(amount.value == 883) - XCTAssert(amount.fraction == 0) - XCTAssert(amount.description == "EUR:883") + func testParsing() { + var str = "TESTKUDOS:23.42" + var amt = try! Amount(fromString: str) + XCTAssert(str == amt.description) + XCTAssert("TESTKUDOS" == amt.currency) + XCTAssert(23 == amt.value) + XCTAssert(UInt64(0.42 * 1e8) == amt.fraction) - XCTAssertThrowsError(try Amount(fromString: "EUR:6548$f.59.**")) + str = "EUR:500000000.00000001" + amt = try! Amount(fromString: str) + XCTAssert(str == amt.description) + XCTAssert("EUR" == amt.currency) + XCTAssert(500000000 == amt.value) + XCTAssert(1 == amt.fraction) - let amount2: Amount = try! Amount(fromString: "EUR:971.32") - XCTAssert(try amount < amount2) - XCTAssert(try amount2 > amount) - XCTAssert(try (amount + amount2) == Amount(fromString: "EUR:1854.32")) - XCTAssert(try (amount2 - amount) == Amount(fromString: "EUR:88.32")) - XCTAssertThrowsError(try amount - amount2) + str = "EUR:1500000000.00000003" + amt = try! Amount(fromString: str) + XCTAssert(str == amt.description) + XCTAssert("EUR" == amt.currency) + XCTAssert(1500000000 == amt.value) + XCTAssert(3 == amt.fraction) - let amount3: Amount = try! Amount(fromString: "USD:12.34") - XCTAssertThrowsError(try amount == amount3) - XCTAssertThrowsError(try amount < amount3) - XCTAssertThrowsError(try amount > amount3) - XCTAssertThrowsError(try amount + amount3) - XCTAssertThrowsError(try amount - amount3) + let maxValue = 4503599627370496 + str = "TESTKUDOS123:\(maxValue).99999999" + amt = try! Amount(fromString: str) + XCTAssert(str == amt.description) + XCTAssert("TESTKUDOS123" == amt.currency) + XCTAssert(maxValue == amt.value) - XCTAssertThrowsError(try amount3 / 0) - XCTAssert(try (amount3 / 1) == amount3) - XCTAssert(try (amount3 / 3) == Amount(fromString: "USD:4.11333333")) - XCTAssert(try (amount3 * 5) == Amount(fromString: "USD:61.7")) + XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS1234:\(maxValue).99999999")) + XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS123:\(maxValue + 1).99999999")) + XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS123:\(maxValue).999999990")) + XCTAssertThrowsError(try Amount(fromString: "TESTKUDOS:0,5")) + XCTAssertThrowsError(try Amount(fromString: "+TESTKUDOS:0.5")) + XCTAssertThrowsError(try Amount(fromString: "0.5")) + XCTAssertThrowsError(try Amount(fromString: ":0.5")) + XCTAssertThrowsError(try Amount(fromString: "EUR::0.5")) + XCTAssertThrowsError(try Amount(fromString: "EUR:.5")) + } + + func testAddition() { + XCTAssert(try Amount(fromString: "EUR:1") + Amount(fromString: "EUR:1") == Amount(fromString: "EUR:2")) + XCTAssert(try Amount(fromString: "EUR:1.5") + Amount(fromString: "EUR:1.5") == Amount(fromString: "EUR:3")) + XCTAssert(try Amount(fromString: "EUR:500000000.00000001") + Amount(fromString: "EUR:0.00000001") == Amount(fromString: "EUR:500000000.00000002")) + XCTAssertThrowsError(try Amount(fromString: "EUR:1") + Amount(fromString: "USD:1")) + XCTAssertThrowsError(try Amount(fromString: "EUR:4503599627370496.99999999") + Amount(fromString: "EUR:4503599627370496.99999999")) + XCTAssertThrowsError(try Amount(fromString: "EUR:4000000000000000") + Amount(fromString: "EUR:4000000000000000")) + } + + func testSubtraction() { + XCTAssert(try Amount(fromString: "EUR:1") - Amount(fromString: "EUR:1") == Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:3") - Amount(fromString: "EUR:1.5") == Amount(fromString: "EUR:1.5")) + XCTAssert(try Amount(fromString: "EUR:500000000.00000002") - Amount(fromString: "EUR:0.00000001") == Amount(fromString: "EUR:500000000.00000001")) + XCTAssertThrowsError(try Amount(fromString: "EUR:3") - Amount(fromString: "USD:1.5")) + XCTAssertThrowsError(try Amount(fromString: "EUR:23.42") - Amount(fromString: "EUR:42.23")) + XCTAssertThrowsError(try Amount(fromString: "EUR:0.5") - Amount(fromString: "EUR:0.50000001")) + } + + func testMultiplication() { + XCTAssert(try Amount(fromString: "EUR:2") * 1 == Amount(fromString: "EUR:2")) + XCTAssert(try Amount(fromString: "EUR:1") * 2 == Amount(fromString: "EUR:2")) + XCTAssert(try Amount(fromString: "EUR:1.5") * 3 == Amount(fromString: "EUR:4.5")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 0 == Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 1 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 2 == Amount(fromString: "EUR:2.22")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 3 == Amount(fromString: "EUR:3.33")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 4 == Amount(fromString: "EUR:4.44")) + XCTAssert(try Amount(fromString: "EUR:1.11") * 5 == Amount(fromString: "EUR:5.55")) + XCTAssert(try Amount(fromString: "EUR:500000000.00000001") * 3 == Amount(fromString: "EUR:1500000000.00000003")) + XCTAssertThrowsError(try Amount(fromString: "4000000000000000") * 2) + } + + func testDivision() { + XCTAssert(try Amount(fromString: "EUR:2") / 1 == Amount(fromString: "EUR:2")) + XCTAssert(try Amount(fromString: "EUR:2") / 2 == Amount(fromString: "EUR:1")) + XCTAssert(try Amount(fromString: "EUR:4.5") / 3 == Amount(fromString: "EUR:1.5")) + XCTAssert(try Amount(fromString: "EUR:0") / 5 == Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:1.11") / 1 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:2.22") / 2 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:3.33") / 3 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:4.44") / 4 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:5.55") / 5 == Amount(fromString: "EUR:1.11")) + XCTAssert(try Amount(fromString: "EUR:1500000000.00000003") / 3 == Amount(fromString: "EUR:500000000.00000001")) + XCTAssert(try Amount(fromString: "EUR:0.00000003") / 2 == Amount(fromString: "EUR:0.00000001")) + XCTAssertThrowsError(try Amount(fromString: "EUR:1") / 0) + } + + func testZero() { + XCTAssert(try Amount(fromString: "EUR") == Amount.zero(currency: "EUR")) + XCTAssert(Amount.zero(currency: "EUR").isZero) + XCTAssert(try Amount(fromString: "EUR:0").isZero) + XCTAssert(try Amount(fromString: "EUR:0.0").isZero) + XCTAssert(try Amount(fromString: "EUR:0.00000").isZero) + XCTAssert(try (Amount(fromString: "EUR:1.001") - Amount(fromString: "EUR:1.001")).isZero) + XCTAssert(try !Amount(fromString: "EUR:0.00000001").isZero) + XCTAssert(try !Amount(fromString: "EUR:1.0").isZero) + XCTAssert(try !Amount(fromString: "EUR:0001.0").isZero) + } + + func testComparison() { + XCTAssert(try Amount(fromString: "EUR:0") <= Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:0") <= Amount(fromString: "EUR:0.00000001")) + XCTAssert(try Amount(fromString: "EUR:0") < Amount(fromString: "EUR:0.00000001")) + XCTAssert(try Amount(fromString: "EUR:0") < Amount(fromString: "EUR:1")) + XCTAssert(try Amount(fromString: "EUR:0") == Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:42") == Amount(fromString: "EUR:42")) + XCTAssert(try Amount(fromString: "EUR:42.00000001") == Amount(fromString: "EUR:42.00000001")) + XCTAssert(try Amount(fromString: "EUR:42.00000001") >= Amount(fromString: "EUR:42.00000001")) + XCTAssert(try Amount(fromString: "EUR:42.00000002") >= Amount(fromString: "EUR:42.00000001")) + XCTAssert(try Amount(fromString: "EUR:42.00000002") > Amount(fromString: "EUR:42.00000001")) + XCTAssert(try Amount(fromString: "EUR:0.00000002") > Amount(fromString: "EUR:0.00000001")) + XCTAssert(try Amount(fromString: "EUR:0.00000001") > Amount(fromString: "EUR:0")) + XCTAssert(try Amount(fromString: "EUR:2") > Amount(fromString: "EUR:1")) + XCTAssertThrowsError(try Amount(fromString: "EUR:0.5") < Amount(fromString: "USD:0.50000001")) } - } diff --git a/taler-swift/Tests/taler-swiftTests/taler_swiftTests.swift b/taler-swift/Tests/taler-swiftTests/taler_swiftTests.swift deleted file mode 100644 index d7b1c8f..0000000 --- a/taler-swift/Tests/taler-swiftTests/taler_swiftTests.swift +++ /dev/null @@ -1,11 +0,0 @@ -import XCTest -@testable import taler_swift - -final class taler_swiftTests: XCTestCase { - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct - // results. - XCTAssertEqual(taler_swift().text, "Hello, World!") - } -} |