Skip to content

Commit

Permalink
test(model-server): ensure ReplicatedModel gets disposed
Browse files Browse the repository at this point in the history
Otherwise, it will try to continue polling and throw exceptions, which causes the tests to fail.
  • Loading branch information
slisson committed Jan 28, 2025
1 parent f1ff628 commit a34591f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ReplicatedModel(
val branchRef: BranchReference,
private val providedScope: CoroutineScope? = null,
initialRemoteVersion: CLVersion? = null,
) {
) : Closable {
private val scope = providedScope ?: CoroutineScope(Dispatchers.Default)
private var state = State.New
private var localModel: LocalModel? = null
Expand Down Expand Up @@ -113,6 +113,10 @@ class ReplicatedModel(
}
}

override fun close() {
dispose()
}

private suspend fun remoteVersionReceived(newRemoteVersion: CLVersion) {
if (isDisposed()) return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,27 @@ class ReplicatedModelTest {
val newClient = createModelClient()
// we do not provide an initial version, so we expect to fetch the latest one (with one "hello" child)
val scope = CoroutineScope(Dispatchers.Default)
val replicatedModel = ReplicatedModel(newClient, defaultBranchReference, scope)
try {
replicatedModel.getBranch()
// if we get here, then we missed an expected exception
assertFalse(true)
} catch (ex: Exception) {
/*
Expected exception, because we did not specify an initial version.
So without an explicit start we do not expect anything useful here.
*/
assertTrue(ex.instanceOf(IllegalStateException::class))
}
ReplicatedModel(newClient, defaultBranchReference, scope).use { replicatedModel ->
try {
replicatedModel.getBranch()
// if we get here, then we missed an expected exception
assertFalse(true)
} catch (ex: Exception) {
/*
Expected exception, because we did not specify an initial version.
So without an explicit start we do not expect anything useful here.
*/
assertTrue(ex.instanceOf(IllegalStateException::class))
}

val branch = replicatedModel.start()
// Step 3: wait a bit so replicated model can fetch the new versions from the server
waitUntilChildArrives(branch, scope, 500)
val branch = replicatedModel.start()
// Step 3: wait a bit so replicated model can fetch the new versions from the server
waitUntilChildArrives(branch, scope, 500)

// Step 4: check, eventually we must have the one "hello" child
val children = getHelloChildrenOfRootNode(branch)
assertEquals(1, children.size)
// Step 4: check, eventually we must have the one "hello" child
val children = getHelloChildrenOfRootNode(branch)
assertEquals(1, children.size)
}
}

@Test
Expand All @@ -93,25 +94,26 @@ class ReplicatedModelTest {

val scope = CoroutineScope(Dispatchers.Default)
// Step 2.2: we provide an initial version, so we expect to fetch it first (0 "hello" child)
val replicatedModel = ReplicatedModel(
ReplicatedModel(
newClient,
defaultBranchReference,
providedScope = scope,
initialRemoteVersion = initialVersionClone as CLVersion,
)
val branch = replicatedModel.getBranch()
).use { replicatedModel ->
val branch = replicatedModel.getBranch()

// Step 3: check, here we must have 0 "hello" child
val emptyChildren = getHelloChildrenOfRootNode(branch)
assertTrue(emptyChildren.isEmpty())
// Step 3: check, here we must have 0 "hello" child
val emptyChildren = getHelloChildrenOfRootNode(branch)
assertTrue(emptyChildren.isEmpty())

replicatedModel.start()
// Step 4: wait a bit so replicated model can fetch the new versions from the server
waitUntilChildArrives(branch, scope, 500)
replicatedModel.start()
// Step 4: wait a bit so replicated model can fetch the new versions from the server
waitUntilChildArrives(branch, scope, 500)

// Step 5: check, eventually we must have 1 "hello" child
val children = getHelloChildrenOfRootNode(branch)
assertEquals(1, children.size)
// Step 5: check, eventually we must have 1 "hello" child
val children = getHelloChildrenOfRootNode(branch)
assertEquals(1, children.size)
}
}

private fun runTest(block: suspend ApplicationTestBuilder.() -> Unit) = testApplication {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,46 +71,48 @@ class ReplicatedRepositoryTest {
val repositoryId = RepositoryId("repo1")
modelClient.initRepository(repositoryId)

val replicatedModel = modelClient.getReplicatedModel(repositoryId.getBranchReference())
val replicatedModel2 = modelClient2.getReplicatedModel(repositoryId.getBranchReference())
val branch1 = replicatedModel.start()
val branch2 = replicatedModel2.start()

val rand = Random(34554)

for (changeId in 1..10) {
println("change set $changeId")
val branchToChange = if (rand.nextBoolean()) {
println("changing branch 1")
branch1
} else {
println("changing branch 2")
branch2
}
branchToChange.runWrite {
val changeGenerator = RandomModelChangeGenerator(branchToChange.getRootNode(), rand)
repeat(1000) { _ ->
changeGenerator.applyRandomChange()
}
println("new tree: " + (branchToChange.transaction.tree as CLTree).hash)
}
modelClient.getReplicatedModel(repositoryId.getBranchReference()).use { replicatedModel ->
modelClient2.getReplicatedModel(repositoryId.getBranchReference()).use { replicatedModel2 ->
val branch1 = replicatedModel.start()
val branch2 = replicatedModel2.start()

val rand = Random(34554)

for (changeId in 1..10) {
println("change set $changeId")
val branchToChange = if (rand.nextBoolean()) {
println("changing branch 1")
branch1
} else {
println("changing branch 2")
branch2
}
branchToChange.runWrite {
val changeGenerator = RandomModelChangeGenerator(branchToChange.getRootNode(), rand)
repeat(1000) { _ ->
changeGenerator.applyRandomChange()
}
println("new tree: " + (branchToChange.transaction.tree as CLTree).hash)
}

val syncTime = measureTime {
for (timeout in 1..1000) {
if (branch1.treeHash() == branch2.treeHash()) break
delay(1.milliseconds)
val syncTime = measureTime {
for (timeout in 1..1000) {
if (branch1.treeHash() == branch2.treeHash()) break
delay(1.milliseconds)
}
}
println("synced after $syncTime")
val data1 = branch1.computeRead {
println("reading on branch 1: " + branch1.treeHash())
branch1.getRootNode().asData()
}
val data2 = branch2.computeRead {
println("reading on branch 2: " + branch2.treeHash())
branch2.getRootNode().asData()
}
assertEquals(data1, data2)
}
}
println("synced after $syncTime")
val data1 = branch1.computeRead {
println("reading on branch 1: " + branch1.treeHash())
branch1.getRootNode().asData()
}
val data2 = branch2.computeRead {
println("reading on branch 2: " + branch2.treeHash())
branch2.getRootNode().asData()
}
assertEquals(data1, data2)
}
}

Expand Down Expand Up @@ -472,18 +474,19 @@ class ReplicatedRepositoryTest {
.also { it.init() }
val repositoryId = RepositoryId("repo1")
modelClient.initRepository(repositoryId)
val replicatedModel = modelClient.getReplicatedModel(repositoryId.getBranchReference())
val branch = replicatedModel.start() as OTBranch
val initialVersion = modelClient.pull(replicatedModel.branchRef, null) as CLVersion
modelClient.getReplicatedModel(repositoryId.getBranchReference()).use { replicatedModel ->
val branch = replicatedModel.start() as OTBranch
val initialVersion = modelClient.pull(replicatedModel.branchRef, null) as CLVersion

branch.computeWriteT {
it.addNewChild(ITree.ROOT_ID, "role", -1, null as IConceptReference?)
}
while (replicatedModel.getCurrentVersion() == initialVersion) {
delay(10)
}
branch.computeWriteT {
it.addNewChild(ITree.ROOT_ID, "role", -1, null as IConceptReference?)
}
while (replicatedModel.getCurrentVersion() == initialVersion) {
delay(10)
}

assertEquals(userId, replicatedModel.getCurrentVersion().author)
assertEquals(userId, replicatedModel.getCurrentVersion().author)
}
}
}

Expand Down

0 comments on commit a34591f

Please sign in to comment.