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

Improve test coverage #19

Merged
merged 20 commits into from
Oct 7, 2022
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
15 changes: 11 additions & 4 deletions yorkie/src/main/kotlin/dev/yorkie/document/crdt/CrdtArray.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ internal class CrdtArray(
createdAt: TimeTicket,
) : CrdtContainer(createdAt), Iterable<CrdtElement> {
val head
get() = elements.dummyHead.value
get() = elements.head

val last
get() = elements.last.value

val length
get() = elements.length

val lastCreated
get() = elements.getLastCreatedAt()

/**
* Returns the sub path of the given [createdAt] element.
*/
Expand Down Expand Up @@ -132,12 +135,16 @@ internal class CrdtArray(
return object : Iterator<CrdtElement> {
var node = elements.firstOrNull()

override fun hasNext() = node?.isRemoved == false
override fun hasNext(): Boolean {
while (node?.isRemoved == true) {
node = node?.next
}
return node != null
}

override fun next(): CrdtElement {
return requireNotNull(node).value.also {
elements.drop(1)
node = elements.firstOrNull()
node = node?.next
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions yorkie/src/main/kotlin/dev/yorkie/document/crdt/RgaTreeList.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.yorkie.document.crdt

import androidx.annotation.VisibleForTesting
import dev.yorkie.document.time.TimeTicket
import dev.yorkie.document.time.TimeTicket.Companion.InitialTimeTicket
import dev.yorkie.document.time.TimeTicket.Companion.compareTo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like some of the work I've done got reverted on merge.
check 9e1f465#diff-d11a0b42e2590d7cdb00a3e25077e19cd889cc034a160dd3a99cea4c352adca4R86

Expand All @@ -8,8 +9,8 @@ import dev.yorkie.util.SplayTreeSet
/**
* [RgaTreeList] is replicated growable array.
*/
internal class RgaTreeList : Iterable<RgaTreeList.RgaTreeListNode> {
val dummyHead = RgaTreeListNode(Primitive.of(1, InitialTimeTicket)).apply {
internal class RgaTreeList private constructor() : Iterable<RgaTreeList.RgaTreeListNode> {
private val dummyHead = RgaTreeListNode(Primitive.of(1, InitialTimeTicket)).apply {
value.removedAt = InitialTimeTicket
}
var last: RgaTreeListNode = dummyHead
Expand All @@ -25,6 +26,9 @@ internal class RgaTreeList : Iterable<RgaTreeList.RgaTreeListNode> {
val length
get() = nodeMapByIndex.length

val head
get() = dummyHead.value

/**
* Adds a new node with [value] after the last node.
*/
Expand Down Expand Up @@ -82,9 +86,7 @@ internal class RgaTreeList : Iterable<RgaTreeList.RgaTreeListNode> {
val node = nodeMapByCreatedAt[createdAt]
?: error("can't find the given node createdAt: $createdAt")

if (prevNode != node &&
(node.value.movedAt == null || checkNotNull(node.value.movedAt) < executedAt)
) {
if (prevNode != node && node.value.movedAt < executedAt) {
delete(node)
insertAfter(prevNode.createdAt, node.value, executedAt)
node.value.movedAt = executedAt
Expand Down Expand Up @@ -202,6 +204,7 @@ internal class RgaTreeList : Iterable<RgaTreeList.RgaTreeListNode> {
}
}

@VisibleForTesting
data class RgaTreeListNode(val value: CrdtElement) {
var prev: RgaTreeListNode? = null
var next: RgaTreeListNode? = null
Expand Down
85 changes: 72 additions & 13 deletions yorkie/src/test/kotlin/dev/yorkie/document/crdt/CrdtArrayTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.yorkie.document.crdt

import dev.yorkie.document.time.ActorID
import dev.yorkie.document.time.TimeTicket
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -31,13 +32,13 @@ class CrdtArrayTest {
assertEquals(crdtElements.size + 1, target.length)

target.remove(timeTickets[1], timeTickets[2])
assertEquals("A1B0C1D1E1F1G1", target.getStructureAsString())
assertEquals("ACDEFG", target.getStructureAsString())
target.remove(timeTickets[2], timeTickets[3])
assertEquals("A1B0C0D1E1F1G1", target.getStructureAsString())
assertEquals("ADEFG", target.getStructureAsString())
target.remove(timeTickets[3], timeTickets[4])
assertEquals("A1B0C0D0E1F1G1", target.getStructureAsString())
assertEquals("AEFG", target.getStructureAsString())
target.remove(timeTickets[6], timeTickets[5])
assertEquals("A1B0C0D0E1F1G1", target.getStructureAsString())
assertEquals("AEFG", target.getStructureAsString())

assertEquals(crdtElements.size - 3 + 1, target.length)
}
Expand All @@ -51,13 +52,34 @@ class CrdtArrayTest {

target.delete(crdtElements[0])
assertEquals(crdtElements.size - 1 + 1, target.length)
assertEquals("B1C1D1E1F1G1", target.getStructureAsString())
assertEquals("BCDEFG", target.getStructureAsString())
target.delete(crdtElements[1])
assertEquals(crdtElements.size - 2 + 1, target.length)
assertEquals("C1D1E1F1G1", target.getStructureAsString())
assertEquals("CDEFG", target.getStructureAsString())
target.delete(crdtElements[2])
assertEquals(crdtElements.size - 3 + 1, target.length)
assertEquals("D1E1F1G1", target.getStructureAsString())
assertEquals("DEFG", target.getStructureAsString())
}

@Test
fun `should handle removeByIndex operations`() {
assertEquals(1, target.length)

crdtElements.forEach { target.insertAfter(target.last.createdAt, it) }
assertEquals(crdtElements.size + 1, target.length)

target.removeByIndex(1, timeTickets[2])
assertEquals("BCDEFG", target.getStructureAsString())
target.removeByIndex(1, timeTickets[3])
assertEquals("CDEFG", target.getStructureAsString())
target.removeByIndex(1, timeTickets[4])
assertEquals("DEFG", target.getStructureAsString())
target.removeByIndex(1, timeTickets[5])
assertEquals("EFG", target.getStructureAsString())
target.removeByIndex(crdtElements.size, timeTickets[6])
assertEquals("EFG", target.getStructureAsString())

assertEquals(crdtElements.size - 4 + 1, target.length)
}

@Test
Expand All @@ -68,15 +90,18 @@ class CrdtArrayTest {
assertEquals(crdtElements.size + 1, target.length)

target.insertAfter(timeTickets[0], Primitive.of(1, createTimeTicket()))
assertEquals("A1H1B1C1D1E1F1G1", target.getStructureAsString())
assertEquals("AHBCDEFG", target.getStructureAsString())
}

@Test
fun `should handle moving an element after the given element`() {
assertEquals(1, target.length)

crdtElements.forEach { target.insertAfter(target.last.createdAt, it) }
assertEquals(crdtElements.size + 1, target.length)

target.moveAfter(timeTickets[5], timeTickets[4], createTimeTicket())
assertEquals("A1B1C1D1F1E1G1", target.getStructureAsString())
assertEquals("ABCDFEG", target.getStructureAsString())
assertEquals(crdtElements.size + 1, target.length)
}

Expand Down Expand Up @@ -116,17 +141,51 @@ class CrdtArrayTest {
assertEquals(sampleTarget1.getStructureAsString(), sampleTarget2.getStructureAsString())
}

@Test
fun `should handle get operations with index`() {
assertEquals(1, target.length)

crdtElements.forEach { target.insertAfter(target.last.createdAt, it) }
assertEquals(crdtElements.size + 1, target.length)

assertEquals(crdtElements[0], target[1])
assertEquals(null, target[10])
}

@Test
fun `should handle get operations with value`() {
assertEquals(1, target.length)

crdtElements.forEach { target.insertAfter(target.last.createdAt, it) }
assertEquals(crdtElements.size + 1, target.length)

assertEquals(crdtElements[0], target[timeTickets[0]])
assertEquals(null, target[createTimeTicket()])
}

@Test
fun `should handle getPrevCreatedAt operations`() {
assertEquals(1, target.length)

crdtElements.forEach { target.insertAfter(target.last.createdAt, it) }
assertEquals(crdtElements.size + 1, target.length)

assertEquals(timeTickets[0], target.getPrevCreatedAt(timeTickets[1]))
assertEquals(timeTickets[1], target.getPrevCreatedAt(timeTickets[2]))
Assert.assertThrows(IllegalStateException::class.java) {
target.getPrevCreatedAt(createTimeTicket())
}
}

private fun createTimeTicket(): TimeTicket {
return TimeTicket(crdtElements.size.toLong(), TimeTicket.INITIAL_DELIMITER, ActorID("H"))
}

private fun createSampleCrdtArray(): CrdtArray {
return CrdtArray(RgaTreeList.create(), TimeTicket.InitialTimeTicket)
return CrdtArray.create(TimeTicket.InitialTimeTicket)
}

private fun CrdtArray.getStructureAsString() = buildString {
target.elements.forEach {
append(it.createdAt.actorID.id + if (it.isRemoved) 0 else 1)
}
target.forEach { append(it.createdAt.actorID.id) }
}
}
33 changes: 33 additions & 0 deletions yorkie/src/test/kotlin/dev/yorkie/document/crdt/RgaTreeListTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dev.yorkie.document.time.ActorID
import dev.yorkie.document.time.TimeTicket
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotSame
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test

Expand Down Expand Up @@ -48,6 +49,10 @@ class RgaTreeListTest {

@Test
fun `should handle remove operations`() {
assertThrows(IllegalStateException::class.java) {
target.remove(timeTickets[0], timeTickets[1])
}

assertEquals(1, target.length)

crdtElements.forEach(target::insert)
Expand All @@ -64,8 +69,32 @@ class RgaTreeListTest {
assertEquals(crdtElements.size - 3 + 1, target.length)
}

@Test
fun `should handle removeByIndex operations`() {
assertEquals(1, target.length)

crdtElements.forEach(target::insert)

target.removeByIndex(1, timeTickets[2])
assertEquals("A0B1C1D1E1F1G1", target.getStructureAsString())
target.removeByIndex(1, timeTickets[3])
assertEquals("A0B0C1D1E1F1G1", target.getStructureAsString())
target.removeByIndex(1, timeTickets[4])
assertEquals("A0B0C0D1E1F1G1", target.getStructureAsString())
target.removeByIndex(1, timeTickets[5])
assertEquals("A0B0C0D0E1F1G1", target.getStructureAsString())
target.removeByIndex(crdtElements.size, timeTickets[6])
assertEquals("A0B0C0D0E1F1G1", target.getStructureAsString())

assertEquals(crdtElements.size - 4 + 1, target.length)
}

@Test
fun `should handle delete operations`() {
assertThrows(IllegalStateException::class.java) {
target.delete(crdtElements[0])
}

assertEquals(1, target.length)

crdtElements.forEach(target::insert)
Expand Down Expand Up @@ -95,6 +124,10 @@ class RgaTreeListTest {

@Test
fun `should handle moving an element after the given element`() {
assertThrows(IllegalStateException::class.java) {
target.moveAfter(timeTickets[0], timeTickets[1], timeTickets[1])
}

crdtElements.forEach(target::insert)

target.moveAfter(timeTickets[5], timeTickets[4], createTimeTicket())
Expand Down