@ -32,7 +32,10 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
@@ -32,7 +32,10 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder
@ -42,7 +45,11 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
@@ -42,7 +45,11 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@ -59,6 +66,7 @@ class DefaultActiveCallManagerTest {
@@ -59,6 +66,7 @@ class DefaultActiveCallManagerTest {
@Test
fun `registerIncomingCall - sets the incoming call as active` ( ) = runTest {
val notificationManagerCompat = mockk < NotificationManagerCompat > ( relaxed = true )
inCancellableScope {
val manager = createActiveCallManager ( notificationManagerCompat = notificationManagerCompat )
assertThat ( manager . activeCall . value ) . isNull ( )
@ -80,12 +88,14 @@ class DefaultActiveCallManagerTest {
@@ -80,12 +88,14 @@ class DefaultActiveCallManagerTest {
verify { notificationManagerCompat . notify ( notificationId , any ( ) ) }
}
}
@OptIn ( ExperimentalCoroutinesApi :: class )
@Test
fun `registerIncomingCall - when there is an already active call adds missed call notification` ( ) = runTest {
val addMissedCallNotificationLambda = lambdaRecorder < SessionId , RoomId , EventId , Unit > { _ , _ , _ -> }
val onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler ( addMissedCallNotificationLambda = addMissedCallNotificationLambda )
inCancellableScope {
val manager = createActiveCallManager (
onMissedCallNotificationHandler = onMissedCallNotificationHandler ,
)
@ -107,10 +117,12 @@ class DefaultActiveCallManagerTest {
@@ -107,10 +117,12 @@ class DefaultActiveCallManagerTest {
. isCalledOnce ( )
. with ( value ( A _SESSION _ID ) , value ( A _ROOM _ID _2 ) , value ( AN _EVENT _ID ) )
}
}
@Test
fun `incomingCallTimedOut - when there isn't an active call does nothing` ( ) = runTest {
val addMissedCallNotificationLambda = lambdaRecorder < SessionId , RoomId , EventId , Unit > { _ , _ , _ -> }
inCancellableScope {
val manager = createActiveCallManager (
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler ( addMissedCallNotificationLambda = addMissedCallNotificationLambda )
)
@ -119,12 +131,14 @@ class DefaultActiveCallManagerTest {
@@ -119,12 +131,14 @@ class DefaultActiveCallManagerTest {
addMissedCallNotificationLambda . assertions ( ) . isNeverCalled ( )
}
}
@OptIn ( ExperimentalCoroutinesApi :: class )
@Test
fun `incomingCallTimedOut - when there is an active call removes it and adds a missed call notification` ( ) = runTest {
val notificationManagerCompat = mockk < NotificationManagerCompat > ( relaxed = true )
val addMissedCallNotificationLambda = lambdaRecorder < SessionId , RoomId , EventId , Unit > { _ , _ , _ -> }
inCancellableScope {
val manager = createActiveCallManager (
onMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler ( addMissedCallNotificationLambda = addMissedCallNotificationLambda ) ,
notificationManagerCompat = notificationManagerCompat ,
@ -140,10 +154,13 @@ class DefaultActiveCallManagerTest {
@@ -140,10 +154,13 @@ class DefaultActiveCallManagerTest {
addMissedCallNotificationLambda . assertions ( ) . isCalledOnce ( )
verify { notificationManagerCompat . cancel ( notificationId ) }
}
}
@Test
fun `hungUpCall - removes existing call if the CallType matches` ( ) = runTest {
val notificationManagerCompat = mockk < NotificationManagerCompat > ( relaxed = true )
// Create a cancellable coroutine scope to cancel the test when needed
inCancellableScope {
val manager = createActiveCallManager ( notificationManagerCompat = notificationManagerCompat )
val notificationData = aCallNotificationData ( )
@ -155,10 +172,13 @@ class DefaultActiveCallManagerTest {
@@ -155,10 +172,13 @@ class DefaultActiveCallManagerTest {
verify { notificationManagerCompat . cancel ( notificationId ) }
}
}
@Test
fun `hungUpCall - does nothing if the CallType doesn't match` ( ) = runTest {
val notificationManagerCompat = mockk < NotificationManagerCompat > ( relaxed = true )
// Create a cancellable coroutine scope to cancel the test when needed
inCancellableScope {
val manager = createActiveCallManager ( notificationManagerCompat = notificationManagerCompat )
manager . registerIncomingCall ( aCallNotificationData ( ) )
@ -169,14 +189,14 @@ class DefaultActiveCallManagerTest {
@@ -169,14 +189,14 @@ class DefaultActiveCallManagerTest {
verify ( exactly = 0 ) { notificationManagerCompat . cancel ( notificationId ) }
}
}
@OptIn ( ExperimentalCoroutinesApi :: class )
@Test
fun `joinedCall - register an ongoing call and tries sending the call notify event` ( ) = runTest {
val notificationManagerCompat = mockk < NotificationManagerCompat > ( relaxed = true )
val manager = createActiveCallManager (
notificationManagerCompat = notificationManagerCompat ,
)
inCancellableScope {
val manager = createActiveCallManager ( notificationManagerCompat = notificationManagerCompat )
assertThat ( manager . activeCall . value ) . isNull ( )
manager . joinedCall ( CallType . RoomCall ( A _SESSION _ID , A _ROOM _ID ) )
@ -194,13 +214,91 @@ class DefaultActiveCallManagerTest {
@@ -194,13 +214,91 @@ class DefaultActiveCallManagerTest {
verify { notificationManagerCompat . cancel ( notificationId ) }
}
}
@OptIn ( ExperimentalCoroutinesApi :: class )
@Test
fun `observeRingingCalls - will cancel the active ringing call if the call is cancelled` ( ) = runTest {
val room = FakeMatrixRoom ( ) . apply {
givenRoomInfo ( aRoomInfo ( ) )
}
val client = FakeMatrixClient ( ) . apply {
givenGetRoomResult ( A _ROOM _ID , room )
}
// Create a cancellable coroutine scope to cancel the test when needed
inCancellableScope {
val matrixClientProvider = FakeMatrixClientProvider ( getClient = { Result . success ( client ) } )
val manager = createActiveCallManager ( matrixClientProvider = matrixClientProvider )
manager . registerIncomingCall ( aCallNotificationData ( ) )
// Call is active (the other user join the call)
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = true ) )
advanceTimeBy ( 1 )
// Call is cancelled (the other user left the call)
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = false ) )
advanceTimeBy ( 1 )
assertThat ( manager . activeCall . value ) . isNull ( )
}
}
@OptIn ( ExperimentalCoroutinesApi :: class )
@Test
fun `observeRingingCalls - will do nothing if either the session or the room are not found` ( ) = runTest {
val room = FakeMatrixRoom ( ) . apply {
givenRoomInfo ( aRoomInfo ( ) )
}
val client = FakeMatrixClient ( ) . apply {
givenGetRoomResult ( A _ROOM _ID , room )
}
// Create a cancellable coroutine scope to cancel the test when needed
inCancellableScope {
val matrixClientProvider = FakeMatrixClientProvider ( getClient = { Result . failure ( IllegalStateException ( " Matrix client not found " ) ) } )
val manager = createActiveCallManager ( matrixClientProvider = matrixClientProvider )
// No matrix client
manager . registerIncomingCall ( aCallNotificationData ( ) )
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = true ) )
advanceTimeBy ( 1 )
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = false ) )
advanceTimeBy ( 1 )
// The call should still be active
assertThat ( manager . activeCall . value ) . isNotNull ( )
// No room
client . givenGetRoomResult ( A _ROOM _ID , null )
matrixClientProvider . getClient = { Result . success ( client ) }
manager . registerIncomingCall ( aCallNotificationData ( ) )
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = true ) )
advanceTimeBy ( 1 )
room . givenRoomInfo ( aRoomInfo ( hasRoomCall = false ) )
advanceTimeBy ( 1 )
// The call should still be active
assertThat ( manager . activeCall . value ) . isNotNull ( )
}
}
private fun TestScope . inCancellableScope ( block : suspend CoroutineScope . ( ) -> Unit ) {
launch ( SupervisorJob ( ) ) {
block ( )
cancel ( )
}
}
private fun TestScope . createActiveCallManager (
private fun Coroutine Scope. createActiveCallManager (
matrixClientProvider : FakeMatrixClientProvider = FakeMatrixClientProvider ( ) ,
onMissedCallNotificationHandler : FakeOnMissedCallNotificationHandler = FakeOnMissedCallNotificationHandler ( ) ,
notificationManagerCompat : NotificationManagerCompat = mockk ( relaxed = true ) ,
coroutineScope : CoroutineScope = this ,
) = DefaultActiveCallManager (
coroutineScope = this ,
coroutineScope = coroutineScope ,
onMissedCallNotificationHandler = onMissedCallNotificationHandler ,
ringingCallNotificationCreator = RingingCallNotificationCreator (
context = InstrumentationRegistry . getInstrumentation ( ) . targetContext ,
@ -209,5 +307,6 @@ class DefaultActiveCallManagerTest {
@@ -209,5 +307,6 @@ class DefaultActiveCallManagerTest {
notificationBitmapLoader = FakeNotificationBitmapLoader ( ) ,
) ,
notificationManagerCompat = notificationManagerCompat ,
matrixClientProvider = matrixClientProvider ,
)
}