Change Counter increase() to accept any numeric value (#51)
This commit is contained in:
parent
43872169ed
commit
247744cf1b
|
@ -60,17 +60,9 @@ class CRDTCounter<T: YorkieCountable>: CRDTElement {
|
|||
public func increase(_ primitive: Primitive) throws -> CRDTCounter {
|
||||
switch primitive.value {
|
||||
case .integer(let int32Value):
|
||||
guard let value = int32Value as? T else {
|
||||
throw YorkieError.type(message: "Value Type mismatch: \(type(of: primitive)), \(T.self)")
|
||||
}
|
||||
|
||||
self.value = self.value.addingReportingOverflow(value).partialValue
|
||||
self.value &+= T(int32Value)
|
||||
case .long(let int64Value):
|
||||
guard let value = int64Value as? T else {
|
||||
throw YorkieError.type(message: "Value Type mismatch: \(type(of: primitive.value))")
|
||||
}
|
||||
|
||||
self.value = self.value.addingReportingOverflow(value).partialValue
|
||||
self.value &+= T(int64Value)
|
||||
default:
|
||||
throw YorkieError.type(message: "Unsupported type of value: \(type(of: primitive.value))")
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ class Primitive: CRDTElement {
|
|||
return .integer(casted)
|
||||
case let casted as Int64:
|
||||
return .long(casted)
|
||||
case let casted as Int:
|
||||
return .long(Int64(casted))
|
||||
case let casted as Double:
|
||||
return .double(casted)
|
||||
case let casted as String:
|
||||
|
|
|
@ -59,28 +59,47 @@ public class JSONCounter<T: YorkieCountable> {
|
|||
* `increase` increases numeric data.
|
||||
*/
|
||||
@discardableResult
|
||||
public func increase<T: YorkieCountable>(value: T) throws -> Self {
|
||||
public func increase(value: any BinaryInteger) -> Self {
|
||||
guard let context, let counter else {
|
||||
let log = "it is not initialized yet"
|
||||
Logger.critical(log)
|
||||
throw YorkieError.unexpected(message: log)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
guard let primitiveValue = Primitive.type(of: value) else {
|
||||
throw YorkieError.type(message: "Unsupported type of value: \(type(of: T.self))")
|
||||
let log = "Unsupported type of value: \(type(of: T.self))"
|
||||
Logger.critical(log)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
let ticket = context.issueTimeTicket()
|
||||
let primitive = Primitive(value: primitiveValue, createdAt: ticket)
|
||||
|
||||
guard primitive.isNumericType else {
|
||||
throw YorkieError.type(message: "Unsupported type of value: \(type(of: T.self))")
|
||||
let log = "Unsupported type of value: \(type(of: T.self))"
|
||||
Logger.critical(log)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
do {
|
||||
try counter.increase(primitive)
|
||||
} catch {
|
||||
let log = "catch error when increase counter: [\(error)]"
|
||||
Logger.critical(log)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
context.push(operation: IncreaseOperation(parentCreatedAt: counter.createdAt, value: primitive, executedAt: ticket))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func increase(value: any BinaryFloatingPoint) -> Self {
|
||||
self.increase(value: T(value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import Foundation
|
|||
public protocol YorkieCountable: BinaryInteger {
|
||||
var littleEndian: Self { get }
|
||||
func addingReportingOverflow(_ other: Self) -> (partialValue: Self, overflow: Bool)
|
||||
static func &+= (lhs: inout Self, rhs: Self)
|
||||
}
|
||||
|
||||
extension Int32: YorkieCountable {}
|
||||
|
|
|
@ -32,10 +32,8 @@ final class CounterIntegrationTests: XCTestCase {
|
|||
await doc.update { root in
|
||||
root.age = JSONCounter(value: Int32(1))
|
||||
root.length = JSONCounter(value: Int64(10))
|
||||
do {
|
||||
try (root.age as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
try (root.length as! JSONCounter<Int64>).increase(value: Int64(3))
|
||||
} catch {}
|
||||
(root.age as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
(root.length as! JSONCounter<Int64>).increase(value: Int64(3))
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
|
@ -48,10 +46,8 @@ final class CounterIntegrationTests: XCTestCase {
|
|||
XCTAssert(result == "{\"age\":6,\"length\":13}")
|
||||
|
||||
await doc.update { root in
|
||||
do {
|
||||
try (root.age as! JSONCounter<Int32>).increase(value: Int32(1)).increase(value: Int32(1))
|
||||
try (root.length as! JSONCounter<Int64>).increase(value: Int64(3)).increase(value: Int64(1))
|
||||
} catch {}
|
||||
(root.age as! JSONCounter<Int32>).increase(value: Int32(1)).increase(value: Int32(1))
|
||||
(root.length as! JSONCounter<Int64>).increase(value: Int64(3)).increase(value: Int64(1))
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
|
@ -59,21 +55,11 @@ final class CounterIntegrationTests: XCTestCase {
|
|||
result = await doc.toSortedJSON()
|
||||
XCTAssert(result == "{\"age\":8,\"length\":17}")
|
||||
|
||||
let expectation = XCTestExpectation(description: "failed Test")
|
||||
var failedTestResult = false
|
||||
|
||||
await doc.update { root in
|
||||
do {
|
||||
try (root.age as! JSONCounter<Int32>).increase(value: Int64(1))
|
||||
} catch {
|
||||
failedTestResult = true
|
||||
expectation.fulfill()
|
||||
(root.age as! JSONCounter<Int32>).increase(value: Int64(1))
|
||||
}
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssert(failedTestResult)
|
||||
result = await doc.toSortedJSON()
|
||||
XCTAssertEqual(result, "{\"age\":9,\"length\":17}")
|
||||
}
|
||||
|
||||
func test_can_sync_counter() async throws {
|
||||
|
@ -110,10 +96,8 @@ final class CounterIntegrationTests: XCTestCase {
|
|||
XCTAssert(result1 == result2)
|
||||
|
||||
await self.d1.update { root in
|
||||
do {
|
||||
try (root.age as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
try (root.length as! JSONCounter<Int64>).increase(value: Int64(3))
|
||||
} catch {}
|
||||
(root.age as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
(root.length as! JSONCounter<Int64>).increase(value: Int64(3))
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 2_000_000_000)
|
||||
|
@ -165,10 +149,8 @@ final class CounterIntegrationTests: XCTestCase {
|
|||
XCTAssert(result1 == "{\"counts\":[1,10]}")
|
||||
|
||||
await self.d1.update { root in
|
||||
do {
|
||||
try ((root.counts as! JSONArray)[0] as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
try ((root.counts as! JSONArray)[1] as! JSONCounter<Int32>).increase(value: Int32(3))
|
||||
} catch {}
|
||||
((root.counts as! JSONArray)[0] as! JSONCounter<Int32>).increase(value: Int32(5))
|
||||
((root.counts as! JSONArray)[1] as! JSONCounter<Int32>).increase(value: Int32(3))
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
|
|
|
@ -964,4 +964,42 @@ class DocumentTests: XCTestCase {
|
|||
{"data":["\\"hello\\"","\\n","\\b","\\t","\\f","\\r","\\\\"]}
|
||||
""")
|
||||
}
|
||||
|
||||
func test_can_handle_counter_overflow() async throws {
|
||||
let doc = Document(key: "test-doc")
|
||||
|
||||
await doc.update { root in
|
||||
root.age = JSONCounter(value: Int32(2_147_483_647))
|
||||
(root.age as? JSONCounter<Int32>)?.increase(value: 1)
|
||||
}
|
||||
|
||||
var result = await doc.toSortedJSON()
|
||||
XCTAssertEqual("{\"age\":-2147483648}", result)
|
||||
|
||||
await doc.update { root in
|
||||
root.age = JSONCounter(value: Int64(9_223_372_036_854_775_807))
|
||||
(root.age as? JSONCounter<Int64>)?.increase(value: 1)
|
||||
}
|
||||
result = await doc.toSortedJSON()
|
||||
XCTAssertEqual("{\"age\":-9223372036854775808}", result)
|
||||
}
|
||||
|
||||
func test_can_handle_counter_float_value() async throws {
|
||||
let doc = Document(key: "test-doc")
|
||||
|
||||
await doc.update { root in
|
||||
root.age = JSONCounter(value: Int32(10))
|
||||
(root.age as? JSONCounter<Int32>)?.increase(value: 3.5)
|
||||
}
|
||||
|
||||
var result = await doc.toSortedJSON()
|
||||
XCTAssertEqual("{\"age\":13}", result)
|
||||
|
||||
await doc.update { root in
|
||||
root.age = JSONCounter(value: Int64(0))
|
||||
(root.age as? JSONCounter<Int64>)?.increase(value: -1.5)
|
||||
}
|
||||
result = await doc.toSortedJSON()
|
||||
XCTAssertEqual("{\"age\":-1}", result)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue