Skip to content
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

Fix incorrect calculation in indexTree.treePosToPath operation #150

Merged
merged 1 commit into from
Feb 23, 2024
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
11 changes: 8 additions & 3 deletions Sources/Util/IndexTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@ extension IndexTreeNode {
}

/**
* `hasTextChild` returns true if the node has an text child.
* `hasTextChild` returns true if the node's children consist of only text children.
*/
var hasTextChild: Bool {
self.children.first { $0.isText } != nil
!self.children.isEmpty && !self.children.contains { !$0.isText }
}

/**
Expand Down Expand Up @@ -778,8 +778,13 @@ class IndexTree<T: IndexTreeNode> {
}

let sizeOfLeftSiblings = addSizeOfLeftSiblings(parent: node.parent!, offset: offset)
node = node.parent!
path.append(sizeOfLeftSiblings + Int(treePos.offset))
node = node.parent!
} else if node.hasTextChild {
// TODO(hackerwins): The function does not consider the situation
// where Element and Text nodes are mixed in the Element's Children.
let sizeOfLeftSiblings = addSizeOfLeftSiblings(parent: node, offset: Int(treePos.offset))
path.append(sizeOfLeftSiblings)
} else {
path.append(Int(treePos.offset))
}
Expand Down
112 changes: 111 additions & 1 deletion Tests/Integration/IntegrationHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import Foundation
import XCTest
@testable import Yorkie

func withTwoClientsAndDocuments(_ title: String, _ callback: (Client, Document, Client, Document) async throws -> Void) async throws {
Expand Down Expand Up @@ -43,3 +43,113 @@ func withTwoClientsAndDocuments(_ title: String, _ callback: (Client, Document,
try await c1.deactivate()
try await c2.deactivate()
}

protocol OperationInfoForDebug {}

struct TreeEditOpInfoForDebug: OperationInfoForDebug {
let from: Int?
let to: Int?
let value: [any JSONTreeNode]?
let fromPath: [Int]?
let toPath: [Int]?

func compare(_ operation: TreeEditOpInfo) {
if let value = from {
XCTAssertEqual(value, operation.from)
}
if let value = to {
XCTAssertEqual(value, operation.to)
}
if let value = value {
XCTAssertEqual(value.count, operation.value.count)
if operation.value.count - 1 >= 0 {
for idx in 0 ... operation.value.count - 1 {
if let exp = value[idx] as? JSONTreeTextNode, let oper = operation.value[idx] as? JSONTreeTextNode {
XCTAssertEqual(exp, oper)
} else if let exp = value[idx] as? JSONTreeElementNode, let oper = operation.value[idx] as? JSONTreeElementNode {
XCTAssertEqual(exp, oper)
} else {
XCTAssertFalse(true)
}
}
}
}
if let value = fromPath {
XCTAssertEqual(value, operation.fromPath)
}
if let value = toPath {
XCTAssertEqual(value, operation.toPath)
}
}
}

struct TreeStyleOpInfoForDebug: OperationInfoForDebug {
let from: Int?
let to: Int?
let value: [String: Codable]?
let fromPath: [Int]?

func compare(_ operation: TreeStyleOpInfo) {
if let value = from {
XCTAssertEqual(value, operation.from)
}
if let value = to {
XCTAssertEqual(value, operation.to)
}
if let value = value {
XCTAssertEqual(value.count, operation.value.count)
if operation.value.count - 1 >= 0 {
for key in operation.value.keys {
XCTAssertEqual(value[key]?.toJSONString, operation.value[key]?.toJSONString)
}
}
}
if let value = fromPath {
XCTAssertEqual(value, operation.fromPath)
}
}
}

func subscribeDocs(_ d1: Document, _ d2: Document, _ d1Expected: [any OperationInfoForDebug]?, _ d2Expected: [any OperationInfoForDebug]?) async {
var d1Operations: [any OperationInfo] = []
var d1Index = 0

await d1.subscribe("$.t") { event in
if let event = event as? LocalChangeEvent {
d1Operations.append(contentsOf: event.value.operations)
} else if let event = event as? RemoteChangeEvent {
d1Operations.append(contentsOf: event.value.operations)
}

while d1Index <= d1Operations.count - 1 {
if let d1Expected, let expected = d1Expected[safe: d1Index] as? TreeEditOpInfoForDebug, let operation = d1Operations[safe: d1Index] as? TreeEditOpInfo {
expected.compare(operation)
} else if let d1Expected, let expected = d1Expected[safe: d1Index] as? TreeStyleOpInfoForDebug, let operation = d1Operations[safe: d1Index] as? TreeStyleOpInfo {
expected.compare(operation)
}

d1Index += 1
}
}

var d2Operations: [any OperationInfo] = []
var d2Index = 0

await d2.subscribe("$.t") { event in
if let event = event as? LocalChangeEvent {
d2Operations.append(contentsOf: event.value.operations.compactMap { $0 as? TreeEditOpInfo })
} else if let event = event as? RemoteChangeEvent {
d2Operations.append(contentsOf: event.value.operations.compactMap { $0 as? TreeEditOpInfo })
}

while d2Index <= d2Operations.count - 1 {
if let d2Expected, let expected = d2Expected[safe: d2Index] as? TreeEditOpInfoForDebug, let operation = d2Operations[safe: d2Index] as? TreeEditOpInfo {
expected.compare(operation)
} else if let d2Expected, let expected = d2Expected[safe: d1Index] as? TreeStyleOpInfoForDebug, let operation = d2Operations[safe: d1Index] as? TreeStyleOpInfo {
expected.compare(operation)
}

d2Index += 1
}
}
}
Loading
Loading