From 18cc65da2c99f277f9d907707d7a6de7e047e398 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 5 Nov 2024 19:00:13 +0530 Subject: [PATCH] Marked atomicCoroutineScope as internal to access from tests, added empty impl for detach and release method, Added test for spec CHA-RL1d --- .../com/ably/chat/RoomLifecycleManager.kt | 24 ++++++- .../com/ably/chat/RoomLifecycleManagerTest.kt | 70 +++++++++++++++++-- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 1f4a987d..adf691e9 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -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). @@ -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") + } } diff --git a/chat-android/src/test/java/com/ably/chat/RoomLifecycleManagerTest.kt b/chat-android/src/test/java/com/ably/chat/RoomLifecycleManagerTest.kt index 1a0300d8..7c07454c 100644 --- a/chat-android/src/test/java/com/ably/chat/RoomLifecycleManagerTest.kt +++ b/chat-android/src/test/java/com/ably/chat/RoomLifecycleManagerTest.kt @@ -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 @@ -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().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().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() @@ -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().apply { setStatus(RoomLifecycle.Released) } @@ -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().apply { + setStatus(RoomLifecycle.Released) + } + val roomLifecycle = spyk(RoomLifecycleManager(roomScope, status, emptyList())) + + val channelReleased = Channel() + 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 { + } }