Skip to content

Commit

Permalink
Fix errors when editing Tree due to missing insPrevID in CRDTTree (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
7hong13 authored Mar 11, 2024
1 parent 79f4573 commit 2f1b4da
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 18 deletions.
195 changes: 177 additions & 18 deletions yorkie/src/androidTest/kotlin/dev/yorkie/document/json/JsonTreeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.yorkie.TreeTest
import dev.yorkie.core.Client
import dev.yorkie.core.GENERAL_TIMEOUT
import dev.yorkie.core.Presence
import dev.yorkie.core.createClient
import dev.yorkie.core.withTwoClientsAndDocuments
import dev.yorkie.document.Document
import dev.yorkie.document.Document.Event.LocalChange
Expand Down Expand Up @@ -1764,8 +1765,7 @@ class JsonTreeTest {
},
Updater(c2, d2),
)
assertTreesXmlEquals("<doc><p>hello</p></doc>", d1)
assertTreesXmlEquals("<doc><p>hello</p></doc>", d2)
assertTreesXmlEquals("<doc><p>hello</p></doc>", d1, d2)

updateAndSync(
Updater(c1, d1) { root, presence ->
Expand All @@ -1775,8 +1775,7 @@ class JsonTreeTest {
},
Updater(c2, d2),
)
assertTreesXmlEquals("<doc><p>ahello</p></doc>", d1)
assertTreesXmlEquals("<doc><p>ahello</p></doc>", d2)
assertTreesXmlEquals("<doc><p>ahello</p></doc>", d1, d2)
val selectionType = object : TypeToken<TreePosStructRange>() {}.type
val selection = gson.fromJson<TreePosStructRange>(
d1.allPresences.value[c1.requireClientId()]!!["selection"],
Expand All @@ -1794,8 +1793,7 @@ class JsonTreeTest {
root.rootTree().edit(2, 2, text { "b" })
},
)
assertTreesXmlEquals("<doc><p>abhello</p></doc>", d1)
assertTreesXmlEquals("<doc><p>abhello</p></doc>", d2)
assertTreesXmlEquals("<doc><p>abhello</p></doc>", d1, d2)

withTimeout(GENERAL_TIMEOUT) {
while (d1Events.isEmpty()) {
Expand Down Expand Up @@ -1832,9 +1830,7 @@ class JsonTreeTest {
},
Updater(c2, d1),
)
var expectedXml = "<r><c><u><p><n></n></p></u></c><c><p><n></n></p></c></r>"
assertTreesXmlEquals(expectedXml, d1)
assertTreesXmlEquals(expectedXml, d2)
assertTreesXmlEquals("<r><c><u><p><n></n></p></u></c><c><p><n></n></p></c></r>", d1, d2)

updateAndSync(
Updater(c1, d1),
Expand All @@ -1844,9 +1840,11 @@ class JsonTreeTest {
root.rootTree().editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 2), text { "3" })
},
)
expectedXml = "<r><c><u><p><n></n></p></u></c><c><p><n>123</n></p></c></r>"
assertTreesXmlEquals(expectedXml, d1)
assertTreesXmlEquals(expectedXml, d2)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>123</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1) { root, _ ->
Expand All @@ -1855,9 +1853,11 @@ class JsonTreeTest {
},
Updater(c2, d1),
)
expectedXml = "<r><c><u><p><n></n></p></u></c><c><p><n>1abcdefgh23</n></p></c></r>"
assertTreesXmlEquals(expectedXml, d1)
assertTreesXmlEquals(expectedXml, d2)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1abcdefgh23</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1),
Expand Down Expand Up @@ -1900,16 +1900,175 @@ class JsonTreeTest {
root.rootTree().editByPath(listOf(1, 0, 0, 7), listOf(1, 0, 0, 8))
},
)
expectedXml = "<r><c><u><p><n></n></p></u></c><c><p><n>1abcd45gh23</n></p></c></r>"
assertTreesXmlEquals(expectedXml, d1)
assertTreesXmlEquals(expectedXml, d2)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1abcd45gh23</n></p></c></r>",
d1,
d2,
)

assertIs<LocalChange>(d2Events.firstOrNull())

collectJob.cancel()
}
}

@Test
fun test_client_reload_cases() {
withTwoClientsAndDocuments(realTimeSync = false) { c1, c2, d1, d2, key ->
// Perform a dummy update to apply changes up to the snapshot threshold.
repeat(500) {
d1.updateAsync { root, _ ->
root["num"] = 0
}.await()
}

// Start scenario.
updateAndSync(
Updater(c1, d1) { root, _ ->
root.setNewTree(
"t",
element("r") {
element("c") {
element("u") {
element("p") {
element("n")
}
}
}
element("c") {
element("p") {
element("n")
}
}
},
)
},
Updater(c2, d2),
)
assertTreesXmlEquals("<r><c><u><p><n></n></p></u></c><c><p><n></n></p></c></r>", d1, d2)

updateAndSync(
Updater(c1, d1) { root, _ ->
root.rootTree().apply {
editByPath(listOf(1, 0, 0, 0), listOf(1, 0, 0, 0), text { "1" })
editByPath(listOf(1, 0, 0, 1), listOf(1, 0, 0, 1), text { "2" })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 2), text { "3" })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 2), text { " " })
editByPath(listOf(1, 0, 0, 3), listOf(1, 0, 0, 3), text { "네이버랑 " })
}
},
Updater(c2, d2),
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>12 네이버랑 3</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1),
Updater(c2, d2) { root, _ ->
root.rootTree().apply {
editByPath(listOf(1, 0, 0, 1), listOf(1, 0, 0, 8), text { " 2 네이버랑 " })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 2), text { "" })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 3), text { "" })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 3), text { "" })
editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 3), text { "카카" })
editByPath(listOf(1, 0, 0, 3), listOf(1, 0, 0, 4), text { "" })
editByPath(listOf(1, 0, 0, 3), listOf(1, 0, 0, 4), text { "카오" })
editByPath(listOf(1, 0, 0, 4), listOf(1, 0, 0, 5), text { "" })
editByPath(listOf(1, 0, 0, 4), listOf(1, 0, 0, 5), text { "오라" })
editByPath(listOf(1, 0, 0, 5), listOf(1, 0, 0, 6), text { "" })
editByPath(listOf(1, 0, 0, 6), listOf(1, 0, 0, 6), text { " " })
}
},
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 카카오랑 2 네이버랑 3</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1) { root, _ ->
root.rootTree().apply {
editByPath(listOf(1, 0, 0, 13), listOf(1, 0, 0, 14))
editByPath(listOf(1, 0, 0, 12), listOf(1, 0, 0, 13))
}
},
Updater(c2, d2),
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 카카오랑 2 네이버3</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1),
Updater(c2, d2) { root, _ ->
root.rootTree().apply {
editByPath(listOf(1, 0, 0, 6), listOf(1, 0, 0, 7))
editByPath(listOf(1, 0, 0, 5), listOf(1, 0, 0, 6))
}
},
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 카카오2 네이버3</n></p></c></r>",
d1,
d2,
)

updateAndSync(
Updater(c1, d1) { root, _ ->
root.rootTree().editByPath(listOf(1, 0, 0, 9), listOf(1, 0, 0, 10))
},
Updater(c2, d2),
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 카카오2 네이3</n></p></c></r>",
d1,
d2,
)

// A new client has been added.
val d3 = Document(key)
val c3 = createClient()
c3.activateAsync().await()
c3.attachAsync(d3, isRealTimeSync = false).await()
assertTreesXmlEquals(d2.getRoot().rootTree().toXml(), d3)

updateAndSync(
Updater(c3, d3) { root, _ ->
root.rootTree().apply {
editByPath(listOf(1, 0, 0, 4), listOf(1, 0, 0, 5))
editByPath(listOf(1, 0, 0, 3), listOf(1, 0, 0, 4))
}
},
Updater(c2, d2),
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 카2 네이3</n></p></c></r>",
d2,
d3,
)

updateAndSync(
Updater(c3, d3) { root, _ ->
root.rootTree().editByPath(listOf(1, 0, 0, 2), listOf(1, 0, 0, 3))
},
Updater(c2, d2),
)
assertTreesXmlEquals(
"<r><c><u><p><n></n></p></u></c><c><p><n>1 2 네이3</n></p></c></r>",
d2,
d3,
)

c3.deactivateAsync().await()
}
}

companion object {

fun JsonObject.rootTree() = getAs<JsonTree>("t")
Expand Down
2 changes: 2 additions & 0 deletions yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtTree.kt
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,8 @@ internal data class CrdtTreeNode private constructor(
it.childrenInternal.forEach { child ->
child.parent = it
}
it.insPrevID = insPrevID
it.insNextID = insNextID
}
}

Expand Down

0 comments on commit 2f1b4da

Please sign in to comment.