yorkie-ios-sdk/Tests/Unit/Document/CRDT/CRDTRootTests.swift

248 lines
11 KiB
Swift

/*
* Copyright 2022 The Yorkie Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import XCTest
@testable import Yorkie
class CRDTRootTests: XCTestCase {
private let actorId = "actor-1"
func test_can_find_with_createdAt() {
// given
let crdtObject = CRDTObject(createdAt: TimeTicket.initial)
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
crdtObject.set(key: "K1", value: a1)
let a2 = Primitive(value: .string("A2"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
crdtObject.set(key: "K2", value: a2)
let a3 = Primitive(value: .string("A3"), createdAt: TimeTicket(lamport: 3, delimiter: 0, actorID: actorId))
crdtObject.set(key: "K3", value: a3)
// when
let target = CRDTRoot(rootObject: crdtObject)
let result = target.find(createdAt: a1.createdAt)
// then
XCTAssertEqual(result?.toJSON(), "\"A1\"")
}
func test_can_create_sub_paths() throws {
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let a2 = Primitive(value: .string("A2"), createdAt: TimeTicket(lamport: 3, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a2", value: a2)
let object2 = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2.set(key: "K-B1", value: b1)
let object3 = CRDTObject(createdAt: TimeTicket(lamport: 6, delimiter: 0, actorID: actorId))
let c1 = Primitive(value: .string("c1"), createdAt: TimeTicket(lamport: 7, delimiter: 0, actorID: actorId))
object3.set(key: "K-c1", value: c1)
rootObject.set(key: "K-$a3", value: object2)
object2.set(key: "K-.B2", value: object3)
let target = CRDTRoot(rootObject: rootObject)
let findedC1 = target.find(createdAt: c1.createdAt)
XCTAssertEqual(findedC1?.toJSON(), "\"c1\"")
let resultA1 = try target.createSubPaths(createdAt: a1.createdAt)
XCTAssertEqual(resultA1, ["$", "K-a1"])
let resultC1 = try target.createSubPaths(createdAt: c1.createdAt)
XCTAssertEqual(resultC1, ["$", "K-\\$a3", "K-\\.B2", "K-c1"])
let result = try target.createPath(createdAt: c1.createdAt)
XCTAssertEqual(result, "$.K-\\$a3.K-\\.B2.K-c1")
}
func test_can_resturn_elements_count() throws {
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let a2 = Primitive(value: .string("A2"), createdAt: TimeTicket(lamport: 3, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a2", value: a2)
let object2 = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2.set(key: "K-B1", value: b1)
let target = CRDTRoot(rootObject: rootObject)
XCTAssertEqual(target.elementMapSize, 3)
}
func test_getRemovedElementSetSize() throws {
// given
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let a2 = Primitive(value: .string("A2"), createdAt: TimeTicket(lamport: 3, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a2", value: a2)
let object2 = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2.set(key: "K-B1", value: b1)
let target = CRDTRoot(rootObject: rootObject)
b1.removedAt = TimeTicket(lamport: 6, delimiter: 0, actorID: self.actorId)
target.registerRemovedElement(b1)
a2.removedAt = TimeTicket(lamport: 7, delimiter: 0, actorID: self.actorId)
target.registerRemovedElement(a2)
// when
let result = target.removedElementSetSize
// then
XCTAssertEqual(result, 2)
}
func test_can_return_garbage_target_length() throws {
// given
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let object2ToRemove = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2ToRemove.set(key: "K-B1", value: b1)
let object3 = CRDTObject(createdAt: TimeTicket(lamport: 6, delimiter: 0, actorID: actorId))
let c1 = Primitive(value: .string("c1"), createdAt: TimeTicket(lamport: 7, delimiter: 0, actorID: actorId))
object3.set(key: "K-c1", value: c1)
rootObject.set(key: "K-a3", value: object2ToRemove)
object2ToRemove.set(key: "K-B2", value: object3)
// when
let target = CRDTRoot(rootObject: rootObject)
object2ToRemove.removedAt = TimeTicket(lamport: 8, delimiter: 0, actorID: self.actorId)
target.registerRemovedElement(object2ToRemove)
let result = target.garbageLength
// then
XCTAssertEqual(result, 4)
}
func test_garbageCollect() throws {
// given
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let object2ToGarbage = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2ToGarbage.set(key: "K-B1", value: b1)
let object3 = CRDTObject(createdAt: TimeTicket(lamport: 6, delimiter: 0, actorID: actorId))
let c1 = Primitive(value: .string("c1"), createdAt: TimeTicket(lamport: 7, delimiter: 0, actorID: actorId))
object3.set(key: "K-c1", value: c1)
rootObject.set(key: "K-a3", value: object2ToGarbage)
object2ToGarbage.set(key: "K-B2", value: object3)
// when
let target = CRDTRoot(rootObject: rootObject)
let removedAtForGarbage = TimeTicket(lamport: 8, delimiter: 0, actorID: self.actorId)
object2ToGarbage.removedAt = removedAtForGarbage
target.registerRemovedElement(object2ToGarbage)
a1.removedAt = TimeTicket(lamport: 9, delimiter: 0, actorID: self.actorId)
target.registerRemovedElement(a1)
let garbageLength = target.garbageLength
XCTAssertEqual(garbageLength, 5)
let result = target.garbageCollect(lessThanOrEqualTo: removedAtForGarbage)
// then
XCTAssertEqual(result, 4)
XCTAssertNil(target.find(createdAt: object2ToGarbage.createdAt))
XCTAssertNil(target.find(createdAt: b1.createdAt))
XCTAssertNil(target.find(createdAt: object3.createdAt))
XCTAssertNil(target.find(createdAt: c1.createdAt))
}
func test_toSortedJSON() throws {
// given
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let object2ToGarbage = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2ToGarbage.set(key: "K-B1", value: b1)
let object3 = CRDTObject(createdAt: TimeTicket(lamport: 6, delimiter: 0, actorID: actorId))
let c1 = Primitive(value: .string("c1"), createdAt: TimeTicket(lamport: 7, delimiter: 0, actorID: actorId))
object3.set(key: "K-c1", value: c1)
rootObject.set(key: "K-a3", value: object2ToGarbage)
object2ToGarbage.set(key: "K-B2", value: object3)
// when
let target = CRDTRoot(rootObject: rootObject)
let result = target.debugDescription
// then
XCTAssertEqual(result, "{\"K-a1\":\"A1\",\"K-a3\":{\"K-B1\":\"B1\",\"K-B2\":{\"K-c1\":\"c1\"}}}")
}
func test_can_deepCopy() {
// given
let rootObject = CRDTObject(createdAt: TimeTicket(lamport: 1, delimiter: 0, actorID: actorId))
let a1 = Primitive(value: .string("A1"), createdAt: TimeTicket(lamport: 2, delimiter: 0, actorID: actorId))
rootObject.set(key: "K-a1", value: a1)
let object2 = CRDTObject(createdAt: TimeTicket(lamport: 4, delimiter: 0, actorID: actorId))
let b1 = Primitive(value: .string("B1"), createdAt: TimeTicket(lamport: 5, delimiter: 0, actorID: actorId))
object2.set(key: "K-B1", value: b1)
let object3 = CRDTObject(createdAt: TimeTicket(lamport: 6, delimiter: 0, actorID: actorId))
let c1 = Primitive(value: .string("c1"), createdAt: TimeTicket(lamport: 7, delimiter: 0, actorID: actorId))
object3.set(key: "K-c1", value: c1)
rootObject.set(key: "K-a3", value: object2)
object2.set(key: "K-B2", value: object3)
let target = CRDTRoot(rootObject: rootObject)
let expectedJson = "{\"K-a1\":\"A1\",\"K-a3\":{\"K-B1\":\"B1\",\"K-B2\":{\"K-c1\":\"c1\"}}}"
XCTAssertEqual(rootObject.toSortedJSON(), expectedJson)
// when
let deepCopied = target.deepcopy()
let result = deepCopied.find(createdAt: rootObject.createdAt)
// then
XCTAssertEqual(result?.toSortedJSON(), expectedJson)
}
}