repo created
This commit is contained in:
commit
93184d21d1
1403 changed files with 189511 additions and 0 deletions
|
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call;
|
||||
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.signaling.SignalingMessageReceiver;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.DISCONNECTED;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.IN_CALL;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.WITH_AUDIO;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.WITH_VIDEO;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.ParticipantType.GUEST;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.ParticipantType.GUEST_MODERATOR;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.ParticipantType.MODERATOR;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.ParticipantType.OWNER;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.ParticipantType.USER;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
public class CallParticipantListExternalSignalingTest {
|
||||
|
||||
private final ParticipantsUpdateParticipantBuilder builder = new ParticipantsUpdateParticipantBuilder();
|
||||
|
||||
private CallParticipantList callParticipantList;
|
||||
private SignalingMessageReceiver.ParticipantListMessageListener participantListMessageListener;
|
||||
|
||||
private CallParticipantList.Observer mockedCallParticipantListObserver;
|
||||
|
||||
private Collection<Participant> expectedJoined;
|
||||
private Collection<Participant> expectedUpdated;
|
||||
private Collection<Participant> expectedLeft;
|
||||
private Collection<Participant> expectedUnchanged;
|
||||
|
||||
// The order of the left participants in some tests depends on how they are internally sorted by the map, so the
|
||||
// list of left participants needs to be checked ignoring the sorting (or, rather, sorting by session ID as in
|
||||
// expectedLeft).
|
||||
// Other tests can just relay on the not guaranteed, but known internal sorting of the elements.
|
||||
private final ArgumentMatcher<List<Participant>> matchesExpectedLeftIgnoringOrder = left -> {
|
||||
Collections.sort(left, Comparator.comparing(Participant::getSessionId));
|
||||
return expectedLeft.equals(left);
|
||||
};
|
||||
|
||||
private static class ParticipantsUpdateParticipantBuilder {
|
||||
private Participant newUser(long inCall, long lastPing, String sessionId, Participant.ParticipantType type,
|
||||
String userId) {
|
||||
Participant participant = new Participant();
|
||||
participant.setInCall(inCall);
|
||||
participant.setLastPing(lastPing);
|
||||
participant.setSessionId(sessionId);
|
||||
participant.setType(type);
|
||||
participant.setUserId(userId);
|
||||
participant.setActorType(Participant.ActorType.USERS);
|
||||
participant.setActorId(userId);
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
private Participant newGuest(long inCall, long lastPing, String sessionId, Participant.ParticipantType type) {
|
||||
Participant participant = new Participant();
|
||||
participant.setInCall(inCall);
|
||||
participant.setLastPing(lastPing);
|
||||
participant.setSessionId(sessionId);
|
||||
participant.setType(type);
|
||||
participant.setActorType(Participant.ActorType.GUESTS);
|
||||
participant.setActorId("sha1-" + sessionId);
|
||||
|
||||
return participant;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SignalingMessageReceiver mockedSignalingMessageReceiver = mock(SignalingMessageReceiver.class);
|
||||
|
||||
callParticipantList = new CallParticipantList(mockedSignalingMessageReceiver);
|
||||
|
||||
mockedCallParticipantListObserver = mock(CallParticipantList.Observer.class);
|
||||
|
||||
// Get internal ParticipantListMessageListener from callParticipantList set in the
|
||||
// mockedSignalingMessageReceiver.
|
||||
ArgumentCaptor<SignalingMessageReceiver.ParticipantListMessageListener> participantListMessageListenerArgumentCaptor =
|
||||
ArgumentCaptor.forClass(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
verify(mockedSignalingMessageReceiver).addListener(participantListMessageListenerArgumentCaptor.capture());
|
||||
|
||||
participantListMessageListener = participantListMessageListenerArgumentCaptor.getValue();
|
||||
|
||||
expectedJoined = new ArrayList<>();
|
||||
expectedUpdated = new ArrayList<>();
|
||||
expectedLeft = new ArrayList<>();
|
||||
expectedUnchanged = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 4, "theSessionId4", USER, "theUserId4"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 5, "theSessionId5", OWNER, "theUserId5"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomThenJoinCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomThenJoinCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedJoined.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomAndCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomAndCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedJoined.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateJoinRoomAndCallRepeated() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeCallFlags() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeCallFlagsSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_VIDEO, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedUpdated.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_VIDEO, 4, "theSessionId4", USER, "theUserId4"));
|
||||
expectedUnchanged.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2", GUEST));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeLastPing() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeLastPingSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 3, "theSessionId3", USER, "theUserId3"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 108, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 815, "theSessionId3", USER, "theUserId3"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeParticipantType() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", USER, "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateChangeParticipantTypeeSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 3, "theSessionId3", USER, "theUserId3"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", USER, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 2, "theSessionId2", GUEST_MODERATOR));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 3, "theSessionId3", MODERATOR, "theUserId3"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCallThenLeaveRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCallThenLeaveRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCallAndRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateLeaveCallAndRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver).onCallParticipantsChanged(eq(expectedJoined), eq(expectedUpdated),
|
||||
argThat(matchesExpectedLeftIgnoringOrder), eq(expectedUnchanged));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParticipantsUpdateSeveralEventsSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
participants.add(builder.newUser(IN_CALL, 5, "theSessionId5", OWNER, "theUserId5"));
|
||||
// theSessionId6 has not joined yet.
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_VIDEO, 7, "theSessionId7", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 8, "theSessionId8", USER, "theUserId8"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 9, "theSessionId9", MODERATOR, "theUserId9"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
// theSessionId1 is gone.
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 5, "theSessionId5", OWNER, "theUserId5"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 6, "theSessionId6", GUEST));
|
||||
participants.add(builder.newGuest(IN_CALL, 7, "theSessionId7", GUEST));
|
||||
participants.add(builder.newUser(IN_CALL, 8, "theSessionId8", USER, "theUserId8"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId9", USER, "theUserId9"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newGuest(IN_CALL | WITH_AUDIO, 6, "theSessionId6", GUEST));
|
||||
expectedJoined.add(builder.newUser(IN_CALL, 8, "theSessionId8", USER, "theUserId8"));
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 5, "theSessionId5", OWNER, "theUserId5"));
|
||||
expectedUpdated.add(builder.newGuest(IN_CALL, 7, "theSessionId7", GUEST));
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2", GUEST));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", USER, "theUserId4"));
|
||||
// Last ping and participant type are not seen as changed, even if they did.
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId9", USER, "theUserId9"));
|
||||
|
||||
verify(mockedCallParticipantListObserver).onCallParticipantsChanged(eq(expectedJoined), eq(expectedUpdated),
|
||||
argThat(matchesExpectedLeftIgnoringOrder), eq(expectedUnchanged));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllParticipantsUpdateDisconnected() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onAllParticipantsUpdate(DISCONNECTED);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
InOrder inOrder = inOrder(mockedCallParticipantListObserver);
|
||||
|
||||
inOrder.verify(mockedCallParticipantListObserver).onCallEndedForAll();
|
||||
inOrder.verify(mockedCallParticipantListObserver).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllParticipantsUpdateDisconnectedWithSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 2, "theSessionId2", USER, "theUserId2"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 3, "theSessionId3", USER, "theUserId3"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 4, "theSessionId4", GUEST));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onAllParticipantsUpdate(DISCONNECTED);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", USER, "theUserId3"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 4, "theSessionId4", GUEST));
|
||||
|
||||
InOrder inOrder = inOrder(mockedCallParticipantListObserver);
|
||||
|
||||
inOrder.verify(mockedCallParticipantListObserver).onCallEndedForAll();
|
||||
inOrder.verify(mockedCallParticipantListObserver).onCallParticipantsChanged(eq(expectedJoined), eq(expectedUpdated),
|
||||
argThat(matchesExpectedLeftIgnoringOrder), eq(expectedUnchanged));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllParticipantsUpdateDisconnectedNoOneInCall() {
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onAllParticipantsUpdate(DISCONNECTED);
|
||||
|
||||
InOrder inOrder = inOrder(mockedCallParticipantListObserver);
|
||||
|
||||
inOrder.verify(mockedCallParticipantListObserver).onCallEndedForAll();
|
||||
verifyNoMoreInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllParticipantsUpdateDisconnectedThenJoinCallAgain() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
participantListMessageListener.onAllParticipantsUpdate(DISCONNECTED);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
participantListMessageListener.onParticipantsUpdate(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL, 1, "theSessionId1", MODERATOR, "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call;
|
||||
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.signaling.SignalingMessageReceiver;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.DISCONNECTED;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.IN_CALL;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.WITH_AUDIO;
|
||||
import static com.nextcloud.talk.models.json.participants.Participant.InCallFlags.WITH_VIDEO;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class CallParticipantListInternalSignalingTest {
|
||||
|
||||
private final UsersInRoomParticipantBuilder builder = new UsersInRoomParticipantBuilder();
|
||||
|
||||
private CallParticipantList callParticipantList;
|
||||
private SignalingMessageReceiver.ParticipantListMessageListener participantListMessageListener;
|
||||
|
||||
private CallParticipantList.Observer mockedCallParticipantListObserver;
|
||||
|
||||
private Collection<Participant> expectedJoined;
|
||||
private Collection<Participant> expectedUpdated;
|
||||
private Collection<Participant> expectedLeft;
|
||||
private Collection<Participant> expectedUnchanged;
|
||||
|
||||
// The order of the left participants in some tests depends on how they are internally sorted by the map, so the
|
||||
// list of left participants needs to be checked ignoring the sorting (or, rather, sorting by session ID as in
|
||||
// expectedLeft).
|
||||
// Other tests can just relay on the not guaranteed, but known internal sorting of the elements.
|
||||
private final ArgumentMatcher<List<Participant>> matchesExpectedLeftIgnoringOrder = left -> {
|
||||
Collections.sort(left, Comparator.comparing(Participant::getSessionId));
|
||||
return expectedLeft.equals(left);
|
||||
};
|
||||
|
||||
private static class UsersInRoomParticipantBuilder {
|
||||
private Participant newUser(long inCall, long lastPing, String sessionId, String userId) {
|
||||
Participant participant = new Participant();
|
||||
participant.setInCall(inCall);
|
||||
participant.setLastPing(lastPing);
|
||||
participant.setSessionId(sessionId);
|
||||
participant.setUserId(userId);
|
||||
participant.setActorType(Participant.ActorType.USERS);
|
||||
participant.setActorId(userId);
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
private Participant newGuest(long inCall, long lastPing, String sessionId) {
|
||||
Participant participant = new Participant();
|
||||
participant.setInCall(inCall);
|
||||
participant.setLastPing(lastPing);
|
||||
participant.setSessionId(sessionId);
|
||||
participant.setActorType(Participant.ActorType.GUESTS);
|
||||
participant.setActorId("sha1-" + sessionId);
|
||||
|
||||
return participant;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SignalingMessageReceiver mockedSignalingMessageReceiver = mock(SignalingMessageReceiver.class);
|
||||
|
||||
callParticipantList = new CallParticipantList(mockedSignalingMessageReceiver);
|
||||
|
||||
mockedCallParticipantListObserver = mock(CallParticipantList.Observer.class);
|
||||
|
||||
// Get internal ParticipantListMessageListener from callParticipantList set in the
|
||||
// mockedSignalingMessageReceiver.
|
||||
ArgumentCaptor<SignalingMessageReceiver.ParticipantListMessageListener> participantListMessageListenerArgumentCaptor =
|
||||
ArgumentCaptor.forClass(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
verify(mockedSignalingMessageReceiver).addListener(participantListMessageListenerArgumentCaptor.capture());
|
||||
|
||||
participantListMessageListener = participantListMessageListenerArgumentCaptor.getValue();
|
||||
|
||||
expectedJoined = new ArrayList<>();
|
||||
expectedUpdated = new ArrayList<>();
|
||||
expectedLeft = new ArrayList<>();
|
||||
expectedUnchanged = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 4, "theSessionId4", "theUserId4"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 5, "theSessionId5", "theUserId5"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomThenJoinCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomThenJoinCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
expectedJoined.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomAndCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomAndCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
expectedJoined.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomJoinRoomAndCallRepeated() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomChangeCallFlags() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomChangeCallFlagsSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_VIDEO, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedUpdated.add(builder.newUser(IN_CALL, 1, "theSessionId1", "theUserId1"));
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_VIDEO, 4, "theSessionId4", "theUserId4"));
|
||||
expectedUnchanged.add(builder.newGuest(IN_CALL | WITH_AUDIO | WITH_VIDEO, 2, "theSessionId2"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomChangeLastPing() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId1", "theUserId1"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomChangeLastPingSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 3, "theSessionId3", "theUserId3"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 108, "theSessionId2"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 815, "theSessionId3", "theUserId3"));
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCall() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCallSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCallThenLeaveRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCallThenLeaveRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantListObserver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCallAndRoom() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
|
||||
verify(mockedCallParticipantListObserver, only()).onCallParticipantsChanged(expectedJoined, expectedUpdated,
|
||||
expectedLeft, expectedUnchanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomLeaveCallAndRoomSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
|
||||
verify(mockedCallParticipantListObserver).onCallParticipantsChanged(eq(expectedJoined), eq(expectedUpdated),
|
||||
argThat(matchesExpectedLeftIgnoringOrder), eq(expectedUnchanged));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersInRoomSeveralEventsSeveralParticipants() {
|
||||
List<Participant> participants = new ArrayList<>();
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 1, "theSessionId1", "theUserId1"));
|
||||
participants.add(builder.newGuest(IN_CALL, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
participants.add(builder.newUser(IN_CALL, 5, "theSessionId5", "theUserId5"));
|
||||
// theSessionId6 has not joined yet.
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_VIDEO, 7, "theSessionId7"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 8, "theSessionId8", "theUserId8"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 9, "theSessionId9", "theUserId9"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
callParticipantList.addObserver(mockedCallParticipantListObserver);
|
||||
|
||||
participants = new ArrayList<>();
|
||||
// theSessionId1 is gone.
|
||||
participants.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
participants.add(builder.newUser(DISCONNECTED, 3, "theSessionId3", "theUserId3"));
|
||||
participants.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 5, "theSessionId5", "theUserId5"));
|
||||
participants.add(builder.newGuest(IN_CALL | WITH_AUDIO, 6, "theSessionId6"));
|
||||
participants.add(builder.newGuest(IN_CALL, 7, "theSessionId7"));
|
||||
participants.add(builder.newUser(IN_CALL, 8, "theSessionId8", "theUserId8"));
|
||||
participants.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId9", "theUserId9"));
|
||||
|
||||
participantListMessageListener.onUsersInRoom(participants);
|
||||
|
||||
expectedJoined.add(builder.newGuest(IN_CALL | WITH_AUDIO, 6, "theSessionId6"));
|
||||
expectedJoined.add(builder.newUser(IN_CALL, 8, "theSessionId8", "theUserId8"));
|
||||
expectedUpdated.add(builder.newUser(IN_CALL | WITH_AUDIO | WITH_VIDEO, 5, "theSessionId5", "theUserId5"));
|
||||
expectedUpdated.add(builder.newGuest(IN_CALL, 7, "theSessionId7"));
|
||||
expectedLeft.add(builder.newUser(DISCONNECTED, 1, "theSessionId1", "theUserId1"));
|
||||
expectedLeft.add(builder.newGuest(DISCONNECTED, 2, "theSessionId2"));
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL, 4, "theSessionId4", "theUserId4"));
|
||||
// Last ping is not seen as changed, even if it did.
|
||||
expectedUnchanged.add(builder.newUser(IN_CALL | WITH_AUDIO, 42, "theSessionId9", "theUserId9"));
|
||||
|
||||
verify(mockedCallParticipantListObserver).onCallParticipantsChanged(eq(expectedJoined),
|
||||
eq(expectedUpdated),
|
||||
argThat(matchesExpectedLeftIgnoringOrder),
|
||||
eq(expectedUnchanged));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call;
|
||||
|
||||
import com.nextcloud.talk.signaling.SignalingMessageReceiver;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
public class CallParticipantListTest {
|
||||
|
||||
private SignalingMessageReceiver mockedSignalingMessageReceiver;
|
||||
|
||||
private CallParticipantList callParticipantList;
|
||||
private SignalingMessageReceiver.ParticipantListMessageListener participantListMessageListener;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mockedSignalingMessageReceiver = mock(SignalingMessageReceiver.class);
|
||||
|
||||
callParticipantList = new CallParticipantList(mockedSignalingMessageReceiver);
|
||||
|
||||
// Get internal ParticipantListMessageListener from callParticipantList set in the
|
||||
// mockedSignalingMessageReceiver.
|
||||
ArgumentCaptor<SignalingMessageReceiver.ParticipantListMessageListener> participantListMessageListenerArgumentCaptor =
|
||||
ArgumentCaptor.forClass(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
verify(mockedSignalingMessageReceiver).addListener(participantListMessageListenerArgumentCaptor.capture());
|
||||
|
||||
participantListMessageListener = participantListMessageListenerArgumentCaptor.getValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroy() {
|
||||
callParticipantList.destroy();
|
||||
|
||||
verify(mockedSignalingMessageReceiver).removeListener(participantListMessageListener);
|
||||
verifyNoMoreInteractions(mockedSignalingMessageReceiver);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
|
||||
class CallParticipantModelTest {
|
||||
private var callParticipantModel: MutableCallParticipantModel? = null
|
||||
private var mockedCallParticipantModelObserver: CallParticipantModel.Observer? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
mockedCallParticipantModelObserver = Mockito.mock(CallParticipantModel.Observer::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetRaisedHand() {
|
||||
callParticipantModel!!.addObserver(mockedCallParticipantModelObserver)
|
||||
callParticipantModel!!.setRaisedHand(true, 4815162342L)
|
||||
Mockito.verify(mockedCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetRaisedHandTwice() {
|
||||
callParticipantModel!!.addObserver(mockedCallParticipantModelObserver)
|
||||
callParticipantModel!!.setRaisedHand(true, 4815162342L)
|
||||
callParticipantModel!!.setRaisedHand(false, 4815162342108L)
|
||||
Mockito.verify(mockedCallParticipantModelObserver, Mockito.times(2))?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetRaisedHandTwiceWithSameValue() {
|
||||
callParticipantModel!!.addObserver(mockedCallParticipantModelObserver)
|
||||
callParticipantModel!!.setRaisedHand(true, 4815162342L)
|
||||
callParticipantModel!!.setRaisedHand(true, 4815162342L)
|
||||
Mockito.verify(mockedCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmitReaction() {
|
||||
callParticipantModel!!.addObserver(mockedCallParticipantModelObserver)
|
||||
callParticipantModel!!.emitReaction("theReaction")
|
||||
Mockito.verify(mockedCallParticipantModelObserver, Mockito.only())?.onReaction("theReaction")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
|
||||
class LocalCallParticipantModelTest {
|
||||
private var localCallParticipantModel: MutableLocalCallParticipantModel? = null
|
||||
private var mockedLocalCallParticipantModelObserver: LocalCallParticipantModel.Observer? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
localCallParticipantModel = MutableLocalCallParticipantModel()
|
||||
mockedLocalCallParticipantModelObserver = Mockito.mock(LocalCallParticipantModel.Observer::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioEnabled() {
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
assertTrue(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioEnabledWhileSpeakingWhileMuted() {
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
assertTrue(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertTrue(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.times(3))?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioEnabledTwiceWhileSpeakingWhileMuted() {
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
assertTrue(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertTrue(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.times(3))?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioDisabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
assertFalse(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioDisabledWhileSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
assertFalse(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertTrue(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.times(3))?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAudioDisabledTwiceWhileSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
assertFalse(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertTrue(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.times(3))?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetSpeakingWhileAudioEnabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
assertTrue(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertTrue(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetNotSpeakingWhileAudioEnabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
assertTrue(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetSpeakingWhileAudioDisabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
assertFalse(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertTrue(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetNotSpeakingWhileAudioDisabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localCallParticipantModel!!.addObserver(mockedLocalCallParticipantModelObserver)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
assertFalse(localCallParticipantModel!!.isAudioEnabled)
|
||||
assertFalse(localCallParticipantModel!!.isSpeaking)
|
||||
assertFalse(localCallParticipantModel!!.isSpeakingWhileMuted)
|
||||
Mockito.verify(mockedLocalCallParticipantModelObserver, Mockito.only())?.onChange()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,641 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import io.reactivex.schedulers.TestScheduler
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.times
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Suppress("LongMethod")
|
||||
class LocalStateBroadcasterMcuTest {
|
||||
|
||||
private var localCallParticipantModel: MutableLocalCallParticipantModel? = null
|
||||
private var mockedMessageSender: MessageSender? = null
|
||||
|
||||
private var localStateBroadcasterMcu: LocalStateBroadcasterMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
localCallParticipantModel = MutableLocalCallParticipantModel()
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
mockedMessageSender = Mockito.mock(MessageSender::class.java)
|
||||
}
|
||||
|
||||
private fun getExpectedUnmuteAudio(): NCSignalingMessage {
|
||||
val expectedUnmuteAudio = NCSignalingMessage()
|
||||
expectedUnmuteAudio.roomType = "video"
|
||||
expectedUnmuteAudio.type = "unmute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedUnmuteAudio.payload = payload
|
||||
|
||||
return expectedUnmuteAudio
|
||||
}
|
||||
|
||||
private fun getExpectedMuteAudio(): NCSignalingMessage {
|
||||
val expectedMuteAudio = NCSignalingMessage()
|
||||
expectedMuteAudio.roomType = "video"
|
||||
expectedMuteAudio.type = "mute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedMuteAudio.payload = payload
|
||||
|
||||
return expectedMuteAudio
|
||||
}
|
||||
|
||||
private fun getExpectedUnmuteVideo(): NCSignalingMessage {
|
||||
val expectedUnmuteVideo = NCSignalingMessage()
|
||||
expectedUnmuteVideo.roomType = "video"
|
||||
expectedUnmuteVideo.type = "unmute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "video"
|
||||
expectedUnmuteVideo.payload = payload
|
||||
|
||||
return expectedUnmuteVideo
|
||||
}
|
||||
|
||||
private fun getExpectedMuteVideo(): NCSignalingMessage {
|
||||
val expectedMuteVideo = NCSignalingMessage()
|
||||
expectedMuteVideo.roomType = "video"
|
||||
expectedMuteVideo.type = "mute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "video"
|
||||
expectedMuteVideo.payload = payload
|
||||
|
||||
return expectedMuteVideo
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWithExponentialBackoffWhenParticipantAdded() {
|
||||
val testScheduler = TestScheduler()
|
||||
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||
|
||||
localStateBroadcasterMcu = LocalStateBroadcasterMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSender
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
// Sending will be done in another thread, so just adding the participant does not send anything until that
|
||||
// other thread could run.
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
var messageCount = 1
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 2
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 3
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 4
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(8, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 5
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(16, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 6
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(100, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWithExponentialBackoffIsTheCurrentState() {
|
||||
// This test could have been included in "testStateSentWithExponentialBackoffWhenParticipantAdded", but was
|
||||
// kept separate for clarity.
|
||||
|
||||
val testScheduler = TestScheduler()
|
||||
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||
|
||||
localStateBroadcasterMcu = LocalStateBroadcasterMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSender
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
// Sending will be done in another thread, so just adding the participant does not send anything until that
|
||||
// other thread could run.
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
val expectedStoppedSpeaking = DataChannelMessage("stoppedSpeaking")
|
||||
|
||||
// Changing the state causes the normal state update to be sent, independently of the initial state
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedStoppedSpeaking)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).sendToAll(expectedStoppedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
val expectedAudioOff = DataChannelMessage("audioOff")
|
||||
val expectedMuteAudio = getExpectedMuteAudio()
|
||||
|
||||
// Changing the state causes the normal state update to be sent, independently of the initial state
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteAudio)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(3)).sendToAll(expectedStoppedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(3)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteAudio)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).send(expectedMuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(3)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
val expectedVideoOff = DataChannelMessage("videoOff")
|
||||
val expectedMuteVideo = getExpectedMuteVideo()
|
||||
|
||||
// Changing the state causes the normal state update to be sent, independently of the initial state
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedVideoOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteVideo)
|
||||
|
||||
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verify(mockedMessageSender!!, times(3)).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(4)).sendToAll(expectedStoppedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).sendToAll(expectedVideoOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteAudio)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteVideo)
|
||||
Mockito.verify(mockedMessageSender!!, times(2)).send(expectedMuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).send(expectedMuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
// Changing the state causes the normal state update to be sent, independently of the initial state
|
||||
Mockito.verify(mockedMessageSender!!, times(4)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedUnmuteVideo)
|
||||
|
||||
testScheduler.advanceTimeBy(8, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verify(mockedMessageSender!!, times(4)).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!, times(5)).sendToAll(expectedStoppedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(5)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteAudio)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedMuteVideo)
|
||||
Mockito.verify(mockedMessageSender!!, times(1)).sendToAll(expectedUnmuteVideo)
|
||||
Mockito.verify(mockedMessageSender!!, times(3)).send(expectedMuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(4)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWithExponentialBackoffWhenAnotherParticipantAdded() {
|
||||
// The state sent through data channels should be restarted, although the state sent through signaling
|
||||
// messages should be independent for each participant.
|
||||
|
||||
val testScheduler = TestScheduler()
|
||||
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||
|
||||
localStateBroadcasterMcu = LocalStateBroadcasterMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSender
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
// Sending will be done in another thread, so just adding the participant does not send anything until that
|
||||
// other thread could run.
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
var dataChannelMessageCount = 1
|
||||
var signalingMessageCount1 = 1
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 2
|
||||
signalingMessageCount1 = 2
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 3
|
||||
signalingMessageCount1 = 3
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 4
|
||||
signalingMessageCount1 = 4
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
val callParticipantModel2 = MutableCallParticipantModel("theSessionId2")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel2)
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 5
|
||||
var signalingMessageCount2 = 1
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 6
|
||||
signalingMessageCount2 = 2
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 7
|
||||
signalingMessageCount2 = 3
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 8
|
||||
signalingMessageCount2 = 4
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
// 0+1+2+4+1=8 seconds since last signaling messages for participant 1
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
signalingMessageCount1 = 5
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
// 1+7=8 seconds since last data channel messages and signaling messages for participant 2
|
||||
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 9
|
||||
signalingMessageCount2 = 5
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
// 7+9=16 seconds since last signaling messages for participant 1
|
||||
testScheduler.advanceTimeBy(9, TimeUnit.SECONDS)
|
||||
|
||||
signalingMessageCount1 = 6
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
// 9+7=16 seconds since last data channel messages and signaling messages for participant 2
|
||||
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 10
|
||||
signalingMessageCount2 = 6
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount1)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount2)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(100, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWithExponentialBackoffWhenParticipantRemoved() {
|
||||
// For simplicity the exponential backoff is not aborted when the participant that triggered it is removed.
|
||||
// However, the signaling messages are stopped when the participant is removed.
|
||||
|
||||
val testScheduler = TestScheduler()
|
||||
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||
|
||||
localStateBroadcasterMcu = LocalStateBroadcasterMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSender
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
// Sending will be done in another thread, so just adding the participant does not send anything until that
|
||||
// other thread could run.
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
var dataChannelMessageCount = 1
|
||||
var signalingMessageCount = 1
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 2
|
||||
signalingMessageCount = 2
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 3
|
||||
signalingMessageCount = 3
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 4
|
||||
signalingMessageCount = 4
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantRemoved(callParticipantModel)
|
||||
|
||||
testScheduler.advanceTimeBy(8, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 5
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(16, TimeUnit.SECONDS)
|
||||
|
||||
dataChannelMessageCount = 6
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(dataChannelMessageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(signalingMessageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(100, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNoLongerSentOnceDestroyed() {
|
||||
val testScheduler = TestScheduler()
|
||||
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||
|
||||
localStateBroadcasterMcu = LocalStateBroadcasterMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSender
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
val callParticipantModel2 = MutableCallParticipantModel("theSessionId2")
|
||||
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
localStateBroadcasterMcu!!.handleCallParticipantAdded(callParticipantModel2)
|
||||
|
||||
// Sending will be done in another thread, so just adding the participant does not send anything until that
|
||||
// other thread could run.
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
testScheduler.advanceTimeBy(0, TimeUnit.SECONDS)
|
||||
|
||||
var messageCount = 1
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(1, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 2
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
|
||||
|
||||
messageCount = 3
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSender!!, times(messageCount)).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
|
||||
localStateBroadcasterMcu!!.destroy()
|
||||
|
||||
testScheduler.advanceTimeBy(100, TimeUnit.SECONDS)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.webrtc.PeerConnection
|
||||
|
||||
class LocalStateBroadcasterNoMcuTest {
|
||||
|
||||
private var localCallParticipantModel: MutableLocalCallParticipantModel? = null
|
||||
private var mockedMessageSenderNoMcu: MessageSenderNoMcu? = null
|
||||
|
||||
private var localStateBroadcasterNoMcu: LocalStateBroadcasterNoMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
localCallParticipantModel = MutableLocalCallParticipantModel()
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
mockedMessageSenderNoMcu = Mockito.mock(MessageSenderNoMcu::class.java)
|
||||
}
|
||||
|
||||
private fun getExpectedUnmuteAudio(): NCSignalingMessage {
|
||||
val expectedUnmuteAudio = NCSignalingMessage()
|
||||
expectedUnmuteAudio.roomType = "video"
|
||||
expectedUnmuteAudio.type = "unmute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedUnmuteAudio.payload = payload
|
||||
|
||||
return expectedUnmuteAudio
|
||||
}
|
||||
|
||||
private fun getExpectedUnmuteVideo(): NCSignalingMessage {
|
||||
val expectedUnmuteVideo = NCSignalingMessage()
|
||||
expectedUnmuteVideo.roomType = "video"
|
||||
expectedUnmuteVideo.type = "unmute"
|
||||
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "video"
|
||||
expectedUnmuteVideo.payload = payload
|
||||
|
||||
return expectedUnmuteVideo
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWhenIceConnected() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateSentWhenIceCompleted() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.COMPLETED)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceCompletedAfterConnected() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.COMPLETED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceConnectedAgain() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.COMPLETED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
// Completed -> Connected could happen with an ICE restart
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.DISCONNECTED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
// Failed -> Checking could happen with an ICE restart
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.FAILED)
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentToOtherParticipantsWhenIceConnected() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
val callParticipantModel2 = MutableCallParticipantModel("theSessionId2")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel2)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
callParticipantModel2.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteAudio = getExpectedUnmuteAudio()
|
||||
val expectedUnmuteVideo = getExpectedUnmuteVideo()
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
callParticipantModel2.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedAudioOn, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedSpeaking, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedVideoOn, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteAudio, "theSessionId2")
|
||||
Mockito.verify(mockedMessageSenderNoMcu!!).send(expectedUnmuteVideo, "theSessionId2")
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceConnectedAfterParticipantIsRemoved() {
|
||||
// This should not happen, as peer connections are expected to be ended when a call participant is removed, but
|
||||
// just in case.
|
||||
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantRemoved(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceCompletedAfterParticipantIsRemoved() {
|
||||
// This should not happen, as peer connections are expected to be ended when a call participant is removed, but
|
||||
// just in case.
|
||||
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantRemoved(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.COMPLETED)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceConnectedAfterDestroyed() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
val callParticipantModel2 = MutableCallParticipantModel("theSessionId2")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel2)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
callParticipantModel2.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
localStateBroadcasterNoMcu!!.destroy()
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
callParticipantModel2.setIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStateNotSentWhenIceCompletedAfterDestroyed() {
|
||||
localStateBroadcasterNoMcu = LocalStateBroadcasterNoMcu(
|
||||
localCallParticipantModel,
|
||||
mockedMessageSenderNoMcu
|
||||
)
|
||||
|
||||
val callParticipantModel = MutableCallParticipantModel("theSessionId")
|
||||
|
||||
localStateBroadcasterNoMcu!!.handleCallParticipantAdded(callParticipantModel)
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.CHECKING)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
|
||||
localStateBroadcasterNoMcu!!.destroy()
|
||||
|
||||
callParticipantModel.setIceConnectionState(PeerConnection.IceConnectionState.COMPLETED)
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSenderNoMcu)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class LocalStateBroadcasterTest {
|
||||
|
||||
private class LocalStateBroadcaster(
|
||||
localCallParticipantModel: LocalCallParticipantModel?,
|
||||
messageSender: MessageSender?
|
||||
) : com.nextcloud.talk.call.LocalStateBroadcaster(localCallParticipantModel, messageSender) {
|
||||
|
||||
override fun handleCallParticipantAdded(callParticipantModel: CallParticipantModel) {
|
||||
// Not used in base class tests
|
||||
}
|
||||
|
||||
override fun handleCallParticipantRemoved(callParticipantModel: CallParticipantModel) {
|
||||
// Not used in base class tests
|
||||
}
|
||||
}
|
||||
|
||||
private var localCallParticipantModel: MutableLocalCallParticipantModel? = null
|
||||
private var mockedMessageSender: MessageSender? = null
|
||||
|
||||
private var localStateBroadcaster: LocalStateBroadcaster? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
localCallParticipantModel = MutableLocalCallParticipantModel()
|
||||
mockedMessageSender = Mockito.mock(MessageSender::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableAudio() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
|
||||
val expectedUnmuteAudio = NCSignalingMessage()
|
||||
expectedUnmuteAudio.roomType = "video"
|
||||
expectedUnmuteAudio.type = "unmute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedUnmuteAudio.payload = payload
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedAudioOn)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedUnmuteAudio)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableAudioTwice() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableAudio() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
val expectedAudioOff = DataChannelMessage("audioOff")
|
||||
|
||||
val expectedMuteAudio = NCSignalingMessage()
|
||||
expectedMuteAudio.roomType = "video"
|
||||
expectedMuteAudio.type = "mute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedMuteAudio.payload = payload
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedMuteAudio)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableAudioTwice() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedSpeaking)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableSpeakingTwice() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableSpeakingWithAudioDisabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableAudioWhileSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
|
||||
val expectedAudioOn = DataChannelMessage("audioOn")
|
||||
val expectedSpeaking = DataChannelMessage("speaking")
|
||||
|
||||
val expectedUnmuteAudio = NCSignalingMessage()
|
||||
expectedUnmuteAudio.roomType = "video"
|
||||
expectedUnmuteAudio.type = "unmute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedUnmuteAudio.payload = payload
|
||||
|
||||
val inOrder = Mockito.inOrder(mockedMessageSender)
|
||||
|
||||
inOrder.verify(mockedMessageSender!!).sendToAll(expectedAudioOn)
|
||||
inOrder.verify(mockedMessageSender!!).sendToAll(expectedSpeaking)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedUnmuteAudio)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
val expectedStoppedSpeaking = DataChannelMessage("stoppedSpeaking")
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedStoppedSpeaking)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableSpeakingTwice() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableAudioWhileSpeaking() {
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
|
||||
val expectedStoppedSpeaking = DataChannelMessage("stoppedSpeaking")
|
||||
val expectedAudioOff = DataChannelMessage("audioOff")
|
||||
|
||||
val expectedMuteAudio = NCSignalingMessage()
|
||||
expectedMuteAudio.roomType = "video"
|
||||
expectedMuteAudio.type = "mute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "audio"
|
||||
expectedMuteAudio.payload = payload
|
||||
|
||||
val inOrder = Mockito.inOrder(mockedMessageSender)
|
||||
|
||||
inOrder.verify(mockedMessageSender!!).sendToAll(expectedStoppedSpeaking)
|
||||
inOrder.verify(mockedMessageSender!!).sendToAll(expectedAudioOff)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedMuteAudio)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableSpeakingWithAudioDisabled() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
|
||||
Mockito.verifyNoInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableVideo() {
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
val expectedVideoOn = DataChannelMessage("videoOn")
|
||||
|
||||
val expectedUnmuteVideo = NCSignalingMessage()
|
||||
expectedUnmuteVideo.roomType = "video"
|
||||
expectedUnmuteVideo.type = "unmute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "video"
|
||||
expectedUnmuteVideo.payload = payload
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedVideoOn)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedUnmuteVideo)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableVideoTwice() {
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableVideo() {
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
val expectedVideoOff = DataChannelMessage("videoOff")
|
||||
|
||||
val expectedMuteVideo = NCSignalingMessage()
|
||||
expectedMuteVideo.roomType = "video"
|
||||
expectedMuteVideo.type = "mute"
|
||||
val payload = NCMessagePayload()
|
||||
payload.name = "video"
|
||||
expectedMuteVideo.payload = payload
|
||||
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedVideoOff)
|
||||
Mockito.verify(mockedMessageSender!!).sendToAll(expectedMuteVideo)
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableVideoTwice() {
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeStateAfterDestroying() {
|
||||
localCallParticipantModel!!.isAudioEnabled = false
|
||||
localCallParticipantModel!!.isSpeaking = false
|
||||
localCallParticipantModel!!.isVideoEnabled = false
|
||||
|
||||
localStateBroadcaster = LocalStateBroadcaster(localCallParticipantModel, mockedMessageSender)
|
||||
|
||||
localStateBroadcaster!!.destroy()
|
||||
localCallParticipantModel!!.isAudioEnabled = true
|
||||
localCallParticipantModel!!.isSpeaking = true
|
||||
localCallParticipantModel!!.isVideoEnabled = true
|
||||
|
||||
Mockito.verifyNoMoreInteractions(mockedMessageSender)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.never
|
||||
|
||||
class MessageSenderMcuTest {
|
||||
|
||||
private var peerConnectionWrappers: MutableList<PeerConnectionWrapper?>? = null
|
||||
private var peerConnectionWrapper1: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2Screen: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper4Screen: PeerConnectionWrapper? = null
|
||||
private var ownPeerConnectionWrapper: PeerConnectionWrapper? = null
|
||||
private var ownPeerConnectionWrapperScreen: PeerConnectionWrapper? = null
|
||||
|
||||
private var messageSender: MessageSenderMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||
|
||||
val callParticipants = HashMap<String, CallParticipant>()
|
||||
|
||||
peerConnectionWrappers = ArrayList()
|
||||
|
||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper1!!.sessionId).thenReturn("theSessionId1")
|
||||
Mockito.`when`(peerConnectionWrapper1!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper1)
|
||||
|
||||
peerConnectionWrapper2 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2)
|
||||
|
||||
peerConnectionWrapper2Screen = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2Screen!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2Screen!!.videoStreamType).thenReturn("screen")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2Screen)
|
||||
|
||||
peerConnectionWrapper4Screen = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper4Screen!!.sessionId).thenReturn("theSessionId4")
|
||||
Mockito.`when`(peerConnectionWrapper4Screen!!.videoStreamType).thenReturn("screen")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper4Screen)
|
||||
|
||||
ownPeerConnectionWrapper = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(ownPeerConnectionWrapper!!.sessionId).thenReturn("ownSessionId")
|
||||
Mockito.`when`(ownPeerConnectionWrapper!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(ownPeerConnectionWrapper)
|
||||
|
||||
ownPeerConnectionWrapperScreen = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(ownPeerConnectionWrapperScreen!!.sessionId).thenReturn("ownSessionId")
|
||||
Mockito.`when`(ownPeerConnectionWrapperScreen!!.videoStreamType).thenReturn("screen")
|
||||
peerConnectionWrappers!!.add(ownPeerConnectionWrapperScreen)
|
||||
|
||||
messageSender = MessageSenderMcu(
|
||||
signalingMessageSender,
|
||||
callParticipants.keys,
|
||||
peerConnectionWrappers,
|
||||
"ownSessionId"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAll() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(ownPeerConnectionWrapper!!).send(message)
|
||||
Mockito.verify(ownPeerConnectionWrapperScreen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAllIfOwnScreenPeerConnection() {
|
||||
peerConnectionWrappers!!.remove(ownPeerConnectionWrapper)
|
||||
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(ownPeerConnectionWrapper!!, never()).send(message)
|
||||
Mockito.verify(ownPeerConnectionWrapperScreen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAllWithoutOwnPeerConnection() {
|
||||
peerConnectionWrappers!!.remove(ownPeerConnectionWrapper)
|
||||
peerConnectionWrappers!!.remove(ownPeerConnectionWrapperScreen)
|
||||
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(ownPeerConnectionWrapper!!, never()).send(message)
|
||||
Mockito.verify(ownPeerConnectionWrapperScreen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.never
|
||||
|
||||
class MessageSenderNoMcuTest {
|
||||
|
||||
private var peerConnectionWrappers: MutableList<PeerConnectionWrapper?>? = null
|
||||
private var peerConnectionWrapper1: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2Screen: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper4Screen: PeerConnectionWrapper? = null
|
||||
|
||||
private var messageSender: MessageSenderNoMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||
|
||||
val callParticipants = HashMap<String, CallParticipant>()
|
||||
|
||||
peerConnectionWrappers = ArrayList()
|
||||
|
||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper1!!.sessionId).thenReturn("theSessionId1")
|
||||
Mockito.`when`(peerConnectionWrapper1!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper1)
|
||||
|
||||
peerConnectionWrapper2 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2)
|
||||
|
||||
peerConnectionWrapper2Screen = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2Screen!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2Screen!!.videoStreamType).thenReturn("screen")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2Screen)
|
||||
|
||||
peerConnectionWrapper4Screen = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper4Screen!!.sessionId).thenReturn("theSessionId4")
|
||||
Mockito.`when`(peerConnectionWrapper4Screen!!.videoStreamType).thenReturn("screen")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper4Screen)
|
||||
|
||||
messageSender = MessageSenderNoMcu(signalingMessageSender, callParticipants.keys, peerConnectionWrappers)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessage() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.send(message, "theSessionId2")
|
||||
|
||||
Mockito.verify(peerConnectionWrapper2!!).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageIfScreenPeerConnection() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.send(message, "theSessionId4")
|
||||
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageIfNoPeerConnection() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.send(message, "theSessionId3")
|
||||
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAll() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(peerConnectionWrapper1!!).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2Screen!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper4Screen!!, never()).send(message)
|
||||
}
|
||||
}
|
||||
134
app/src/test/java/com/nextcloud/talk/call/MessageSenderTest.kt
Normal file
134
app/src/test/java/com/nextcloud/talk/call/MessageSenderTest.kt
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.invocation.InvocationOnMock
|
||||
|
||||
class MessageSenderTest {
|
||||
|
||||
private class MessageSender(
|
||||
signalingMessageSender: SignalingMessageSender?,
|
||||
callParticipantSessionIds: Set<String>?,
|
||||
peerConnectionWrappers: List<PeerConnectionWrapper>?
|
||||
) : com.nextcloud.talk.call.MessageSender(
|
||||
signalingMessageSender,
|
||||
callParticipantSessionIds,
|
||||
peerConnectionWrappers
|
||||
) {
|
||||
|
||||
override fun sendToAll(dataChannelMessage: DataChannelMessage?) {
|
||||
// Not used in base class tests
|
||||
}
|
||||
}
|
||||
|
||||
private var signalingMessageSender: SignalingMessageSender? = null
|
||||
|
||||
private var callParticipants: MutableMap<String, CallParticipant>? = null
|
||||
|
||||
private var messageSender: MessageSender? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||
|
||||
callParticipants = HashMap()
|
||||
|
||||
val callParticipant1: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||
callParticipants!!["theSessionId1"] = callParticipant1
|
||||
|
||||
val callParticipant2: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||
callParticipants!!["theSessionId2"] = callParticipant2
|
||||
|
||||
val callParticipant3: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||
callParticipants!!["theSessionId3"] = callParticipant3
|
||||
|
||||
val callParticipant4: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||
callParticipants!!["theSessionId4"] = callParticipant4
|
||||
|
||||
val peerConnectionWrappers = ArrayList<PeerConnectionWrapper>()
|
||||
|
||||
messageSender = MessageSender(signalingMessageSender, callParticipants!!.keys, peerConnectionWrappers)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendSignalingMessage() {
|
||||
val message: NCSignalingMessage = Mockito.mock(NCSignalingMessage::class.java)
|
||||
messageSender!!.send(message, "theSessionId2")
|
||||
|
||||
Mockito.verify(message).to = "theSessionId2"
|
||||
Mockito.verify(signalingMessageSender!!).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendSignalingMessageIfUnknownSessionId() {
|
||||
val message: NCSignalingMessage = Mockito.mock(NCSignalingMessage::class.java)
|
||||
messageSender!!.send(message, "unknownSessionId")
|
||||
|
||||
Mockito.verify(message).to = "unknownSessionId"
|
||||
Mockito.verify(signalingMessageSender!!).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendSignalingMessageToAll() {
|
||||
val sentTo: MutableList<String?> = ArrayList()
|
||||
doAnswer { invocation: InvocationOnMock ->
|
||||
val arguments = invocation.arguments
|
||||
val message = (arguments[0] as NCSignalingMessage)
|
||||
|
||||
sentTo.add(message.to)
|
||||
null
|
||||
}.`when`(signalingMessageSender!!).send(any())
|
||||
|
||||
val message = NCSignalingMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
assertTrue(sentTo.contains("theSessionId1"))
|
||||
assertTrue(sentTo.contains("theSessionId2"))
|
||||
assertTrue(sentTo.contains("theSessionId3"))
|
||||
assertTrue(sentTo.contains("theSessionId4"))
|
||||
Mockito.verify(signalingMessageSender!!, times(4)).send(message)
|
||||
Mockito.verifyNoMoreInteractions(signalingMessageSender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendSignalingMessageToAllWhenParticipantsWereUpdated() {
|
||||
val callParticipant5: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||
callParticipants!!["theSessionId5"] = callParticipant5
|
||||
|
||||
callParticipants!!.remove("theSessionId2")
|
||||
callParticipants!!.remove("theSessionId3")
|
||||
|
||||
val sentTo: MutableList<String?> = ArrayList()
|
||||
doAnswer { invocation: InvocationOnMock ->
|
||||
val arguments = invocation.arguments
|
||||
val message = (arguments[0] as NCSignalingMessage)
|
||||
|
||||
sentTo.add(message.to)
|
||||
null
|
||||
}.`when`(signalingMessageSender!!).send(any())
|
||||
|
||||
val message = NCSignalingMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
assertTrue(sentTo.contains("theSessionId1"))
|
||||
assertTrue(sentTo.contains("theSessionId4"))
|
||||
assertTrue(sentTo.contains("theSessionId5"))
|
||||
Mockito.verify(signalingMessageSender!!, times(3)).send(message)
|
||||
Mockito.verifyNoMoreInteractions(signalingMessageSender)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import com.nextcloud.talk.contacts.apiService.FakeItem
|
||||
import com.nextcloud.talk.contacts.repository.FakeRepositoryError
|
||||
import com.nextcloud.talk.contacts.repository.FakeRepositorySuccess
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ContactsViewModelTest {
|
||||
private lateinit var viewModel: ContactsViewModel
|
||||
private val repository: ContactsRepository = FakeRepositorySuccess()
|
||||
|
||||
val dispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
Dispatchers.setMain(dispatcher)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
viewModel = ContactsViewModel(repository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch contacts`() =
|
||||
runTest {
|
||||
viewModel = ContactsViewModel(repository)
|
||||
viewModel.getContactsFromSearchParams()
|
||||
assert(viewModel.contactsViewState.value is ContactsUiState.Success)
|
||||
val successState = viewModel.contactsViewState.value as ContactsUiState.Success
|
||||
assert(successState.contacts == FakeItem.contacts)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test error contacts state`() =
|
||||
runTest {
|
||||
viewModel = ContactsViewModel(FakeRepositoryError())
|
||||
assert(viewModel.contactsViewState.value is ContactsUiState.Error)
|
||||
val errorState = viewModel.contactsViewState.value as ContactsUiState.Error
|
||||
assert(errorState.message == "unable to fetch contacts")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update search query`() {
|
||||
viewModel.updateSearchQuery("Ma")
|
||||
assert(viewModel.searchQuery.value == "Ma")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial search query is empty string`() {
|
||||
viewModel.updateSearchQuery("")
|
||||
assert(viewModel.searchQuery.value == "")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial shareType is User`() {
|
||||
assert(viewModel.shareTypeList.contains(ShareType.User.shareType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update shareTypes`() {
|
||||
viewModel.updateShareTypes(listOf(ShareType.Group.shareType))
|
||||
assert(viewModel.shareTypeList.contains(ShareType.Group.shareType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial room state is none`() =
|
||||
runTest {
|
||||
assert(viewModel.roomViewState.value is RoomUiState.None)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test success room state`() =
|
||||
runTest {
|
||||
viewModel.createRoom("1", "users", "s@gmail.com", null)
|
||||
assert(viewModel.roomViewState.value is RoomUiState.Success)
|
||||
val successState = viewModel.roomViewState.value as RoomUiState.Success
|
||||
assert(successState.conversation == FakeItem.roomOverall.ocs!!.data)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test failure room state`() =
|
||||
runTest {
|
||||
viewModel = ContactsViewModel(FakeRepositoryError())
|
||||
viewModel.createRoom("1", "users", "s@gmail.com", null)
|
||||
assert(viewModel.roomViewState.value is RoomUiState.Error)
|
||||
val errorState = viewModel.roomViewState.value as RoomUiState.Error
|
||||
assert(errorState.message == "unable to create room")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test image uri`() {
|
||||
val expectedImageUri = "https://mydomain.com/index.php/avatar/vidya/512"
|
||||
val imageUri = viewModel.getImageUri("vidya", false)
|
||||
assert(imageUri == expectedImageUri)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test error image uri`() {
|
||||
val expectedImageUri = "https://mydoman.com/index.php/avatar/vidya/512"
|
||||
val imageUri = viewModel.getImageUri("vidya", false)
|
||||
assert(imageUri != expectedImageUri)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.apiService
|
||||
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOCS
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOCS
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericMeta
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
object FakeItem {
|
||||
val contacts: List<AutocompleteUser> =
|
||||
listOf(
|
||||
AutocompleteUser(id = "android", label = "Android", source = "users"),
|
||||
AutocompleteUser(id = "android1", label = "Android 1", source = "users"),
|
||||
AutocompleteUser(id = "android2", label = "Android 2", source = "users"),
|
||||
AutocompleteUser(id = "Benny", label = "Benny J", source = "users"),
|
||||
AutocompleteUser(id = "Benjamin", label = "Benjamin Schmidt", source = "users"),
|
||||
AutocompleteUser(id = "Chris", label = "Christoph Schmidt", source = "users"),
|
||||
AutocompleteUser(id = "Daniel", label = "Daniel H", source = "users"),
|
||||
AutocompleteUser(id = "Dennis", label = "Dennis Richard", source = "users"),
|
||||
AutocompleteUser(id = "Emma", label = "Emma Jackson", source = "users"),
|
||||
AutocompleteUser(id = "Emily", label = "Emily Jackson", source = "users"),
|
||||
AutocompleteUser(id = "Mario", label = "Mario Schmidt", source = "users"),
|
||||
AutocompleteUser(id = "Maria", label = "Maria Schmidt", source = "users"),
|
||||
AutocompleteUser(id = "Samsung", label = "Samsung A52", source = "users"),
|
||||
AutocompleteUser(id = "Tom", label = "Tom Müller", source = "users"),
|
||||
AutocompleteUser(id = "Tony", label = "Tony Baker", source = "users")
|
||||
)
|
||||
val contactsOverall = AutocompleteOverall(
|
||||
ocs = AutocompleteOCS(
|
||||
meta = GenericMeta(
|
||||
status = "ok",
|
||||
statusCode = 200,
|
||||
message = "OK"
|
||||
),
|
||||
data = contacts
|
||||
)
|
||||
)
|
||||
val roomOverall: RoomOverall = RoomOverall(
|
||||
ocs = RoomOCS(
|
||||
meta = GenericMeta(
|
||||
status = "ok",
|
||||
statusCode = 200,
|
||||
message = "OK"
|
||||
),
|
||||
data = mock(Conversation::class.java)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kota@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.repository
|
||||
|
||||
import com.nextcloud.talk.contacts.ContactsRepository
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
|
||||
class FakeRepositoryError : ContactsRepository {
|
||||
@Suppress("Detekt.TooGenericExceptionThrown")
|
||||
override suspend fun getContacts(searchQuery: String?, shareTypes: List<String>): AutocompleteOverall =
|
||||
throw Exception("unable to fetch contacts")
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionThrown")
|
||||
override suspend fun createRoom(
|
||||
roomType: String,
|
||||
sourceType: String?,
|
||||
userId: String,
|
||||
conversationName: String?
|
||||
): RoomOverall = throw Exception("unable to create room")
|
||||
|
||||
override fun getImageUri(avatarId: String, requestBigSize: Boolean) =
|
||||
"https://mydoman.com/index.php/avatar/$avatarId/512"
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@email.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.repository
|
||||
|
||||
import com.nextcloud.talk.contacts.ContactsRepository
|
||||
import com.nextcloud.talk.contacts.apiService.FakeItem
|
||||
|
||||
class FakeRepositorySuccess : ContactsRepository {
|
||||
override suspend fun getContacts(searchQuery: String?, shareTypes: List<String>) = FakeItem.contactsOverall
|
||||
|
||||
override suspend fun createRoom(roomType: String, sourceType: String?, userId: String, conversationName: String?) =
|
||||
FakeItem.roomOverall
|
||||
|
||||
override fun getImageUri(avatarId: String, requestBigSize: Boolean) =
|
||||
"https://mydomain.com/index.php/avatar/$avatarId/512"
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversationinfo.viewmodel
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class ConversationInfoViewModelTest {
|
||||
|
||||
@Test
|
||||
fun `createConversationNameByParticipants should combine names correctly`() {
|
||||
val original = listOf("Dave", null, "Charlie")
|
||||
val all = listOf("Bob", "Charlie", "Dave", "Alice", null, "Simon")
|
||||
|
||||
val expectedName = "Charlie, Dave, Alice, Bob, Simon"
|
||||
val result = ConversationInfoViewModel.createConversationNameByParticipants(original, all)
|
||||
|
||||
assertEquals(expectedName, result)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.json
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.data.database.mappers.asEntity
|
||||
import com.nextcloud.talk.data.database.mappers.asModel
|
||||
import com.nextcloud.talk.data.database.model.ConversationEntity
|
||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.io.File
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class ConversationConversionTest(private val jsonFileName: String) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{index}: testDeserialization({0})")
|
||||
fun data(): List<String> =
|
||||
listOf(
|
||||
"RoomOverallExample_APIv1.json",
|
||||
"RoomOverallExample_APIv2.json",
|
||||
"RoomOverallExample_APIv4.json"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeserialization() {
|
||||
val jsonFile = File("src/test/resources/$jsonFileName")
|
||||
val jsonString = jsonFile.readText()
|
||||
|
||||
val roomOverall: RoomOverall = LoganSquare.parse(jsonString, RoomOverall::class.java)
|
||||
assertNotNull(roomOverall)
|
||||
|
||||
val conversationJson = roomOverall.ocs!!.data!!
|
||||
assertNotNull(conversationJson)
|
||||
|
||||
val conversationEntity = conversationJson.asEntity(1)
|
||||
assertNotNull(conversationEntity)
|
||||
|
||||
val apiVersion: Int = jsonFileName.substringAfterLast("APIv").first().digitToInt()
|
||||
|
||||
checkConversationEntity(conversationEntity, apiVersion)
|
||||
|
||||
val conversationModel = conversationEntity.asModel()
|
||||
val conversationEntityConvertedBack = conversationModel.asEntity()
|
||||
|
||||
checkConversationEntity(conversationEntityConvertedBack, apiVersion)
|
||||
}
|
||||
|
||||
private fun checkConversationEntity(conversationEntity: ConversationEntity, apiVersion: Int) {
|
||||
assertEquals("1@juwd77g6", conversationEntity.internalId)
|
||||
assertEquals(1, conversationEntity.accountId)
|
||||
|
||||
// check if default values are set for the fields when API_V1 is used
|
||||
if (apiVersion == 1) {
|
||||
checkConversationEntityV1(conversationEntity)
|
||||
}
|
||||
|
||||
if (apiVersion >= 1) {
|
||||
checkConversationEntityLargerThanV1(conversationEntity)
|
||||
}
|
||||
|
||||
if (apiVersion >= 2) {
|
||||
assertEquals(false, conversationEntity.canDeleteConversation)
|
||||
assertEquals(true, conversationEntity.canLeaveConversation)
|
||||
}
|
||||
|
||||
if (apiVersion >= 3) {
|
||||
assertEquals("test", conversationEntity.description)
|
||||
// assertEquals("", conversationEntity.attendeeId) // Not implemented
|
||||
// assertEquals("", conversationEntity.attendeePin) // Not implemented
|
||||
assertEquals("users", conversationEntity.actorType)
|
||||
assertEquals("marcel2", conversationEntity.actorId)
|
||||
|
||||
// assertEquals("", conversationEntity.listable) // Not implemented
|
||||
assertEquals(0, conversationEntity.callFlag)
|
||||
// assertEquals("", conversationEntity.sipEnabled) // Not implemented
|
||||
// assertEquals("", conversationEntity.canEnableSIP) // Not implemented
|
||||
assertEquals(92320, conversationEntity.lastCommonReadMessage)
|
||||
}
|
||||
|
||||
if (apiVersion >= 4) {
|
||||
checkConversationEntityV4(conversationEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkConversationEntityLargerThanV1(conversationEntity: ConversationEntity) {
|
||||
assertEquals("juwd77g6", conversationEntity.token)
|
||||
assertEquals(ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL, conversationEntity.type)
|
||||
assertEquals("marcel", conversationEntity.name)
|
||||
assertEquals("Marcel", conversationEntity.displayName)
|
||||
assertEquals(Participant.ParticipantType.OWNER, conversationEntity.participantType)
|
||||
assertEquals(
|
||||
ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_WRITE,
|
||||
conversationEntity.conversationReadOnlyState
|
||||
)
|
||||
assertEquals(1727185155, conversationEntity.lastPing)
|
||||
assertEquals("0", conversationEntity.sessionId)
|
||||
assertEquals(false, conversationEntity.hasPassword)
|
||||
assertEquals(false, conversationEntity.hasCall)
|
||||
assertEquals(true, conversationEntity.canStartCall)
|
||||
assertEquals(1727098966, conversationEntity.lastActivity)
|
||||
assertEquals(false, conversationEntity.favorite)
|
||||
assertEquals(ConversationEnums.NotificationLevel.ALWAYS, conversationEntity.notificationLevel)
|
||||
assertEquals(ConversationEnums.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS, conversationEntity.lobbyState)
|
||||
assertEquals(0, conversationEntity.lobbyTimer)
|
||||
assertEquals(0, conversationEntity.unreadMessages)
|
||||
assertEquals(false, conversationEntity.unreadMention)
|
||||
assertEquals(92320, conversationEntity.lastReadMessage)
|
||||
assertNotNull(conversationEntity.lastMessage)
|
||||
assertTrue(conversationEntity.lastMessage is String)
|
||||
assertTrue(conversationEntity.lastMessage!!.contains("token"))
|
||||
assertEquals(ConversationEnums.ObjectType.DEFAULT, conversationEntity.objectType)
|
||||
}
|
||||
|
||||
private fun checkConversationEntityV4(conversationEntity: ConversationEntity) {
|
||||
assertEquals("143a9df3", conversationEntity.avatarVersion)
|
||||
assertEquals(0, conversationEntity.callStartTime)
|
||||
assertEquals(0, conversationEntity.callRecording)
|
||||
assertEquals(false, conversationEntity.unreadMentionDirect)
|
||||
// assertEquals(, conversationEntity.breakoutRoomMode) // Not implemented
|
||||
// assertEquals(, conversationEntity.breakoutRoomStatus) // Not implemented
|
||||
assertEquals("away", conversationEntity.status)
|
||||
assertEquals("👻", conversationEntity.statusIcon)
|
||||
assertEquals("buuuuh", conversationEntity.statusMessage)
|
||||
assertEquals(null, conversationEntity.statusClearAt)
|
||||
assertEquals("143a9df3", conversationEntity.avatarVersion)
|
||||
// assertEquals("", conversationEntity.isCustomAvatar) // Not implemented
|
||||
assertEquals(0, conversationEntity.callStartTime)
|
||||
assertEquals(0, conversationEntity.callRecording)
|
||||
// assertEquals("", conversationEntity.recordingConsent) // Not implemented
|
||||
// assertEquals("", conversationEntity.mentionPermissions) // Not implemented
|
||||
// assertEquals("", conversationEntity.isArchived) // Not implemented
|
||||
}
|
||||
|
||||
private fun checkConversationEntityV1(conversationEntity: ConversationEntity) {
|
||||
// default values for API_V2 fields
|
||||
assertEquals(false, conversationEntity.canDeleteConversation)
|
||||
assertEquals(true, conversationEntity.canLeaveConversation)
|
||||
|
||||
// default values for API_V3 fields
|
||||
assertEquals("", conversationEntity.description)
|
||||
assertEquals("", conversationEntity.actorType)
|
||||
assertEquals("", conversationEntity.actorId)
|
||||
assertEquals(0, conversationEntity.callFlag)
|
||||
assertEquals(0, conversationEntity.lastCommonReadMessage)
|
||||
|
||||
// default values for API_V4 fields
|
||||
assertEquals("", conversationEntity.avatarVersion)
|
||||
assertEquals(0, conversationEntity.callStartTime)
|
||||
assertEquals(0, conversationEntity.callRecording)
|
||||
assertEquals(false, conversationEntity.unreadMentionDirect)
|
||||
assertEquals("", conversationEntity.status)
|
||||
assertEquals("", conversationEntity.statusIcon)
|
||||
assertEquals("", conversationEntity.statusMessage)
|
||||
assertEquals(null, conversationEntity.statusClearAt)
|
||||
assertEquals("", conversationEntity.avatarVersion)
|
||||
assertEquals(0, conversationEntity.callStartTime)
|
||||
assertEquals(0, conversationEntity.callRecording)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.login.data
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.work.WorkInfo
|
||||
import com.nextcloud.talk.account.data.LoginRepository
|
||||
import com.nextcloud.talk.account.data.io.LocalLoginDataSource
|
||||
import com.nextcloud.talk.account.data.model.LoginCompletion
|
||||
import com.nextcloud.talk.account.data.model.LoginResponse
|
||||
import com.nextcloud.talk.account.data.network.NetworkLoginDataSource
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.junit.MockitoJUnitRunner
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Suppress("TooManyFunctions", "TooGenericExceptionCaught")
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(MockitoJUnitRunner.Silent::class)
|
||||
class LoginRepositoryTest {
|
||||
|
||||
@get:Rule
|
||||
val rule = InstantTaskExecutorRule()
|
||||
|
||||
// Repository dependencies
|
||||
@Mock
|
||||
lateinit var networkLoginDataSource: NetworkLoginDataSource
|
||||
|
||||
@Mock
|
||||
lateinit var localLoginDataSource: LocalLoginDataSource
|
||||
|
||||
// Additional mocks for LocalLoginDataSource dependencies
|
||||
@Mock
|
||||
lateinit var liveData: LiveData<WorkInfo?>
|
||||
|
||||
lateinit var repo: LoginRepository
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
repo = LoginRepository(networkLoginDataSource, localLoginDataSource)
|
||||
}
|
||||
|
||||
// ========== pollLogin() Tests ==========
|
||||
|
||||
@Test
|
||||
fun `pollLogin returns successful LoginCompletion when network returns HTTP 200`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val successfulLoginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(successfulLoginData)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
assertEquals("https://server.com", result?.server)
|
||||
assertEquals("testuser", result?.loginName)
|
||||
assertEquals("apppass123", result?.appPassword)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pollLogin returns null when network returns null`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(null)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pollLogin continues polling when status is not HTTP 200 then returns successful result`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val pendingLoginData = LoginCompletion(202, "https://server.com", "testuser", "apppass123")
|
||||
val successfulLoginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(pendingLoginData)
|
||||
.thenReturn(successfulLoginData)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
verify(networkLoginDataSource, times(2)).performLoginFlowV2(mockResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pollLogin handles slow connection by continuing to poll with delays`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val slowResponse1 = LoginCompletion(202, "https://server.com", "testuser", "apppass123")
|
||||
val slowResponse2 = LoginCompletion(404, "https://server.com", "testuser", "apppass123")
|
||||
val slowResponse3 = LoginCompletion(500, "https://server.com", "testuser", "apppass123")
|
||||
val successResponse = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(slowResponse1)
|
||||
.thenReturn(slowResponse2)
|
||||
.thenReturn(slowResponse3)
|
||||
.thenReturn(successResponse)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
verify(networkLoginDataSource, times(4)).performLoginFlowV2(mockResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pollLogin handles network timeouts during slow connection gracefully`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val timeoutResponse = LoginCompletion(408, "https://server.com", "testuser", "apppass123")
|
||||
val successResponse = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(timeoutResponse)
|
||||
.thenReturn(timeoutResponse)
|
||||
.thenReturn(successResponse)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
verify(networkLoginDataSource, times(3)).performLoginFlowV2(mockResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pollLogin stops when cancelLoginFlow is called`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val pendingLoginData = LoginCompletion(
|
||||
202,
|
||||
"https://server.com",
|
||||
"testuser",
|
||||
"apppass123"
|
||||
)
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(pendingLoginData)
|
||||
|
||||
// Act - cancel before polling
|
||||
repo.cancelLoginFlow()
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
verify(networkLoginDataSource, never()).performLoginFlowV2(any())
|
||||
}
|
||||
|
||||
// ========== startLoginFlowFromQR() Tests ==========
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR returns LoginCompletion for valid QR data with all parameters`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/user:testuser&server:https%3A//example.com&password:testpass"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
assertEquals("https://example.com", result?.server)
|
||||
assertEquals("testuser", result?.loginName)
|
||||
assertEquals("testpass", result?.appPassword)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR returns LoginCompletion for minimal valid QR data`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/server:https%3A//example.com"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR returns null for invalid prefix`() {
|
||||
// Arrange
|
||||
val qrData = "invalid://login/user:testuser&server:https://example.com"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR returns null when too many arguments provided`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/user:test&server:https://example.com&password:pass&extra:value"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR returns null for empty data`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR sets reAuth flag correctly`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/server:https%3A//example.com"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData, reAuth = true)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR handles URL encoding correctly`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/user:test%40user.com&server:https%3A//example.com%3A8080&password:test%26pass"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals("test@user.com", result?.loginName)
|
||||
assertEquals("https://example.com:8080", result?.server)
|
||||
assertEquals("test&pass", result?.appPassword)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR handles mixed parameter order`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/password:testpass&user:testuser&server:https%3A//example.com"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals("https://example.com", result?.server)
|
||||
assertEquals("testuser", result?.loginName)
|
||||
assertEquals("testpass", result?.appPassword)
|
||||
}
|
||||
|
||||
// ========== startLoginFlow() Tests ==========
|
||||
|
||||
@Test
|
||||
fun `startLoginFlow returns LoginResponse from network`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val baseUrl = "https://example.com"
|
||||
val mockResponse = LoginResponse("token123", "https://example.com/poll", "https://example.com/login")
|
||||
whenever(networkLoginDataSource.anonymouslyPostLoginRequest(baseUrl))
|
||||
.thenReturn(mockResponse)
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlow(baseUrl)
|
||||
|
||||
// Assert
|
||||
assertEquals(mockResponse, result)
|
||||
verify(networkLoginDataSource).anonymouslyPostLoginRequest(baseUrl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlow returns null when network returns null`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val baseUrl = "https://example.com"
|
||||
whenever(networkLoginDataSource.anonymouslyPostLoginRequest(baseUrl))
|
||||
.thenReturn(null)
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlow(baseUrl)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlow sets reAuth flag correctly`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val baseUrl = "https://example.com"
|
||||
val mockResponse = LoginResponse("token123", "https://example.com/poll", "https://example.com/login")
|
||||
whenever(networkLoginDataSource.anonymouslyPostLoginRequest(baseUrl))
|
||||
.thenReturn(mockResponse)
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlow(baseUrl, reAuth = true)
|
||||
|
||||
// Assert
|
||||
assertEquals(mockResponse, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlow handles network SSL exceptions`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val baseUrl = "https://example.com"
|
||||
whenever(networkLoginDataSource.anonymouslyPostLoginRequest(baseUrl))
|
||||
.thenReturn(null) // NetworkLoginDataSource catches SSL exceptions and returns null
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlow(baseUrl)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
verify(networkLoginDataSource).anonymouslyPostLoginRequest(baseUrl)
|
||||
}
|
||||
|
||||
// ========== cancelLoginFlow() Tests ==========
|
||||
|
||||
@Test
|
||||
fun `cancelLoginFlow stops polling loop`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val pendingLoginData = LoginCompletion(202, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(pendingLoginData)
|
||||
|
||||
// Act
|
||||
repo.cancelLoginFlow()
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
// ========== parseAndLogin() Tests ==========
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin returns null when user is scheduled for deletion`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(true)
|
||||
whenever(localLoginDataSource.startAccountRemovalWorker())
|
||||
.thenReturn(liveData)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
verify(localLoginDataSource).startAccountRemovalWorker()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin returns null when user exists and reAuth is false`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(true)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
verify(localLoginDataSource, never()).updateUser(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin updates user when user exists and reAuth is true`() {
|
||||
// Arrange - First set reAuth to true via QR flow
|
||||
val qrData = "nc://login/server:https%3A//example.com"
|
||||
repo.startLoginFlowFromQR(qrData, reAuth = true)
|
||||
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(true)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
verify(localLoginDataSource).updateUser(loginData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin returns Bundle for new user with https protocol`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(false)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertTrue(result is Bundle)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin returns Bundle for new user with http protocol`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "http://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(false)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertTrue(result is Bundle)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin returns Bundle for new user without protocol prefix`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(false)
|
||||
|
||||
// Act
|
||||
val result = repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertTrue(result is Bundle)
|
||||
}
|
||||
|
||||
// ========== LocalLoginDataSource Integration Tests ==========
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin properly integrates with LocalLoginDataSource checkIfUserIsScheduledForDeletion`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(true)
|
||||
whenever(localLoginDataSource.startAccountRemovalWorker())
|
||||
.thenReturn(liveData)
|
||||
|
||||
// Act
|
||||
repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
verify(localLoginDataSource).checkIfUserIsScheduledForDeletion(loginData)
|
||||
verify(localLoginDataSource).startAccountRemovalWorker()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin properly integrates with LocalLoginDataSource checkIfUserExists`() {
|
||||
// Arrange
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(true)
|
||||
|
||||
// Act
|
||||
repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
verify(localLoginDataSource).checkIfUserExists(loginData)
|
||||
verify(localLoginDataSource, never()).updateUser(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseAndLogin calls updateUser with correct LoginCompletion data`() {
|
||||
// Arrange - Set reAuth flag first
|
||||
val qrData = "nc://login/server:https%3A//example.com"
|
||||
repo.startLoginFlowFromQR(qrData, reAuth = true)
|
||||
|
||||
val loginData = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
whenever(localLoginDataSource.checkIfUserIsScheduledForDeletion(loginData))
|
||||
.thenReturn(false)
|
||||
whenever(localLoginDataSource.checkIfUserExists(loginData))
|
||||
.thenReturn(true)
|
||||
|
||||
// Act
|
||||
repo.parseAndLogin(loginData)
|
||||
|
||||
// Assert
|
||||
verify(localLoginDataSource).updateUser(loginData)
|
||||
}
|
||||
|
||||
// ========== Edge Cases and Error Handling ==========
|
||||
|
||||
@Test
|
||||
fun `pollLogin handles performLoginFlowV2 returning error status codes`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val mockResponse = LoginResponse("token123", "https://server.com/poll", "https://server.com/login")
|
||||
val errorResponse = LoginCompletion(404, "", "", "")
|
||||
val successResponse = LoginCompletion(200, "https://server.com", "testuser", "apppass123")
|
||||
|
||||
whenever(networkLoginDataSource.performLoginFlowV2(mockResponse))
|
||||
.thenReturn(errorResponse)
|
||||
.thenReturn(successResponse)
|
||||
|
||||
// Act
|
||||
val result = repo.pollLogin(mockResponse)
|
||||
|
||||
// Assert
|
||||
assertNotNull(result)
|
||||
assertEquals(200, result?.status)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR handles malformed URL gracefully`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/malformed&data&without&proper&key:value"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result) // Should still create LoginCompletion with empty values
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `startLoginFlowFromQR handles partial parameter data`() {
|
||||
// Arrange
|
||||
val qrData = "nc://login/user:testuser&server:"
|
||||
|
||||
// Act
|
||||
val result = repo.startLoginFlowFromQR(qrData)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.login.data.network
|
||||
|
||||
import com.nextcloud.talk.account.data.model.LoginResponse
|
||||
import com.nextcloud.talk.account.data.network.NetworkLoginDataSource
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@Suppress("ktlint:standard:max-line-length", "MaxLineLength")
|
||||
class NetworkLoginDataSourceTest {
|
||||
|
||||
lateinit var network: NetworkLoginDataSource
|
||||
private val okHttpClient: OkHttpClient = OkHttpClient.Builder().build()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
network = NetworkLoginDataSource(okHttpClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing anonymouslyPostLoginRequest correct path`() {
|
||||
val server = MockWebServer()
|
||||
server.start(0)
|
||||
val httpUrl = server.url("index.php/login/v2")
|
||||
val validResponse = """
|
||||
{
|
||||
"poll":{
|
||||
"token":"mQUYQdffOSAMJYtm8pVpkOsVqXt5hglnuSpO5EMbgJMNEPFGaiDe8OUjvrJ2WcYcBSLgqynu9jaPFvZHMl83ybMvp6aDIDARjTFIBpRWod6p32fL9LIpIStvc6k8Wrs1",
|
||||
"endpoint":"https:\/\/cloud.example.com\/login\/v2\/poll"
|
||||
},
|
||||
"login":"https:\/\/cloud.example.com\/login\/v2\/flow\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg"
|
||||
}
|
||||
""".trimIndent()
|
||||
val mockResponse = MockResponse().setBody(validResponse)
|
||||
server.enqueue(mockResponse)
|
||||
|
||||
val loginResponse = network.anonymouslyPostLoginRequest(httpUrl.toString())
|
||||
assertNotNull(loginResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing anonymouslyPostLoginRequest error path`() {
|
||||
val server = MockWebServer()
|
||||
val invalidResponse = MockResponse()
|
||||
.addHeader("Content-Type", "application/json; charset=utf-8")
|
||||
.addHeader("Cache-Control", "no-cache")
|
||||
.setResponseCode(404)
|
||||
.setBody("{}")
|
||||
|
||||
server.start()
|
||||
server.enqueue(invalidResponse)
|
||||
val httpUrl = server.url("index.php/login/v2")
|
||||
|
||||
val loginResponse = network.anonymouslyPostLoginRequest(httpUrl.toString())
|
||||
assertNull(loginResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing anonymouslyPostLoginRequest malformed response`() {
|
||||
val server = MockWebServer()
|
||||
val validResponse = """
|
||||
{
|
||||
"poll":{
|
||||
"token":"mQUYQdffOSAMJYtm8pVpkOsVqXt5hglnuSpO5EMbgJMNEPFGaiDe8OUjvrJ2WcYcBSLgqynu9jaPFvZHMl83ybMvp6aDIDARjTFIBpRWod6p32fL9LIpIStvc6k8Wrs1"
|
||||
},
|
||||
"login":"https:\/\/cloud.example.com\/login\/v2\/flow\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val mockResponse = MockResponse().setBody(validResponse)
|
||||
server.enqueue(mockResponse)
|
||||
server.start()
|
||||
val httpUrl = server.url("index.php/login/v2")
|
||||
|
||||
val loginResponse = network.anonymouslyPostLoginRequest(httpUrl.toString())
|
||||
assertNull(loginResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing performLoginFlowV2 correct path`() {
|
||||
val server = MockWebServer()
|
||||
val validBody = """
|
||||
{
|
||||
"server":"https:\/\/cloud.example.com",
|
||||
"loginName":"username",
|
||||
"appPassword":"yKTVA4zgxjfivy52WqD8kW3M2pKGQr6srmUXMipRdunxjPFripJn0GMfmtNOqOolYSuJ6sCN"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val validResponse = MockResponse()
|
||||
.setBody(validBody)
|
||||
|
||||
server.enqueue(validResponse)
|
||||
server.start()
|
||||
val httpUrl = server.url("login/v2/poll")
|
||||
val loginResponse = LoginResponse(
|
||||
token = "mQUYQdffOSAMJYtm8pVpkOsVqXt5hglnuSpO5EMbgJMNEPFGaiDe8OUjvrJ2WcYcBSLgqynu9jaPFvZHMl83ybMvp6aDIDARjTFIBpRWod6p32fL9LIpIStvc6k8Wrs1",
|
||||
pollUrl = httpUrl.toString(),
|
||||
loginUrl = "https:\\/\\/cloud.example.com\\/login\\/v2\\/flow\\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg"
|
||||
)
|
||||
|
||||
val loginCompletion = network.performLoginFlowV2(loginResponse)
|
||||
assertNotNull(loginCompletion)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing performLoginFlowV2 error path`() {
|
||||
val server = MockWebServer()
|
||||
|
||||
val invalidResponse = MockResponse()
|
||||
.addHeader("Content-Type", "application/json; charset=utf-8")
|
||||
.addHeader("Cache-Control", "no-cache")
|
||||
.setResponseCode(404)
|
||||
.setBody("{}")
|
||||
|
||||
server.enqueue(invalidResponse)
|
||||
server.start()
|
||||
val httpUrl = server.url("login/v2/poll")
|
||||
val loginResponse = LoginResponse(
|
||||
token = "mQUYQdffOSAMJYtm8pVpkOsVqXt5hglnuSpO5EMbgJMNEPFGaiDe8OUjvrJ2WcYcBSLgqynu9jaPFvZHMl83ybMvp6aDIDARjTFIBpRWod6p32fL9LIpIStvc6k8Wrs1",
|
||||
pollUrl = httpUrl.toString(),
|
||||
loginUrl = "https:\\/\\/cloud.example.com\\/login\\/v2\\/flow\\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg"
|
||||
)
|
||||
|
||||
val loginCompletion = network.performLoginFlowV2(loginResponse)
|
||||
assert(loginCompletion?.status == 404)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testing performLoginFlowV2 malformed response`() {
|
||||
val server = MockWebServer()
|
||||
val validBody = """
|
||||
{
|
||||
"server":"https:\/\/cloud.example.com",
|
||||
"loginName":"username"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val validResponse = MockResponse()
|
||||
.setBody(validBody)
|
||||
|
||||
server.enqueue(validResponse)
|
||||
server.start()
|
||||
val httpUrl = server.url("login/v2/poll")
|
||||
val loginResponse = LoginResponse(
|
||||
token = "mQUYQdffOSAMJYtm8pVpkOsVqXt5hglnuSpO5EMbgJMNEPFGaiDe8OUjvrJ2WcYcBSLgqynu9jaPFvZHMl83ybMvp6aDIDARjTFIBpRWod6p32fL9LIpIStvc6k8Wrs1",
|
||||
pollUrl = httpUrl.toString(),
|
||||
loginUrl = "https:\\/\\/cloud.example.com\\/login\\/v2\\/flow\\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg"
|
||||
)
|
||||
|
||||
val loginCompletion = network.performLoginFlowV2(loginResponse)
|
||||
assertNull(loginCompletion)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.messagesearch
|
||||
|
||||
import com.nextcloud.talk.models.domain.SearchMessageEntry
|
||||
import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository
|
||||
import com.nextcloud.talk.test.fakes.FakeUnifiedSearchRepository
|
||||
import io.reactivex.Observable
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class MessageSearchHelperTest {
|
||||
|
||||
val repository = FakeUnifiedSearchRepository()
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun createMessageEntry(
|
||||
searchTerm: String = "foo",
|
||||
thumbnailURL: String = "foo",
|
||||
title: String = "foo",
|
||||
messageExcerpt: String = "foo",
|
||||
conversationToken: String = "foo",
|
||||
messageId: String? = "foo"
|
||||
) = SearchMessageEntry(searchTerm, thumbnailURL, title, messageExcerpt, conversationToken, messageId)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emptySearch() {
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(0, false, emptyList())
|
||||
|
||||
val sut = MessageSearchHelper(repository)
|
||||
|
||||
val testObserver = sut.startMessageSearch("foo").test()
|
||||
testObserver.assertComplete()
|
||||
testObserver.assertValueCount(1)
|
||||
val expected = MessageSearchHelper.MessageSearchResults(emptyList(), false)
|
||||
testObserver.assertValue(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonEmptySearch_withMoreResults() {
|
||||
val entries = (1..5).map { createMessageEntry() }
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(5, true, entries)
|
||||
|
||||
val sut = MessageSearchHelper(repository)
|
||||
|
||||
val observable = sut.startMessageSearch("foo")
|
||||
val expected = MessageSearchHelper.MessageSearchResults(entries, true)
|
||||
testCall(observable, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonEmptySearch_withNoMoreResults() {
|
||||
val entries = (1..2).map { createMessageEntry() }
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(2, false, entries)
|
||||
|
||||
val sut = MessageSearchHelper(repository)
|
||||
|
||||
val observable = sut.startMessageSearch("foo")
|
||||
val expected = MessageSearchHelper.MessageSearchResults(entries, false)
|
||||
testCall(observable, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonEmptySearch_consecutiveSearches_sameResult() {
|
||||
val entries = (1..2).map { createMessageEntry() }
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(2, false, entries)
|
||||
|
||||
val sut = MessageSearchHelper(repository)
|
||||
|
||||
repeat(5) {
|
||||
val observable = sut.startMessageSearch("foo")
|
||||
val expected = MessageSearchHelper.MessageSearchResults(entries, false)
|
||||
testCall(observable, expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadMore_noPreviousResults() {
|
||||
val sut = MessageSearchHelper(repository)
|
||||
Assert.assertEquals(null, sut.loadMore())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadMore_previousResults_sameSearch() {
|
||||
val sut = MessageSearchHelper(repository)
|
||||
|
||||
val firstPageEntries = (1..5).map { createMessageEntry() }
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(5, true, firstPageEntries)
|
||||
|
||||
val firstPageObservable = sut.startMessageSearch("foo")
|
||||
Assert.assertEquals(0, repository.lastRequestedCursor)
|
||||
val firstPageExpected = MessageSearchHelper.MessageSearchResults(firstPageEntries, true)
|
||||
testCall(firstPageObservable, firstPageExpected)
|
||||
|
||||
val secondPageEntries = (1..5).map { createMessageEntry(title = "bar") }
|
||||
repository.response = UnifiedSearchRepository.UnifiedSearchResults(10, false, secondPageEntries)
|
||||
|
||||
val secondPageObservable = sut.loadMore()
|
||||
Assert.assertEquals(5, repository.lastRequestedCursor)
|
||||
Assert.assertNotNull(secondPageObservable)
|
||||
val secondPageExpected = MessageSearchHelper.MessageSearchResults(firstPageEntries + secondPageEntries, false)
|
||||
testCall(secondPageObservable!!, secondPageExpected)
|
||||
}
|
||||
|
||||
private fun testCall(
|
||||
searchCall: Observable<MessageSearchHelper.MessageSearchResults>,
|
||||
expectedResult: MessageSearchHelper.MessageSearchResults
|
||||
) {
|
||||
val testObserver = searchCall.test()
|
||||
testObserver.assertComplete()
|
||||
testObserver.assertValueCount(1)
|
||||
testObserver.assertValue(expectedResult)
|
||||
testObserver.dispose()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverCallParticipantTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCallParticipantMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(null, "theSessionId");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCallParticipantMessageListenerWithNullSessionId() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageRaiseHand() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("raiseHand");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("raiseHand");
|
||||
messagePayload.setState(Boolean.TRUE);
|
||||
messagePayload.setTimestamp(4815162342L);
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener, only()).onRaiseHand(true, 4815162342L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageReaction() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("reaction");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("reaction");
|
||||
messagePayload.setReaction("theReaction");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener, only()).onReaction("theReaction");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageUnshareScreen() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener, only()).onUnshareScreen();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageSeveralListenersSameFrom() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener1, "theSessionId");
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener2, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener1, only()).onUnshareScreen();
|
||||
verify(mockedCallParticipantMessageListener2, only()).onUnshareScreen();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageNotMatchingSessionId() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("notMatchingSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
signalingMessageReceiver.removeListener(mockedCallParticipantMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener3 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener1, "theSessionId");
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener2, "theSessionId");
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener3, "theSessionId");
|
||||
signalingMessageReceiver.removeListener(mockedCallParticipantMessageListener2);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener1, only()).onUnshareScreen();
|
||||
verify(mockedCallParticipantMessageListener3, only()).onUnshareScreen();
|
||||
verifyNoInteractions(mockedCallParticipantMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallParticipantMessageAfterAddingListenerAgainForDifferentFrom() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId");
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId2");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedCallParticipantMessageListener);
|
||||
|
||||
signalingMessage.setFrom("theSessionId2");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener, only()).onUnshareScreen();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCallParticipantMessageListenerWhenHandlingCallParticipantMessage() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener2, "theSessionId");
|
||||
return null;
|
||||
}).when(mockedCallParticipantMessageListener1).onUnshareScreen();
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener1, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedCallParticipantMessageListener1, only()).onUnshareScreen();
|
||||
verifyNoInteractions(mockedCallParticipantMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveCallParticipantMessageListenerWhenHandlingCallParticipantMessage() {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.CallParticipantMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedCallParticipantMessageListener2);
|
||||
return null;
|
||||
}).when(mockedCallParticipantMessageListener1).onUnshareScreen();
|
||||
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener1, "theSessionId");
|
||||
signalingMessageReceiver.addListener(mockedCallParticipantMessageListener2, "theSessionId");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("unshareScreen");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
InOrder inOrder = inOrder(mockedCallParticipantMessageListener1, mockedCallParticipantMessageListener2);
|
||||
|
||||
inOrder.verify(mockedCallParticipantMessageListener1).onUnshareScreen();
|
||||
inOrder.verify(mockedCallParticipantMessageListener2).onUnshareScreen();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverLocalParticipantTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLocalParticipantMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener((SignalingMessageReceiver.LocalParticipantMessageListener) null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingLocalParticipantMessageSwitchTo() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
Map<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedLocalParticipantMessageListener, only()).onSwitchTo("theToken");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingLocalParticipantMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
HashMap<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verifyNoInteractions(mockedLocalParticipantMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingLocalParticipantMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener3 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
HashMap<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedLocalParticipantMessageListener1, only()).onSwitchTo("theToken");
|
||||
verify(mockedLocalParticipantMessageListener3, only()).onSwitchTo("theToken");
|
||||
verifyNoInteractions(mockedLocalParticipantMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingLocalParticipantMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
HashMap<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedLocalParticipantMessageListener, only()).onSwitchTo("theToken");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLocalParticipantMessageListenerWhenHandlingExternalSignalingLocalParticipantMessage() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2);
|
||||
return null;
|
||||
}).when(mockedLocalParticipantMessageListener1).onSwitchTo("theToken");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
HashMap<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedLocalParticipantMessageListener1, only()).onSwitchTo("theToken");
|
||||
verifyNoInteractions(mockedLocalParticipantMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLocalParticipantMessageListenerWhenHandlingExternalSignalingLocalParticipantMessage() {
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 =
|
||||
mock(SignalingMessageReceiver.LocalParticipantMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener2);
|
||||
return null;
|
||||
}).when(mockedLocalParticipantMessageListener1).onSwitchTo("theToken");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "switchto");
|
||||
eventMap.put("target", "room");
|
||||
HashMap<String, Object> switchToMap = new HashMap<>();
|
||||
switchToMap.put("roomid", "theToken");
|
||||
eventMap.put("switchto", switchToMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
InOrder inOrder = inOrder(mockedLocalParticipantMessageListener1, mockedLocalParticipantMessageListener2);
|
||||
|
||||
inOrder.verify(mockedLocalParticipantMessageListener1).onSwitchTo("theToken");
|
||||
inOrder.verify(mockedLocalParticipantMessageListener2).onSwitchTo("theToken");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverOfferTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOfferMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener((SignalingMessageReceiver.OfferMessageListener) null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferMessage() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener, only()).onOffer("theSessionId", "theRoomType", "theSdp", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferMessageWithNick() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedOfferMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedOfferMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener1 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener2 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener3 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedOfferMessageListener2);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener1, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
verify(mockedOfferMessageListener3, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
verifyNoInteractions(mockedOfferMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOfferMessageListenerWhenHandlingOffer() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener1 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener2 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener2);
|
||||
return null;
|
||||
}).when(mockedOfferMessageListener1).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener1);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener1, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
verifyNoInteractions(mockedOfferMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveOfferMessageListenerWhenHandlingOffer() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener1 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener2 =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedOfferMessageListener2);
|
||||
return null;
|
||||
}).when(mockedOfferMessageListener1).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener2);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
InOrder inOrder = inOrder(mockedOfferMessageListener1, mockedOfferMessageListener2);
|
||||
|
||||
inOrder.verify(mockedOfferMessageListener1).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
inOrder.verify(mockedOfferMessageListener2).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverParticipantListTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener((SignalingMessageReceiver.ParticipantListMessageListener) null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageUsersInRoom() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(2);
|
||||
Map<String, Object> user1 = new HashMap<>();
|
||||
user1.put("inCall", 7);
|
||||
user1.put("lastPing", 4815);
|
||||
user1.put("roomId", 108);
|
||||
user1.put("sessionId", "theSessionId1");
|
||||
user1.put("userId", "theUserId");
|
||||
// If any of the following properties is set in any of the participants all the other participants in the
|
||||
// message would have it too. But for test simplicity, and as it is not relevant for the processing, in this
|
||||
// test they are included only in one of the participants.
|
||||
user1.put("participantPermissions", 42);
|
||||
user1.put("actorType", "federated_users");
|
||||
user1.put("actorId", "theActorId");
|
||||
users.add(user1);
|
||||
Map<String, Object> user2 = new HashMap<>();
|
||||
user2.put("inCall", 0);
|
||||
user2.put("lastPing", 162342);
|
||||
user2.put("roomId", 108);
|
||||
user2.put("sessionId", "theSessionId2");
|
||||
user2.put("userId", "");
|
||||
users.add(user2);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant1 = new Participant();
|
||||
expectedParticipant1.setInCall(Participant.InCallFlags.IN_CALL | Participant.InCallFlags.WITH_AUDIO | Participant.InCallFlags.WITH_VIDEO);
|
||||
expectedParticipant1.setLastPing(4815);
|
||||
expectedParticipant1.setSessionId("theSessionId1");
|
||||
expectedParticipant1.setUserId("theUserId");
|
||||
expectedParticipant1.setActorType(Participant.ActorType.FEDERATED);
|
||||
expectedParticipant1.setActorId("theActorId");
|
||||
expectedParticipantList.add(expectedParticipant1);
|
||||
|
||||
Participant expectedParticipant2 = new Participant();
|
||||
expectedParticipant2.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant2.setLastPing(162342);
|
||||
expectedParticipant2.setSessionId("theSessionId2");
|
||||
expectedParticipantList.add(expectedParticipant2);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
verifyNoInteractions(mockedParticipantListMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener3 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onUsersInRoom(expectedParticipantList);
|
||||
verify(mockedParticipantListMessageListener3, only()).onUsersInRoom(expectedParticipantList);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWhenHandlingInternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onUsersInRoom(expectedParticipantList);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveParticipantListMessageListenerWhenHandlingInternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
InOrder inOrder = inOrder(mockedParticipantListMessageListener1, mockedParticipantListMessageListener2);
|
||||
|
||||
inOrder.verify(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
inOrder.verify(mockedParticipantListMessageListener2).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageParticipantsUpdate() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
Map<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
List<Map<String, Object>> users = new ArrayList<>(2);
|
||||
Map<String, Object> user1 = new HashMap<>();
|
||||
user1.put("inCall", 7);
|
||||
user1.put("lastPing", 4815);
|
||||
user1.put("sessionId", "theSessionId1");
|
||||
user1.put("participantType", 3);
|
||||
user1.put("userId", "theUserId");
|
||||
// If any of the following properties is set in any of the participants all the other participants in the
|
||||
// message would have it too. But for test simplicity, and as it is not relevant for the processing, in this
|
||||
// test they are included only in one of the participants.
|
||||
user1.put("nextcloudSessionId", "theNextcloudSessionId");
|
||||
user1.put("participantPermissions", 42);
|
||||
user1.put("actorType", "federated_users");
|
||||
user1.put("actorId", "theActorId");
|
||||
users.add(user1);
|
||||
Map<String, Object> user2 = new HashMap<>();
|
||||
user2.put("inCall", 0);
|
||||
user2.put("lastPing", 162342);
|
||||
user2.put("sessionId", "theSessionId2");
|
||||
user2.put("participantType", 4);
|
||||
users.add(user2);
|
||||
updateMap.put("users", users);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>(2);
|
||||
Participant expectedParticipant1 = new Participant();
|
||||
expectedParticipant1.setInCall(Participant.InCallFlags.IN_CALL | Participant.InCallFlags.WITH_AUDIO | Participant.InCallFlags.WITH_VIDEO);
|
||||
expectedParticipant1.setLastPing(4815);
|
||||
expectedParticipant1.setSessionId("theSessionId1");
|
||||
expectedParticipant1.setType(Participant.ParticipantType.USER);
|
||||
expectedParticipant1.setUserId("theUserId");
|
||||
expectedParticipant1.setActorType(Participant.ActorType.FEDERATED);
|
||||
expectedParticipant1.setActorId("theActorId");
|
||||
expectedParticipantList.add(expectedParticipant1);
|
||||
|
||||
Participant expectedParticipant2 = new Participant();
|
||||
expectedParticipant2.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant2.setLastPing(162342);
|
||||
expectedParticipant2.setSessionId("theSessionId2");
|
||||
expectedParticipant2.setType(Participant.ParticipantType.GUEST);
|
||||
expectedParticipantList.add(expectedParticipant2);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onParticipantsUpdate(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAllParticipantsUpdate() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
Map<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verifyNoInteractions(mockedParticipantListMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener3 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verify(mockedParticipantListMessageListener3, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWhenHandlingExternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveParticipantListMessageListenerWhenHandlingExternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("incall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
InOrder inOrder = inOrder(mockedParticipantListMessageListener1, mockedParticipantListMessageListener2);
|
||||
|
||||
inOrder.verify(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
inOrder.verify(mockedParticipantListMessageListener2).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferWithOfferAndWebRtcMessageListeners() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
InOrder inOrder = inOrder(mockedOfferMessageListener, mockedWebRtcMessageListener);
|
||||
|
||||
inOrder.verify(mockedOfferMessageListener).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
inOrder.verify(mockedWebRtcMessageListener).onOffer("theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddWebRtcMessageListenerWhenHandlingOffer() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
return null;
|
||||
}).when(mockedOfferMessageListener).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
InOrder inOrder = inOrder(mockedOfferMessageListener, mockedWebRtcMessageListener);
|
||||
|
||||
inOrder.verify(mockedOfferMessageListener).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
inOrder.verify(mockedWebRtcMessageListener).onOffer("theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveWebRtcMessageListenerWhenHandlingOffer() {
|
||||
SignalingMessageReceiver.OfferMessageListener mockedOfferMessageListener =
|
||||
mock(SignalingMessageReceiver.OfferMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedWebRtcMessageListener);
|
||||
return null;
|
||||
}).when(mockedOfferMessageListener).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
|
||||
signalingMessageReceiver.addListener(mockedOfferMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedOfferMessageListener, only()).onOffer("theSessionId", "theRoomType", "theSdp", "theNick");
|
||||
verifyNoInteractions(mockedWebRtcMessageListener);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverWebRtcTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddWebRtcMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(null, "theSessionId", "theRoomType");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddWebRtcMessageListenerWithNullSessionId() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, null, "theRoomType");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddWebRtcMessageListenerWithNullRoomType() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageOffer() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onOffer("theSdp", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageOfferWithNick() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("offer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("offer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onOffer("theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageAnswer() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("answer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("answer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onAnswer("theSdp", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageAnswerWithNick() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("answer");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
messagePayload.setType("answer");
|
||||
messagePayload.setSdp("theSdp");
|
||||
messagePayload.setNick("theNick");
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onAnswer("theSdp", "theNick");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageCandidate() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("candidate");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
NCMessagePayload messagePayload = new NCMessagePayload();
|
||||
NCIceCandidate iceCandidate = new NCIceCandidate();
|
||||
iceCandidate.setSdpMid("theSdpMid");
|
||||
iceCandidate.setSdpMLineIndex(42);
|
||||
iceCandidate.setCandidate("theSdp");
|
||||
messagePayload.setIceCandidate(iceCandidate);
|
||||
signalingMessage.setPayload(messagePayload);
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onCandidate("theSdpMid", 42, "theSdp");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageEndOfCandidates() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onEndOfCandidates();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageSeveralListenersSameFrom() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener1 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener2 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener1, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener2, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener1, only()).onEndOfCandidates();
|
||||
verify(mockedWebRtcMessageListener2, only()).onEndOfCandidates();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageNotMatchingSessionId() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("notMatchingSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedWebRtcMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageNotMatchingRoomType() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("notMatchingRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedWebRtcMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.removeListener(mockedWebRtcMessageListener);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedWebRtcMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener1 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener2 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener3 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener1, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener2, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener3, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.removeListener(mockedWebRtcMessageListener2);
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener1, only()).onEndOfCandidates();
|
||||
verify(mockedWebRtcMessageListener3, only()).onEndOfCandidates();
|
||||
verifyNoInteractions(mockedWebRtcMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebRtcMessageAfterAddingListenerAgainForDifferentFrom() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener, "theSessionId2", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verifyNoInteractions(mockedWebRtcMessageListener);
|
||||
|
||||
signalingMessage.setFrom("theSessionId2");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener, only()).onEndOfCandidates();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddWebRtcMessageListenerWhenHandlingWebRtcMessage() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener1 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener2 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener2, "theSessionId", "theRoomType");
|
||||
return null;
|
||||
}).when(mockedWebRtcMessageListener1).onEndOfCandidates();
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener1, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
verify(mockedWebRtcMessageListener1, only()).onEndOfCandidates();
|
||||
verifyNoInteractions(mockedWebRtcMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveWebRtcMessageListenerWhenHandlingWebRtcMessage() {
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener1 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
SignalingMessageReceiver.WebRtcMessageListener mockedWebRtcMessageListener2 =
|
||||
mock(SignalingMessageReceiver.WebRtcMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedWebRtcMessageListener2);
|
||||
return null;
|
||||
}).when(mockedWebRtcMessageListener1).onEndOfCandidates();
|
||||
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener1, "theSessionId", "theRoomType");
|
||||
signalingMessageReceiver.addListener(mockedWebRtcMessageListener2, "theSessionId", "theRoomType");
|
||||
|
||||
NCSignalingMessage signalingMessage = new NCSignalingMessage();
|
||||
signalingMessage.setFrom("theSessionId");
|
||||
signalingMessage.setType("endOfCandidates");
|
||||
signalingMessage.setRoomType("theRoomType");
|
||||
signalingMessageReceiver.processSignalingMessage(signalingMessage);
|
||||
|
||||
InOrder inOrder = inOrder(mockedWebRtcMessageListener1, mockedWebRtcMessageListener2);
|
||||
|
||||
inOrder.verify(mockedWebRtcMessageListener1).onEndOfCandidates();
|
||||
inOrder.verify(mockedWebRtcMessageListener2).onEndOfCandidates();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.test.fakes
|
||||
|
||||
import com.nextcloud.talk.models.domain.StartCallRecordingModel
|
||||
import com.nextcloud.talk.models.domain.StopCallRecordingModel
|
||||
import com.nextcloud.talk.repositories.callrecording.CallRecordingRepository
|
||||
import io.reactivex.Observable
|
||||
|
||||
class FakeCallRecordingRepository : CallRecordingRepository {
|
||||
|
||||
override fun startRecording(roomToken: String) = Observable.just(StartCallRecordingModel(true))
|
||||
|
||||
override fun stopRecording(roomToken: String) = Observable.just(StopCallRecordingModel(true))
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.test.fakes
|
||||
|
||||
import com.nextcloud.talk.models.domain.SearchMessageEntry
|
||||
import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository
|
||||
import io.reactivex.Observable
|
||||
|
||||
class FakeUnifiedSearchRepository : UnifiedSearchRepository {
|
||||
|
||||
lateinit var response: UnifiedSearchRepository.UnifiedSearchResults<SearchMessageEntry>
|
||||
var lastRequestedCursor = -1
|
||||
|
||||
override fun searchMessages(
|
||||
searchTerm: String,
|
||||
cursor: Int,
|
||||
limit: Int
|
||||
): Observable<UnifiedSearchRepository.UnifiedSearchResults<SearchMessageEntry>> {
|
||||
lastRequestedCursor = cursor
|
||||
return Observable.just(response)
|
||||
}
|
||||
|
||||
override fun searchInRoom(
|
||||
roomToken: String,
|
||||
searchTerm: String,
|
||||
cursor: Int,
|
||||
limit: Int
|
||||
): Observable<UnifiedSearchRepository.UnifiedSearchResults<SearchMessageEntry>> {
|
||||
lastRequestedCursor = cursor
|
||||
return Observable.just(response)
|
||||
}
|
||||
}
|
||||
90
app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt
Normal file
90
app/src/test/java/com/nextcloud/talk/utils/BundleKeysTest.kt
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Test
|
||||
class BundleKeysTest {
|
||||
|
||||
@Test
|
||||
fun testBundleKeysValues() {
|
||||
assertEquals("KEY_SELECTED_USERS", BundleKeys.KEY_SELECTED_USERS)
|
||||
assertEquals("KEY_SELECTED_GROUPS", BundleKeys.KEY_SELECTED_GROUPS)
|
||||
assertEquals("KEY_SELECTED_CIRCLES", BundleKeys.KEY_SELECTED_CIRCLES)
|
||||
assertEquals("KEY_SELECTED_EMAILS", BundleKeys.KEY_SELECTED_EMAILS)
|
||||
assertEquals("KEY_USERNAME", BundleKeys.KEY_USERNAME)
|
||||
assertEquals("KEY_TOKEN", BundleKeys.KEY_TOKEN)
|
||||
assertEquals("KEY_TRANSLATE_MESSAGE", BundleKeys.KEY_TRANSLATE_MESSAGE)
|
||||
assertEquals("KEY_BASE_URL", BundleKeys.KEY_BASE_URL)
|
||||
assertEquals("KEY_IS_ACCOUNT_IMPORT", BundleKeys.KEY_IS_ACCOUNT_IMPORT)
|
||||
assertEquals("KEY_ORIGINAL_PROTOCOL", BundleKeys.KEY_ORIGINAL_PROTOCOL)
|
||||
assertEquals("KEY_OPERATION_CODE", BundleKeys.KEY_OPERATION_CODE)
|
||||
assertEquals("KEY_APP_ITEM_PACKAGE_NAME", BundleKeys.KEY_APP_ITEM_PACKAGE_NAME)
|
||||
assertEquals("KEY_APP_ITEM_NAME", BundleKeys.KEY_APP_ITEM_NAME)
|
||||
assertEquals("KEY_CONVERSATION_PASSWORD", BundleKeys.KEY_CONVERSATION_PASSWORD)
|
||||
assertEquals("KEY_ROOM_TOKEN", BundleKeys.KEY_ROOM_TOKEN)
|
||||
assertEquals("KEY_ROOM_ONE_TO_ONE", BundleKeys.KEY_ROOM_ONE_TO_ONE)
|
||||
assertEquals("KEY_NEW_CONVERSATION", BundleKeys.KEY_NEW_CONVERSATION)
|
||||
assertEquals("KEY_ADD_PARTICIPANTS", BundleKeys.KEY_ADD_PARTICIPANTS)
|
||||
assertEquals("KEY_EXISTING_PARTICIPANTS", BundleKeys.KEY_EXISTING_PARTICIPANTS)
|
||||
assertEquals("KEY_CALL_URL", BundleKeys.KEY_CALL_URL)
|
||||
assertEquals("KEY_NEW_ROOM_NAME", BundleKeys.KEY_NEW_ROOM_NAME)
|
||||
assertEquals("KEY_MODIFIED_BASE_URL", BundleKeys.KEY_MODIFIED_BASE_URL)
|
||||
assertEquals("KEY_NOTIFICATION_SUBJECT", BundleKeys.KEY_NOTIFICATION_SUBJECT)
|
||||
assertEquals("KEY_NOTIFICATION_SIGNATURE", BundleKeys.KEY_NOTIFICATION_SIGNATURE)
|
||||
assertEquals("KEY_INTERNAL_USER_ID", BundleKeys.KEY_INTERNAL_USER_ID)
|
||||
assertEquals("KEY_CONVERSATION_TYPE", BundleKeys.KEY_CONVERSATION_TYPE)
|
||||
assertEquals("KEY_INVITED_PARTICIPANTS", BundleKeys.KEY_INVITED_PARTICIPANTS)
|
||||
assertEquals("KEY_INVITED_CIRCLE", BundleKeys.KEY_INVITED_CIRCLE)
|
||||
assertEquals("KEY_INVITED_GROUP", BundleKeys.KEY_INVITED_GROUP)
|
||||
assertEquals("KEY_INVITED_EMAIL", BundleKeys.KEY_INVITED_EMAIL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBundleKeysValues2() {
|
||||
assertEquals("KEY_CONVERSATION_NAME", BundleKeys.KEY_CONVERSATION_NAME)
|
||||
assertEquals("KEY_RECORDING_STATE", BundleKeys.KEY_RECORDING_STATE)
|
||||
assertEquals("KEY_CALL_VOICE_ONLY", BundleKeys.KEY_CALL_VOICE_ONLY)
|
||||
assertEquals("KEY_CALL_WITHOUT_NOTIFICATION", BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION)
|
||||
assertEquals("KEY_FROM_NOTIFICATION_START_CALL", BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)
|
||||
assertEquals("KEY_ROOM_ID", BundleKeys.KEY_ROOM_ID)
|
||||
assertEquals("KEY_ARE_CALL_SOUNDS", BundleKeys.KEY_ARE_CALL_SOUNDS)
|
||||
assertEquals("KEY_FILE_PATHS", BundleKeys.KEY_FILE_PATHS)
|
||||
assertEquals("KEY_ACCOUNT", BundleKeys.KEY_ACCOUNT)
|
||||
assertEquals("KEY_FILE_ID", BundleKeys.KEY_FILE_ID)
|
||||
assertEquals("KEY_NOTIFICATION_ID", BundleKeys.KEY_NOTIFICATION_ID)
|
||||
assertEquals("KEY_NOTIFICATION_TIMESTAMP", BundleKeys.KEY_NOTIFICATION_TIMESTAMP)
|
||||
assertEquals("KEY_SHARED_TEXT", BundleKeys.KEY_SHARED_TEXT)
|
||||
assertEquals("KEY_GEOCODING_QUERY", BundleKeys.KEY_GEOCODING_QUERY)
|
||||
assertEquals("KEY_META_DATA", BundleKeys.KEY_META_DATA)
|
||||
assertEquals("KEY_FORWARD_MSG_FLAG", BundleKeys.KEY_FORWARD_MSG_FLAG)
|
||||
assertEquals("KEY_FORWARD_MSG_TEXT", BundleKeys.KEY_FORWARD_MSG_TEXT)
|
||||
assertEquals("KEY_FORWARD_HIDE_SOURCE_ROOM", BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM)
|
||||
assertEquals("KEY_SYSTEM_NOTIFICATION_ID", BundleKeys.KEY_SYSTEM_NOTIFICATION_ID)
|
||||
assertEquals("KEY_MESSAGE_ID", BundleKeys.KEY_MESSAGE_ID)
|
||||
assertEquals("KEY_MIME_TYPE_FILTER", BundleKeys.KEY_MIME_TYPE_FILTER)
|
||||
assertEquals(
|
||||
"KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO",
|
||||
BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO
|
||||
)
|
||||
assertEquals(
|
||||
"KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO",
|
||||
BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO
|
||||
)
|
||||
assertEquals("KEY_IS_MODERATOR", BundleKeys.KEY_IS_MODERATOR)
|
||||
assertEquals("KEY_SWITCH_TO_ROOM", BundleKeys.KEY_SWITCH_TO_ROOM)
|
||||
assertEquals("KEY_START_CALL_AFTER_ROOM_SWITCH", BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH)
|
||||
assertEquals("KEY_IS_BREAKOUT_ROOM", BundleKeys.KEY_IS_BREAKOUT_ROOM)
|
||||
assertEquals("KEY_NOTIFICATION_RESTRICT_DELETION", BundleKeys.KEY_NOTIFICATION_RESTRICT_DELETION)
|
||||
assertEquals("KEY_DISMISS_RECORDING_URL", BundleKeys.KEY_DISMISS_RECORDING_URL)
|
||||
assertEquals("KEY_SHARE_RECORDING_TO_CHAT_URL", BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL)
|
||||
assertEquals("KEY_GEOCODING_RESULT", BundleKeys.KEY_GEOCODING_RESULT)
|
||||
assertEquals("ADD_ADDITIONAL_ACCOUNT", BundleKeys.ADD_ADDITIONAL_ACCOUNT)
|
||||
assertEquals("SAVED_TRANSLATED_MESSAGE", BundleKeys.SAVED_TRANSLATED_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.utils;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DoNotDisturbUtilsTest {
|
||||
|
||||
@Mock
|
||||
private Context context;
|
||||
|
||||
@Mock
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
@Mock
|
||||
private AudioManager audioManager;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(context.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(notificationManager);
|
||||
when(context.getSystemService(Context.AUDIO_SERVICE)).thenReturn(audioManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPlaySound_givenAndroidMAndInterruptionFilterNone_assertReturnsFalse() {
|
||||
DoNotDisturbUtils.INSTANCE.setTestingBuildVersion(Build.VERSION_CODES.M);
|
||||
|
||||
when(notificationManager.getCurrentInterruptionFilter()).thenReturn(NotificationManager.INTERRUPTION_FILTER_NONE);
|
||||
when(audioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
|
||||
|
||||
assertFalse("shouldPlaySound incorrectly returned true",
|
||||
DoNotDisturbUtils.INSTANCE.shouldPlaySound(context));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPlaySound_givenRingerModeNotNormal_assertReturnsFalse() throws Exception {
|
||||
DoNotDisturbUtils.INSTANCE.setTestingBuildVersion(Build.VERSION_CODES.LOLLIPOP);
|
||||
when(audioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
|
||||
|
||||
assertFalse("shouldPlaySound incorrectly returned true",
|
||||
DoNotDisturbUtils.INSTANCE.shouldPlaySound(context));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tim Krüger <t@timkrueger.me>
|
||||
* SPDX-FileCopyrightText: 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import junit.framework.TestCase
|
||||
import org.junit.Test
|
||||
|
||||
class ParticipantPermissionsTest : TestCase() {
|
||||
|
||||
@Test
|
||||
fun test_areFlagsSet() {
|
||||
val spreedCapability = SpreedCapability()
|
||||
val conversation = createConversation()
|
||||
|
||||
conversation.permissions = ParticipantPermissions.PUBLISH_SCREEN or
|
||||
ParticipantPermissions.JOIN_CALL or
|
||||
ParticipantPermissions.DEFAULT
|
||||
|
||||
val user = User()
|
||||
user.id = 1
|
||||
|
||||
val attendeePermissions =
|
||||
ParticipantPermissions(
|
||||
spreedCapability,
|
||||
ConversationModel.mapToConversationModel(conversation, user)
|
||||
)
|
||||
|
||||
assert(attendeePermissions.canPublishScreen)
|
||||
assert(attendeePermissions.canJoinCall)
|
||||
assert(attendeePermissions.isDefault)
|
||||
|
||||
assertFalse(attendeePermissions.isCustom)
|
||||
assertFalse(attendeePermissions.canStartCall())
|
||||
assertFalse(attendeePermissions.canIgnoreLobby())
|
||||
assertTrue(attendeePermissions.canPublishAudio())
|
||||
assertTrue(attendeePermissions.canPublishVideo())
|
||||
}
|
||||
|
||||
private fun createConversation() =
|
||||
Conversation(
|
||||
token = "test",
|
||||
name = "test",
|
||||
displayName = "test",
|
||||
description = "test",
|
||||
type = ConversationEnums.ConversationType.DUMMY,
|
||||
lastPing = 1,
|
||||
participantType = Participant.ParticipantType.DUMMY,
|
||||
hasPassword = true,
|
||||
sessionId = "test",
|
||||
actorId = "test",
|
||||
actorType = "test",
|
||||
password = "test",
|
||||
favorite = false,
|
||||
lastActivity = 1,
|
||||
unreadMessages = 1,
|
||||
unreadMention = false,
|
||||
lastMessage = null,
|
||||
objectType = ConversationEnums.ObjectType.DEFAULT,
|
||||
notificationLevel = ConversationEnums.NotificationLevel.ALWAYS,
|
||||
conversationReadOnlyState = ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_WRITE,
|
||||
lobbyState = ConversationEnums.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS,
|
||||
lobbyTimer = 1,
|
||||
lastReadMessage = 1,
|
||||
lastCommonReadMessage = 1,
|
||||
hasCall = true,
|
||||
callFlag = 1,
|
||||
canStartCall = false,
|
||||
canLeaveConversation = true,
|
||||
canDeleteConversation = true,
|
||||
unreadMentionDirect = true,
|
||||
notificationCalls = 1,
|
||||
permissions = 1,
|
||||
messageExpiration = 1,
|
||||
status = "test",
|
||||
statusIcon = "test",
|
||||
statusMessage = "test",
|
||||
statusClearAt = 1,
|
||||
callRecording = 1,
|
||||
avatarVersion = "test",
|
||||
hasCustomAvatar = true,
|
||||
callStartTime = 1,
|
||||
recordingConsentRequired = 1,
|
||||
remoteServer = "",
|
||||
remoteToken = ""
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class UserIdUtilsTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var user: User
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetIdForUser_if_userIsNull_returnsNoId() {
|
||||
Mockito.`when`(user.id).thenReturn(null)
|
||||
val result = UserIdUtils.getIdForUser(user)
|
||||
Assert.assertEquals("The id is NO_ID when user is null", UserIdUtils.NO_ID, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetIdForUser_if_userIdIsSet_returnsUserId() {
|
||||
val expectedId: Long = 12345
|
||||
Mockito.`when`(user.id).thenReturn(expectedId)
|
||||
val result = UserIdUtils.getIdForUser(user)
|
||||
Assert.assertEquals("The id is correct user id is not null", expectedId, result)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2023 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.viewmodels
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import io.reactivex.android.plugins.RxAndroidPlugins
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
|
||||
open class AbstractViewModelTest {
|
||||
@get:Rule
|
||||
val instantExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeClass
|
||||
fun setUpClass() {
|
||||
RxJavaPlugins.setIoSchedulerHandler {
|
||||
Schedulers.trampoline()
|
||||
}
|
||||
RxJavaPlugins.setComputationSchedulerHandler {
|
||||
Schedulers.trampoline()
|
||||
}
|
||||
RxJavaPlugins.setNewThreadSchedulerHandler {
|
||||
Schedulers.trampoline()
|
||||
}
|
||||
|
||||
RxAndroidPlugins.setInitMainThreadSchedulerHandler {
|
||||
Schedulers.trampoline()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.viewmodels
|
||||
|
||||
import com.nextcloud.talk.test.fakes.FakeCallRecordingRepository
|
||||
import com.vividsolutions.jts.util.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class CallRecordingViewModelTest : AbstractViewModelTest() {
|
||||
|
||||
private val repository = FakeCallRecordingRepository()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCallRecordingViewModel_clickStartRecord() {
|
||||
val viewModel = CallRecordingViewModel(repository)
|
||||
viewModel.setData("foo")
|
||||
viewModel.clickRecordButton()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartingState)
|
||||
|
||||
// fake to execute setRecordingState which would be triggered by signaling message
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartedState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCallRecordingViewModel_clickStopRecord() {
|
||||
val viewModel = CallRecordingViewModel(repository)
|
||||
viewModel.setData("foo")
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
|
||||
|
||||
Assert.equals(true, (viewModel.viewState.value as CallRecordingViewModel.RecordingStartedState).showStartedInfo)
|
||||
|
||||
viewModel.clickRecordButton()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingConfirmStopState)
|
||||
|
||||
viewModel.stopRecording()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStoppedState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCallRecordingViewModel_keepConfirmState() {
|
||||
val viewModel = CallRecordingViewModel(repository)
|
||||
viewModel.setData("foo")
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
|
||||
|
||||
Assert.equals(true, (viewModel.viewState.value as CallRecordingViewModel.RecordingStartedState).showStartedInfo)
|
||||
|
||||
viewModel.clickRecordButton()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingConfirmStopState)
|
||||
|
||||
viewModel.clickRecordButton()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingConfirmStopState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCallRecordingViewModel_continueRecordingWhenDismissStopDialog() {
|
||||
val viewModel = CallRecordingViewModel(repository)
|
||||
viewModel.setData("foo")
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
|
||||
viewModel.clickRecordButton()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingConfirmStopState)
|
||||
|
||||
viewModel.dismissStopRecording()
|
||||
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartedState)
|
||||
|
||||
Assert.equals(
|
||||
false,
|
||||
(viewModel.viewState.value as CallRecordingViewModel.RecordingStartedState).showStartedInfo
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetRecordingStateDirectly() {
|
||||
val viewModel = CallRecordingViewModel(repository)
|
||||
viewModel.setData("foo")
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STOPPED_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStoppedState)
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_AUDIO_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartedState)
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTED_VIDEO_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartedState)
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTING_AUDIO_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartingState)
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_STARTING_VIDEO_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingStartingState)
|
||||
|
||||
viewModel.setRecordingState(CallRecordingViewModel.RECORDING_FAILED_CODE)
|
||||
Assert.isTrue(viewModel.viewState.value is CallRecordingViewModel.RecordingErrorState)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.webrtc
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
class DataChannelMessageNotifierTest {
|
||||
|
||||
private lateinit var notifier: DataChannelMessageNotifier
|
||||
private lateinit var listener: PeerConnectionWrapper.DataChannelMessageListener
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
notifier = DataChannelMessageNotifier()
|
||||
listener = mock(PeerConnectionWrapper.DataChannelMessageListener::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddListener() {
|
||||
notifier.addListener(listener)
|
||||
assertTrue(notifier.dataChannelMessageListeners.contains(listener))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveListener() {
|
||||
notifier.addListener(listener)
|
||||
notifier.removeListener(listener)
|
||||
assertFalse(notifier.dataChannelMessageListeners.contains(listener))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyAudioOn() {
|
||||
notifier.addListener(listener)
|
||||
notifier.notifyAudioOn()
|
||||
verify(listener).onAudioOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyAudioOff() {
|
||||
notifier.addListener(listener)
|
||||
notifier.notifyAudioOff()
|
||||
verify(listener).onAudioOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyVideoOn() {
|
||||
notifier.addListener(listener)
|
||||
notifier.notifyVideoOn()
|
||||
verify(listener).onVideoOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyVideoOff() {
|
||||
notifier.addListener(listener)
|
||||
notifier.notifyVideoOff()
|
||||
verify(listener).onVideoOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyNickChanged() {
|
||||
notifier.addListener(listener)
|
||||
val newNick = "NewNick"
|
||||
notifier.notifyNickChanged(newNick)
|
||||
verify(listener).onNickChanged(newNick)
|
||||
}
|
||||
}
|
||||
27
app/src/test/java/com/nextcloud/talk/webrtc/GlobalsTest.kt
Normal file
27
app/src/test/java/com/nextcloud/talk/webrtc/GlobalsTest.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.webrtc
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class GlobalsTest {
|
||||
@Test
|
||||
fun testRoomToken() {
|
||||
Assert.assertEquals("roomToken", Globals.ROOM_TOKEN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTargetParticipants() {
|
||||
Assert.assertEquals("participants", Globals.TARGET_PARTICIPANTS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTargetRoom() {
|
||||
Assert.assertEquals("room", Globals.TARGET_ROOM)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.webrtc
|
||||
|
||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper.PeerConnectionObserver
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.webrtc.MediaStream
|
||||
import org.webrtc.PeerConnection
|
||||
|
||||
class PeerConnectionNotifierTest {
|
||||
private var notifier: PeerConnectionNotifier? = null
|
||||
private var observer1: PeerConnectionObserver? = null
|
||||
private var observer2: PeerConnectionObserver? = null
|
||||
private var mockStream: MediaStream? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
notifier = PeerConnectionNotifier()
|
||||
observer1 = Mockito.mock(PeerConnectionObserver::class.java)
|
||||
observer2 = Mockito.mock(PeerConnectionObserver::class.java)
|
||||
mockStream = Mockito.mock(MediaStream::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddObserver() {
|
||||
notifier!!.addObserver(observer1)
|
||||
notifier!!.notifyStreamAdded(mockStream)
|
||||
Mockito.verify(observer1)?.onStreamAdded(mockStream)
|
||||
Mockito.verify(observer2, Mockito.never())?.onStreamAdded(mockStream)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveObserver() {
|
||||
notifier!!.addObserver(observer1)
|
||||
notifier!!.addObserver(observer2)
|
||||
notifier!!.removeObserver(observer1)
|
||||
notifier!!.notifyStreamAdded(mockStream)
|
||||
Mockito.verify(observer1, Mockito.never())?.onStreamAdded(mockStream)
|
||||
Mockito.verify(observer2)?.onStreamAdded(mockStream)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyStreamAdded() {
|
||||
notifier!!.addObserver(observer1)
|
||||
notifier!!.notifyStreamAdded(mockStream)
|
||||
Mockito.verify(observer1)?.onStreamAdded(mockStream)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyStreamRemoved() {
|
||||
notifier!!.addObserver(observer1)
|
||||
notifier!!.notifyStreamRemoved(mockStream)
|
||||
Mockito.verify(observer1)?.onStreamRemoved(mockStream)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifyIceConnectionStateChanged() {
|
||||
notifier!!.addObserver(observer1)
|
||||
notifier!!.notifyIceConnectionStateChanged(PeerConnection.IceConnectionState.CONNECTED)
|
||||
Mockito.verify(observer1)?.onIceConnectionStateChanged(PeerConnection.IceConnectionState.CONNECTED)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,760 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.webrtc
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||
import com.nextcloud.talk.signaling.SignalingMessageReceiver
|
||||
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper.DataChannelMessageListener
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatcher
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.argThat
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.atLeast
|
||||
import org.mockito.Mockito.atMostOnce
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.doNothing
|
||||
import org.mockito.Mockito.inOrder
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.invocation.InvocationOnMock
|
||||
import org.mockito.stubbing.Answer
|
||||
import org.webrtc.DataChannel
|
||||
import org.webrtc.MediaConstraints
|
||||
import org.webrtc.PeerConnection
|
||||
import org.webrtc.PeerConnectionFactory
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.HashMap
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
@Suppress("LongMethod", "TooGenericExceptionCaught")
|
||||
class PeerConnectionWrapperTest {
|
||||
|
||||
private var peerConnectionWrapper: PeerConnectionWrapper? = null
|
||||
private var mockedPeerConnection: PeerConnection? = null
|
||||
private var mockedPeerConnectionFactory: PeerConnectionFactory? = null
|
||||
private var mockedSignalingMessageReceiver: SignalingMessageReceiver? = null
|
||||
private var mockedSignalingMessageSender: SignalingMessageSender? = null
|
||||
|
||||
/**
|
||||
* Helper answer for DataChannel methods.
|
||||
*/
|
||||
private class ReturnValueOrThrowIfDisposed<T>(val value: T) : Answer<T> {
|
||||
override fun answer(currentInvocation: InvocationOnMock): T {
|
||||
if (Mockito.mockingDetails(currentInvocation.mock).invocations.find {
|
||||
it!!.method.name === "dispose"
|
||||
} !== null
|
||||
) {
|
||||
throw IllegalStateException("DataChannel has been disposed")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper matcher for DataChannelMessages.
|
||||
*/
|
||||
private inner class MatchesDataChannelMessage(private val expectedDataChannelMessage: DataChannelMessage) :
|
||||
ArgumentMatcher<DataChannel.Buffer> {
|
||||
override fun matches(buffer: DataChannel.Buffer): Boolean {
|
||||
// DataChannel.Buffer does not implement "equals", so the comparison needs to be done on the ByteBuffer
|
||||
// instead.
|
||||
return dataChannelMessageToBuffer(expectedDataChannelMessage).data.equals(buffer.data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dataChannelMessageToBuffer(dataChannelMessage: DataChannelMessage) =
|
||||
DataChannel.Buffer(
|
||||
ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).toByteArray()),
|
||||
false
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockedPeerConnection = Mockito.mock(PeerConnection::class.java)
|
||||
mockedPeerConnectionFactory = Mockito.mock(PeerConnectionFactory::class.java)
|
||||
mockedSignalingMessageReceiver = Mockito.mock(SignalingMessageReceiver::class.java)
|
||||
mockedSignalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessage() {
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
any(PeerConnection.Observer::class.java)
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("the-message-type"))
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).send(
|
||||
argThat(MatchesDataChannelMessage(DataChannelMessage("the-message-type")))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageWithOpenRemoteDataChannel() {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val mockedRandomIdDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedRandomIdDataChannel.label()).thenReturn("random-id")
|
||||
Mockito.`when`(mockedRandomIdDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
peerConnectionObserverArgumentCaptor.value.onDataChannel(mockedRandomIdDataChannel)
|
||||
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("the-message-type"))
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).send(
|
||||
argThat(MatchesDataChannelMessage(DataChannelMessage("the-message-type")))
|
||||
)
|
||||
Mockito.verify(mockedRandomIdDataChannel, never()).send(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageBeforeOpeningDataChannel() {
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
any(PeerConnection.Observer::class.java)
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.CONNECTING)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
val statusDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
doNothing().`when`(mockedStatusDataChannel).registerObserver(statusDataChannelObserverArgumentCaptor.capture())
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("the-message-type"))
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("another-message-type"))
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel, never()).send(any())
|
||||
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
statusDataChannelObserverArgumentCaptor.value.onStateChange()
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).send(
|
||||
argThat(MatchesDataChannelMessage(DataChannelMessage("the-message-type")))
|
||||
)
|
||||
Mockito.verify(mockedStatusDataChannel).send(
|
||||
argThat(MatchesDataChannelMessage(DataChannelMessage("another-message-type")))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageBeforeOpeningDataChannelWithDifferentThreads() {
|
||||
// A brute force approach is used to test race conditions between different threads just repeating the test
|
||||
// several times. Due to this the test passing could be a false positive, as it could have been a matter of
|
||||
// luck, but even if the test may wrongly pass sometimes it is better than nothing (although, in general, with
|
||||
// that number of reruns, it fails when it should).
|
||||
for (i in 1..1000) {
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
any(PeerConnection.Observer::class.java)
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.CONNECTING)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
val statusDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
doNothing().`when`(mockedStatusDataChannel)
|
||||
.registerObserver(statusDataChannelObserverArgumentCaptor.capture())
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val dataChannelMessageCount = 5
|
||||
|
||||
val sendThread = thread {
|
||||
for (j in 1..dataChannelMessageCount) {
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("the-message-type-$j"))
|
||||
}
|
||||
}
|
||||
|
||||
// Exceptions thrown in threads are not propagated to the main thread, so it needs to be explicitly done
|
||||
// (for example, for ConcurrentModificationExceptions when iterating over the data channel messages).
|
||||
var exceptionOnStateChange: Exception? = null
|
||||
|
||||
val openDataChannelThread = thread {
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
|
||||
try {
|
||||
statusDataChannelObserverArgumentCaptor.value.onStateChange()
|
||||
} catch (e: Exception) {
|
||||
exceptionOnStateChange = e
|
||||
}
|
||||
}
|
||||
|
||||
sendThread.join()
|
||||
openDataChannelThread.join()
|
||||
|
||||
if (exceptionOnStateChange !== null) {
|
||||
throw exceptionOnStateChange!!
|
||||
}
|
||||
|
||||
val inOrder = inOrder(mockedStatusDataChannel)
|
||||
|
||||
for (j in 1..dataChannelMessageCount) {
|
||||
inOrder.verify(mockedStatusDataChannel).send(
|
||||
argThat(MatchesDataChannelMessage(DataChannelMessage("the-message-type-$j")))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReceiveDataChannelMessage() {
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
any(PeerConnection.Observer::class.java)
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
val statusDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
doNothing().`when`(mockedStatusDataChannel).registerObserver(statusDataChannelObserverArgumentCaptor.capture())
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val mockedDataChannelMessageListener = Mockito.mock(DataChannelMessageListener::class.java)
|
||||
peerConnectionWrapper!!.addListener(mockedDataChannelMessageListener)
|
||||
|
||||
// The payload must be a map to be able to serialize it and, therefore, generate the data that would have been
|
||||
// received from another participant, so it is not possible to test receiving the nick as a String payload.
|
||||
val payloadMap = HashMap<String, String>()
|
||||
payloadMap["name"] = "the-nick-in-map"
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("nickChanged", null, payloadMap))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onNickChanged("the-nick-in-map")
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOn"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onAudioOn()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOff"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onAudioOff()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("videoOn"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onVideoOn()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("videoOff"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onVideoOff()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReceiveDataChannelMessageWithOpenRemoteDataChannel() {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
val statusDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
doNothing().`when`(mockedStatusDataChannel).registerObserver(statusDataChannelObserverArgumentCaptor.capture())
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val randomIdDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
val mockedRandomIdDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedRandomIdDataChannel.label()).thenReturn("random-id")
|
||||
Mockito.`when`(mockedRandomIdDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
doNothing().`when`(mockedRandomIdDataChannel).registerObserver(
|
||||
randomIdDataChannelObserverArgumentCaptor.capture()
|
||||
)
|
||||
peerConnectionObserverArgumentCaptor.value.onDataChannel(mockedRandomIdDataChannel)
|
||||
|
||||
val mockedDataChannelMessageListener = Mockito.mock(DataChannelMessageListener::class.java)
|
||||
peerConnectionWrapper!!.addListener(mockedDataChannelMessageListener)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOn"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onAudioOn()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
|
||||
randomIdDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOff"))
|
||||
)
|
||||
|
||||
Mockito.verify(mockedDataChannelMessageListener).onAudioOff()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePeerConnectionWithOpenRemoteDataChannel() {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenReturn("status")
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val mockedRandomIdDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedRandomIdDataChannel.label()).thenReturn("random-id")
|
||||
Mockito.`when`(mockedRandomIdDataChannel.state()).thenReturn(DataChannel.State.OPEN)
|
||||
peerConnectionObserverArgumentCaptor.value.onDataChannel(mockedRandomIdDataChannel)
|
||||
|
||||
peerConnectionWrapper!!.removePeerConnection()
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).dispose()
|
||||
Mockito.verify(mockedRandomIdDataChannel).dispose()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePeerConnectionWhileAddingRemoteDataChannelsWithDifferentThreads() {
|
||||
// A brute force approach is used to test race conditions between different threads just repeating the test
|
||||
// several times. Due to this the test passing could be a false positive, as it could have been a matter of
|
||||
// luck, but even if the test may wrongly pass sometimes it is better than nothing (although, in general, with
|
||||
// that number of reruns, it fails when it should).
|
||||
for (i in 1..1000) {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenAnswer(ReturnValueOrThrowIfDisposed("status"))
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenAnswer(
|
||||
ReturnValueOrThrowIfDisposed(DataChannel.State.OPEN)
|
||||
)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val dataChannelCount = 5
|
||||
|
||||
val mockedRandomIdDataChannels: MutableList<DataChannel> = ArrayList()
|
||||
val dataChannelObservers: MutableList<DataChannel.Observer?> = ArrayList()
|
||||
for (j in 0..<dataChannelCount) {
|
||||
mockedRandomIdDataChannels.add(Mockito.mock(DataChannel::class.java))
|
||||
// Add data channels with duplicated labels (from the second data channel and onwards) to test that
|
||||
// they are correctly disposed also in that case (which should not happen anyway, but just in case).
|
||||
Mockito.`when`(mockedRandomIdDataChannels[j].label())
|
||||
.thenAnswer(ReturnValueOrThrowIfDisposed("random-id-" + ((j + 1) / 2)))
|
||||
Mockito.`when`(mockedRandomIdDataChannels[j].state())
|
||||
.thenAnswer(ReturnValueOrThrowIfDisposed(DataChannel.State.OPEN))
|
||||
|
||||
// Store a reference to the registered observer, if any, to be called after the registration. The call
|
||||
// is done outside the mock to better simulate the normal behaviour, as it would not be called during
|
||||
// the registration itself.
|
||||
dataChannelObservers.add(null)
|
||||
doAnswer { invocation ->
|
||||
if (Mockito.mockingDetails(invocation.mock).invocations.find {
|
||||
it!!.method.name === "dispose"
|
||||
} !== null
|
||||
) {
|
||||
throw IllegalStateException("DataChannel has been disposed")
|
||||
}
|
||||
|
||||
dataChannelObservers[j] = invocation.getArgument(0, DataChannel.Observer::class.java)
|
||||
|
||||
null
|
||||
}.`when`(mockedRandomIdDataChannels[j]).registerObserver(any())
|
||||
}
|
||||
|
||||
val onDataChannelThread = thread {
|
||||
// Add again "status" data channel to test that it is correctly disposed also in that case (which
|
||||
// should not happen anyway even if it was added by the remote peer, but just in case)
|
||||
peerConnectionObserverArgumentCaptor.value.onDataChannel(mockedStatusDataChannel)
|
||||
|
||||
for (j in 0..<dataChannelCount) {
|
||||
peerConnectionObserverArgumentCaptor.value.onDataChannel(mockedRandomIdDataChannels[j])
|
||||
|
||||
// Call "onStateChange" on the registered observer to simulate that the data channel was opened.
|
||||
dataChannelObservers[j]?.onStateChange()
|
||||
}
|
||||
}
|
||||
|
||||
// Exceptions thrown in threads are not propagated to the main thread, so it needs to be explicitly done
|
||||
// (for example, for ConcurrentModificationExceptions when iterating over the data channels).
|
||||
var exceptionRemovePeerConnection: Exception? = null
|
||||
|
||||
val removePeerConnectionThread = thread {
|
||||
try {
|
||||
peerConnectionWrapper!!.removePeerConnection()
|
||||
} catch (e: Exception) {
|
||||
exceptionRemovePeerConnection = e
|
||||
}
|
||||
}
|
||||
|
||||
onDataChannelThread.join()
|
||||
removePeerConnectionThread.join()
|
||||
|
||||
if (exceptionRemovePeerConnection !== null) {
|
||||
throw exceptionRemovePeerConnection!!
|
||||
}
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).dispose()
|
||||
for (j in 0..<dataChannelCount) {
|
||||
Mockito.verify(mockedRandomIdDataChannels[j]).dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePeerConnectionWhileSendingWithDifferentThreads() {
|
||||
// A brute force approach is used to test race conditions between different threads just repeating the test
|
||||
// several times. Due to this the test passing could be a false positive, as it could have been a matter of
|
||||
// luck, but even if the test may wrongly pass sometimes it is better than nothing (although, in general, with
|
||||
// that number of reruns, it fails when it should).
|
||||
for (i in 1..1000) {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenAnswer(ReturnValueOrThrowIfDisposed("status"))
|
||||
Mockito.`when`(mockedStatusDataChannel.state())
|
||||
.thenAnswer(ReturnValueOrThrowIfDisposed(DataChannel.State.OPEN))
|
||||
Mockito.`when`(mockedStatusDataChannel.send(any())).thenAnswer(ReturnValueOrThrowIfDisposed(true))
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val dataChannelMessageCount = 5
|
||||
|
||||
// Exceptions thrown in threads are not propagated to the main thread, so it needs to be explicitly done
|
||||
// (for example, for IllegalStateExceptions when using a disposed data channel).
|
||||
var exceptionSend: Exception? = null
|
||||
|
||||
val sendThread = thread {
|
||||
try {
|
||||
for (j in 0..<dataChannelMessageCount) {
|
||||
peerConnectionWrapper!!.send(DataChannelMessage("the-message-type-$j"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
exceptionSend = e
|
||||
}
|
||||
}
|
||||
|
||||
val removePeerConnectionThread = thread {
|
||||
peerConnectionWrapper!!.removePeerConnection()
|
||||
}
|
||||
|
||||
sendThread.join()
|
||||
removePeerConnectionThread.join()
|
||||
|
||||
if (exceptionSend !== null) {
|
||||
throw exceptionSend!!
|
||||
}
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).registerObserver(any())
|
||||
Mockito.verify(mockedStatusDataChannel).dispose()
|
||||
Mockito.verify(mockedStatusDataChannel, atLeast(0)).label()
|
||||
Mockito.verify(mockedStatusDataChannel, atLeast(0)).state()
|
||||
Mockito.verify(mockedStatusDataChannel, atLeast(0)).send(any())
|
||||
Mockito.verifyNoMoreInteractions(mockedStatusDataChannel)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemovePeerConnectionWhileReceivingWithDifferentThreads() {
|
||||
// A brute force approach is used to test race conditions between different threads just repeating the test
|
||||
// several times. Due to this the test passing could be a false positive, as it could have been a matter of
|
||||
// luck, but even if the test may wrongly pass sometimes it is better than nothing (although, in general, with
|
||||
// that number of reruns, it fails when it should).
|
||||
for (i in 1..1000) {
|
||||
val peerConnectionObserverArgumentCaptor: ArgumentCaptor<PeerConnection.Observer> =
|
||||
ArgumentCaptor.forClass(PeerConnection.Observer::class.java)
|
||||
|
||||
Mockito.`when`(
|
||||
mockedPeerConnectionFactory!!.createPeerConnection(
|
||||
any(PeerConnection.RTCConfiguration::class.java),
|
||||
peerConnectionObserverArgumentCaptor.capture()
|
||||
)
|
||||
).thenReturn(mockedPeerConnection)
|
||||
|
||||
val mockedStatusDataChannel = Mockito.mock(DataChannel::class.java)
|
||||
Mockito.`when`(mockedStatusDataChannel.label()).thenAnswer(ReturnValueOrThrowIfDisposed("status"))
|
||||
Mockito.`when`(mockedStatusDataChannel.state()).thenAnswer(
|
||||
ReturnValueOrThrowIfDisposed(DataChannel.State.OPEN)
|
||||
)
|
||||
Mockito.`when`(mockedPeerConnection!!.createDataChannel(eq("status"), any()))
|
||||
.thenReturn(mockedStatusDataChannel)
|
||||
|
||||
val statusDataChannelObserverArgumentCaptor: ArgumentCaptor<DataChannel.Observer> =
|
||||
ArgumentCaptor.forClass(DataChannel.Observer::class.java)
|
||||
|
||||
doNothing().`when`(mockedStatusDataChannel)
|
||||
.registerObserver(statusDataChannelObserverArgumentCaptor.capture())
|
||||
|
||||
peerConnectionWrapper = PeerConnectionWrapper(
|
||||
mockedPeerConnectionFactory,
|
||||
ArrayList<PeerConnection.IceServer>(),
|
||||
MediaConstraints(),
|
||||
"the-session-id",
|
||||
"the-local-session-id",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
"video",
|
||||
mockedSignalingMessageReceiver,
|
||||
mockedSignalingMessageSender
|
||||
)
|
||||
|
||||
val mockedDataChannelMessageListener = Mockito.mock(DataChannelMessageListener::class.java)
|
||||
peerConnectionWrapper!!.addListener(mockedDataChannelMessageListener)
|
||||
|
||||
// Exceptions thrown in threads are not propagated to the main thread, so it needs to be explicitly done
|
||||
// (for example, for IllegalStateExceptions when using a disposed data channel).
|
||||
var exceptionOnMessage: Exception? = null
|
||||
|
||||
val onMessageThread = thread {
|
||||
try {
|
||||
// It is assumed that, even if its data channel was disposed, its buffers can be used while there
|
||||
// is a reference to them, so no special mock behaviour is added to throw an exception in that case.
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOn"))
|
||||
)
|
||||
|
||||
statusDataChannelObserverArgumentCaptor.value.onMessage(
|
||||
dataChannelMessageToBuffer(DataChannelMessage("audioOff"))
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
exceptionOnMessage = e
|
||||
}
|
||||
}
|
||||
|
||||
val removePeerConnectionThread = thread {
|
||||
peerConnectionWrapper!!.removePeerConnection()
|
||||
}
|
||||
|
||||
onMessageThread.join()
|
||||
removePeerConnectionThread.join()
|
||||
|
||||
if (exceptionOnMessage !== null) {
|
||||
throw exceptionOnMessage!!
|
||||
}
|
||||
|
||||
Mockito.verify(mockedStatusDataChannel).registerObserver(any())
|
||||
Mockito.verify(mockedStatusDataChannel).dispose()
|
||||
Mockito.verify(mockedStatusDataChannel, atLeast(0)).label()
|
||||
Mockito.verify(mockedStatusDataChannel, atLeast(0)).state()
|
||||
Mockito.verifyNoMoreInteractions(mockedStatusDataChannel)
|
||||
Mockito.verify(mockedDataChannelMessageListener, atMostOnce()).onAudioOn()
|
||||
Mockito.verify(mockedDataChannelMessageListener, atMostOnce()).onAudioOff()
|
||||
Mockito.verifyNoMoreInteractions(mockedDataChannelMessageListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue