Skip to content

Commit

Permalink
Marked atomicCoroutineScope as internal to access from tests, added e…
Browse files Browse the repository at this point in the history
…mpty

impl for detach and release method, Added test for spec CHA-RL1d
  • Loading branch information
sacOO7 committed Nov 5, 2024
1 parent 983ec92 commit 18cc65d
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 6 deletions.
24 changes: 23 additions & 1 deletion chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class RoomLifecycleManager
* See [Kotlin Dispatchers](https://kt.academy/article/cc-dispatchers) for more information.
* Spec: CHA-RL7
*/
private val atomicCoroutineScope = AtomicCoroutineScope(roomScope)
internal val atomicCoroutineScope = AtomicCoroutineScope(roomScope)

/**
* This flag indicates whether some sort of controlled operation is in progress (e.g. attaching, detaching, releasing).
Expand Down Expand Up @@ -435,4 +435,26 @@ class RoomLifecycleManager
}
}.awaitAll()
}

/**
* Detaches the room. If the room is already detached, this is a no-op.
* If one of the channels fails to detach, the room status will be set to failed.
* If the room is in the process of detaching, this will wait for the detachment to complete.
* @return when the room is detached.
*/
internal suspend fun detach() {
TODO("Need to impl. room detach")
}

/**
* Releases the room. If the room is already released, this is a no-op.
* Any channel that detaches into the failed state is ok. But any channel that fails to detach
* will cause the room status to be set to failed.
*
* @returns Returns when the room is released. If a channel detaches into a non-terminated
* state (e.g. attached), release will throw exception.
*/
internal suspend fun release() {
TODO("Need to impl. room release")
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.ably.chat

import io.ably.lib.types.AblyException
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.spyk
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert
Expand All @@ -17,21 +23,21 @@ class RoomLifecycleManagerTest {
)

@Test
fun `(CHA-RL1a) Attach return when channel in already in attached state`() = runTest {
fun `(CHA-RL1a) Attach success when channel in already in attached state`() = runTest {
val status = spyk<DefaultStatus>().apply {
setStatus(RoomLifecycle.Attached)
}
val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, listOf()))
val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, emptyList()))
val result = kotlin.runCatching { roomLifecycle.attach() }
Assert.assertTrue(result.isSuccess)
}

@Test
fun `(CHA-RL1b) Attach return when channel in releasing state`() = runTest {
fun `(CHA-RL1b) Attach throws exception when channel in releasing state`() = runTest {
val status = spyk<DefaultStatus>().apply {
setStatus(RoomLifecycle.Releasing)
}
val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, listOf()))
val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, emptyList()))
val exception = Assert.assertThrows(AblyException::class.java) {
runBlocking {
roomLifecycle.attach()
Expand All @@ -43,7 +49,7 @@ class RoomLifecycleManagerTest {
}

@Test
fun `(CHA-RL1c) Attach return when channel in released state`() = runTest {
fun `(CHA-RL1c) Attach throws exception when channel in released state`() = runTest {
val status = spyk<DefaultStatus>().apply {
setStatus(RoomLifecycle.Released)
}
Expand All @@ -57,4 +63,58 @@ class RoomLifecycleManagerTest {
Assert.assertEquals(102_103, exception.errorInfo.code)
Assert.assertEquals(500, exception.errorInfo.statusCode)
}

@Test
fun `(CHA-RL1d) Attach op should wait for existing operation as per (CHA-RL7)`() = runTest {
val status = spyk<DefaultStatus>().apply {
setStatus(RoomLifecycle.Released)
}
val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, emptyList()))

val channelReleased = Channel<Unit>()
coEvery {
roomLifecycle.release()
} coAnswers {
roomLifecycle.atomicCoroutineScope.async {
status.setStatus(RoomLifecycle.Releasing)
channelReleased.receive()
status.setStatus(RoomLifecycle.Released)
}
}
launch { roomLifecycle.release() }

// Release op started
assertWaiter { !roomLifecycle.atomicCoroutineScope.finishedProcessing }
assertWaiter { status.current == RoomLifecycle.Releasing }

val roomAttachOpDeferred = async(SupervisorJob()) { roomLifecycle.attach() }
Assert.assertEquals(RoomLifecycle.Releasing, status.current)
channelReleased.send(Unit)

// Release op finished
assertWaiter { roomLifecycle.atomicCoroutineScope.finishedProcessing }
assertWaiter { status.current == RoomLifecycle.Released }

val result = kotlin.runCatching { roomAttachOpDeferred.await() }
Assert.assertTrue(result.isFailure)
val exception = result.exceptionOrNull() as AblyException

Assert.assertEquals("unable to attach room; room is released", exception.errorInfo.message)
Assert.assertEquals(102_103, exception.errorInfo.code)
Assert.assertEquals(500, exception.errorInfo.statusCode)

coVerify { roomLifecycle.release() }
}

@Test
fun `(CHA-RL1e) Attach op should transition room into ATTACHING state`() = runTest {
}

@Test
fun `(CHA-RL1f) Attach op should attach each contributor channel sequentially`() = runTest {
}

@Test
fun `(CHA-RL1g) When all contributor channels ATTACH, op is complete and room should be considered ATTACHED`() = runTest {
}
}

0 comments on commit 18cc65d

Please sign in to comment.