taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit b1abb86c2c09dddd4f4021f998789e9926dd21b3
parent 9da8903e826aea2ff2d780b975d085512f0b21fa
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
Date:   Tue, 21 Jun 2022 01:52:09 -0400

implement and test timestamps and durations

Diffstat:
MTaler.xcodeproj/project.pbxproj | 4----
MTaler.xcodeproj/project.xcworkspace/xcuserdata/jonathan.xcuserdatad/UserInterfaceState.xcuserstate | 0
MTaler/WalletBackend.swift | 40----------------------------------------
DTalerTests/TimestampTests.swift | 43-------------------------------------------
Mtaler-swift/Sources/taler-swift/Amount.swift | 1-
Ataler-swift/Sources/taler-swift/Time.swift | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtaler-swift/Tests/taler-swiftTests/AmountTests.swift | 10+---------
Ataler-swift/Tests/taler-swiftTests/TimeTests.swift | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 222 insertions(+), 97 deletions(-)

diff --git a/Taler.xcodeproj/project.pbxproj b/Taler.xcodeproj/project.pbxproj @@ -34,7 +34,6 @@ D17D8B8325ADB29B001BD43D /* libllhttp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D17D8B4D25ADB12C001BD43D /* libllhttp.a */; }; D17D8B8425ADB29B001BD43D /* libhistogram.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D17D8B5625ADB130001BD43D /* libhistogram.a */; }; D17D8B8525ADB29B001BD43D /* libcares.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D17D8B4825ADB12B001BD43D /* libcares.a */; }; - D18DBB5E26DF160D00A4480D /* TimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D18DBB5D26DF160D00A4480D /* TimestampTests.swift */; }; D1AFF0F3268D59C200FBB744 /* libiono.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1AFF0F2268D59A500FBB744 /* libiono.a */; }; D1D65B9826992E4600C1012A /* WalletBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1D65B9726992E4600C1012A /* WalletBackend.swift */; }; /* End PBXBuildFile section */ @@ -149,7 +148,6 @@ D17D8B5525ADB12E001BD43D /* libuvwasi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libuvwasi.a; path = "ios-node-v8/taler-ios-build/compiled/node-arm64/libuvwasi.a"; sourceTree = "<group>"; }; D17D8B5625ADB130001BD43D /* libhistogram.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libhistogram.a; path = "ios-node-v8/taler-ios-build/compiled/node-arm64/libhistogram.a"; sourceTree = "<group>"; }; D17D8B5725ADB130001BD43D /* libtorque_base.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtorque_base.a; path = "ios-node-v8/taler-ios-build/compiled/node-arm64/libtorque_base.a"; sourceTree = "<group>"; }; - D18DBB5D26DF160D00A4480D /* TimestampTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimestampTests.swift; sourceTree = "<group>"; }; D1AB963B259EB13D00DEAB23 /* libnode.89.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libnode.89.dylib; path = "ios-node-v8/taler-ios-build/compiled/x64-v8a/libnode.89.dylib"; sourceTree = "<group>"; }; D1AFF0F2268D59A500FBB744 /* libiono.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libiono.a; path = iono/compiled/x64/libiono.a; sourceTree = "<group>"; }; D1D65B9726992E4600C1012A /* WalletBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletBackend.swift; sourceTree = "<group>"; }; @@ -285,7 +283,6 @@ isa = PBXGroup; children = ( D14AFD3924D232B500C51073 /* Info.plist */, - D18DBB5D26DF160D00A4480D /* TimestampTests.swift */, ); path = TalerTests; sourceTree = "<group>"; @@ -601,7 +598,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 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 differ. diff --git a/Taler/WalletBackend.swift b/Taler/WalletBackend.swift @@ -134,46 +134,6 @@ class WalletBackendGetBalancesRequest: WalletBackendRequest { } /** - A timestamp, measured in milliseconds elapsed from an epoch. - */ -enum TimestampError: Error { - case invalidString -} - -enum Timestamp: Codable, Equatable { - case millisecondsSinceEpoch(Int) - case never - - enum CodingKeys: String, CodingKey { - case t_ms = "t_ms" - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - do { - self = Timestamp.millisecondsSinceEpoch(try container.decode(Int.self, forKey: .t_ms)) - } catch { - let stringValue = try container.decode(String.self, forKey: .t_ms) - if stringValue == "never" { - self = Timestamp.never - } else { - throw TimestampError.invalidString - } - } - } - - func encode(to encoder: Encoder) throws { - var value = encoder.singleValueContainer() - switch self { - case .millisecondsSinceEpoch(let t_ms): - try value.encode(t_ms) - case .never: - try value.encode("never") - } - } -} - -/** A billing or mailing location. */ struct Location: Codable { diff --git a/TalerTests/TimestampTests.swift b/TalerTests/TimestampTests.swift @@ -1,43 +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 TimestampTests: 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 testTimestamps() throws { - let json1 = "{ \"t_ms\" : 12309487 }".data(using: .utf8)! - let json2 = "{ \"t_ms\" : \"never\" }".data(using: .utf8)! - let json3 = "{ \"t_ms\" : \"sometime\" }".data(using: .utf8)! - - var t: Timestamp = try! JSONDecoder().decode(Timestamp.self, from: json1) - XCTAssertEqual(t, Timestamp.millisecondsSinceEpoch(12309487)) - - t = try! JSONDecoder().decode(Timestamp.self, from: json2) - XCTAssertEqual(t, Timestamp.never) - XCTAssertThrowsError(t = try JSONDecoder().decode(Timestamp.self, from: json3)) - } - -} diff --git a/taler-swift/Sources/taler-swift/Amount.swift b/taler-swift/Sources/taler-swift/Amount.swift @@ -13,7 +13,6 @@ * 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 Foundation /// Errors for `Amount`. diff --git a/taler-swift/Sources/taler-swift/Time.swift b/taler-swift/Sources/taler-swift/Time.swift @@ -0,0 +1,128 @@ +/* + * This file is part of GNU Taler + * (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 + * 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 Foundation + +/// Errors for `Timestamp`. +enum TimestampError: Error { + /// The string cannot be parsed to create an `Amount`. + case invalidStringRepresentation + + /// Invalid arguments were supplied to an arithmetic operation. + case invalidArithmeticArguments +} + +/// A point in time, represented by milliseconds from Jaunuary 1, 1970.. +public enum Timestamp: Codable, Equatable { + case milliseconds(UInt64) + case never + + enum CodingKeys: String, CodingKey { + case t_ms = "t_ms" + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + do { + self = Timestamp.milliseconds(try container.decode(UInt64.self, forKey: .t_ms)) + } catch { + let stringValue = try container.decode(String.self, forKey: .t_ms) + if stringValue == "never" { + self = Timestamp.never + } else { + throw TimestampError.invalidStringRepresentation + } + } + } + + static func now() -> Timestamp { + return Timestamp.milliseconds(UInt64(Date().timeIntervalSince1970 * 1000.0)) + } + + public func encode(to encoder: Encoder) throws { + var value = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .milliseconds(let t_ms): + try value.encode(t_ms, forKey: .t_ms) + case .never: + try value.encode("never", forKey: .t_ms) + } + } +} + +/// A duration of time, measured in milliseconds. +public enum Duration: Equatable { + case milliseconds(UInt64) + case forever +} + +/// Addition of a timestamp and a duration. +func +(lhs: Timestamp, rhs: Duration) -> Timestamp { + switch lhs { + case .milliseconds(let lhs_ms): + switch rhs { + case .milliseconds(let rhs_ms): + let result = lhs_ms.addingReportingOverflow(rhs_ms) + if result.overflow { + return Timestamp.never + } else { + return Timestamp.milliseconds(result.partialValue) + } + case .forever: + return Timestamp.never + } + case .never: + return Timestamp.never + } +} + +/// Subtraction of a duration from a timestamp. +func -(lhs: Timestamp, rhs: Duration) -> Timestamp { + switch lhs { + case .milliseconds(let lhs_ms): + switch rhs { + case .milliseconds(let rhs_ms): + let result = lhs_ms.subtractingReportingOverflow(rhs_ms) + if result.overflow { + return Timestamp.milliseconds(0) + } else { + return Timestamp.milliseconds(result.partialValue) + } + case .forever: + return Timestamp.milliseconds(0) + } + case .never: + return Timestamp.never + } +} + +/// Calculates the difference between two timestamps. +func -(lhs: Timestamp, rhs: Timestamp) throws -> Duration { + switch lhs { + case .milliseconds(let lhs_ms): + switch rhs { + case .milliseconds(let rhs_ms): + if lhs_ms < rhs_ms { + return Duration.milliseconds(0) + } else { + return Duration.milliseconds(lhs_ms - rhs_ms) + } + case .never: + throw TimestampError.invalidArithmeticArguments + } + case .never: + return Duration.forever + } +} diff --git a/taler-swift/Tests/taler-swiftTests/AmountTests.swift b/taler-swift/Tests/taler-swiftTests/AmountTests.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 @@ -18,14 +18,6 @@ import XCTest @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. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testParsing() { var str = "TESTKUDOS:23.42" var amt = try! Amount(fromString: str) diff --git a/taler-swift/Tests/taler-swiftTests/TimeTests.swift b/taler-swift/Tests/taler-swiftTests/TimeTests.swift @@ -0,0 +1,93 @@ +/* + * This file is part of GNU Taler + * (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 + * 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 Foundation + +import XCTest +@testable import taler_swift + +class TimeTests: XCTestCase { + func testParsing() { + let json1 = "{ \"t_ms\" : 12309487 }".data(using: .utf8)! + let json2 = "{ \"t_ms\" : \"never\" }".data(using: .utf8)! + let json3 = "{ \"t_ms\" : \"sometime\" }".data(using: .utf8)! + + var t: Timestamp = try! JSONDecoder().decode(Timestamp.self, from: json1) + XCTAssertEqual(t, Timestamp.milliseconds(12309487)) + + t = try! JSONDecoder().decode(Timestamp.self, from: json2) + XCTAssertEqual(t, Timestamp.never) + XCTAssertThrowsError(t = try JSONDecoder().decode(Timestamp.self, from: json3)) + } + + func testSerialize() { + let str1 = "{\"t_ms\":12309487}" + let time1 = Timestamp.milliseconds(12309487) + + let str2 = "{\"t_ms\":\"never\"}" + let time2 = Timestamp.never + + XCTAssert(try! String(data: JSONEncoder().encode(time1), encoding: .utf8)! == str1) + XCTAssert(try! String(data: JSONEncoder().encode(time2), encoding: .utf8)! == str2) + } + + func testTimestampDurationAdd() { + XCTAssert(Timestamp.never + Duration.milliseconds(0) == Timestamp.never) + XCTAssert(Timestamp.never + Duration.milliseconds(123456) == Timestamp.never) + XCTAssert(Timestamp.never + Duration.forever == Timestamp.never) + + XCTAssert(Timestamp.milliseconds(0) + Duration.milliseconds(0) == Timestamp.milliseconds(0)) + XCTAssert(Timestamp.milliseconds(0) + Duration.milliseconds(12309487) == Timestamp.milliseconds(12309487)) + XCTAssert(Timestamp.milliseconds(0) + Duration.forever == Timestamp.never) + + XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds(0) == Timestamp.milliseconds(12309487)) + XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds(UInt64.max - 12309487) == Timestamp.milliseconds(UInt64.max)) + XCTAssert(Timestamp.milliseconds(12309487) + Duration.milliseconds((UInt64.max - 12309487) + 1) == Timestamp.never) + XCTAssert(Timestamp.milliseconds(12309487) + Duration.forever == Timestamp.never) + } + + func testTimestampDurationSubtraction() { + XCTAssert(Timestamp.never - Duration.milliseconds(0) == Timestamp.never) + XCTAssert(Timestamp.never - Duration.milliseconds(123456) == Timestamp.never) + XCTAssert(Timestamp.never - Duration.forever == Timestamp.never) + + XCTAssert(Timestamp.milliseconds(0) - Duration.milliseconds(0) == Timestamp.milliseconds(0)) + XCTAssert(Timestamp.milliseconds(0) - Duration.milliseconds(123456) == Timestamp.milliseconds(0)) + XCTAssert(Timestamp.milliseconds(0) - Duration.forever == Timestamp.milliseconds(0)) + + XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(0) == Timestamp.milliseconds(12309487)) + XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(487) == Timestamp.milliseconds(12309000)) + XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(12309487) == Timestamp.milliseconds(0)) + XCTAssert(Timestamp.milliseconds(12309487) - Duration.milliseconds(12309488) == Timestamp.milliseconds(0)) + XCTAssert(Timestamp.milliseconds(12309487) - Duration.forever == Timestamp.milliseconds(0)) + } + + func testTimestampDifference() { + XCTAssert(try! Timestamp.never - Timestamp.milliseconds(0) == Duration.forever) + XCTAssert(try! Timestamp.never - Timestamp.milliseconds(123456) == Duration.forever) + XCTAssert(try! Timestamp.never - Timestamp.never == Duration.forever) + + XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(0) == Duration.milliseconds(0)) + XCTAssert(try! Timestamp.milliseconds(0) - Timestamp.milliseconds(123456) == Duration.milliseconds(0)) + XCTAssertThrowsError(try Timestamp.milliseconds(0) - Timestamp.never) + + XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(0) == Duration.milliseconds(12309487)) + XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309000) == Duration.milliseconds(487)) + XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309487) == Duration.milliseconds(0)) + XCTAssert(try! Timestamp.milliseconds(12309487) - Timestamp.milliseconds(12309488) == Duration.milliseconds(0)) + XCTAssertThrowsError(try Timestamp.milliseconds(12309487) - Timestamp.never) + } +}