Add SignalingMessageReceiver class to listen to signaling messages

For now only WebRTC messages can be listened to, although it will be
extended with other kinds later.

This commit only introduces the base class, although it is not used yet
anywhere; a concrete implementation will be added in a following commit.

The test class is named "SignalingMessageReceiverWebRtcTest" rather than
just "SignalingMessageReceiverTest" to have smaller, more manageable
test classes for each listener kind rather than one large test class for
all of them.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2022-10-17 10:49:20 +02:00
parent 0e36002036
commit 45787caf0a
3 changed files with 708 additions and 0 deletions

View file

@ -0,0 +1,222 @@
/*
* Nextcloud Talk application
*
* @author Daniel Calviño Sánchez
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
/**
* Hub to register listeners for signaling messages of different kinds.
*
* Adding and removing listeners, as well as notifying them is internally synchronized. This should be kept in mind
* if listeners are added or removed when handling an event to prevent deadlocks (nevertheless, just adding or
* removing a listener in the same thread handling the event is fine, and in most cases it will be fine too if done
* in a different thread, as long as the notifier thread is not forced to wait until the listener is added or removed).
*
* SignalingMessageReceiver does not fetch the signaling messages itself; subclasses must fetch them and then call
* the appropriate protected methods to process the messages and notify the listeners.
*/
public abstract class SignalingMessageReceiver {
/**
* Listener for WebRTC messages.
*
* The messages are bound to a specific peer connection, so each listener is expected to handle messages only for
* a single peer connection.
*/
public interface WebRtcMessageListener {
void onOffer(String sdp, String nick);
void onAnswer(String sdp, String nick);
void onCandidate(String sdpMid, int sdpMLineIndex, String sdp);
void onEndOfCandidates();
}
private final WebRtcMessageNotifier webRtcMessageNotifier = new WebRtcMessageNotifier();
/**
* Adds a listener for WebRTC messages from the given session ID and room type.
*
* A listener is expected to be added only once. If the same listener is added again it will no longer be notified
* for the messages from the previous session ID or room type.
*
* @param listener the WebRtcMessageListener
* @param sessionId the ID of the session that messages come from
* @param roomType the room type that messages come from
*/
public void addListener(WebRtcMessageListener listener, String sessionId, String roomType) {
webRtcMessageNotifier.addListener(listener, sessionId, roomType);
}
public void removeListener(WebRtcMessageListener listener) {
webRtcMessageNotifier.removeListener(listener);
}
protected void processSignalingMessage(NCSignalingMessage signalingMessage) {
// Note that in the internal signaling server message "data" is the String representation of a JSON
// object, although it is already decoded when used here.
String type = signalingMessage.getType();
String sessionId = signalingMessage.getFrom();
String roomType = signalingMessage.getRoomType();
if ("offer".equals(type)) {
// Message schema (external signaling server):
// {
// "type": "message",
// "message": {
// "sender": {
// ...
// },
// "data": {
// "to": #STRING#,
// "from": #STRING#,
// "type": "offer",
// "roomType": #STRING#, // "video" or "screen"
// "payload": {
// "type": "offer",
// "sdp": #STRING#,
// },
// "sid": #STRING#, // external signaling server >= 0.5.0
// },
// },
// }
//
// Message schema (internal signaling server):
// {
// "type": "message",
// "data": {
// "to": #STRING#,
// "sid": #STRING#,
// "roomType": #STRING#, // "video" or "screen"
// "type": "offer",
// "payload": {
// "type": "offer",
// "sdp": #STRING#,
// "nick": #STRING#, // Optional
// },
// "from": #STRING#,
// },
// }
NCMessagePayload payload = signalingMessage.getPayload();
if (payload == null) {
// Broken message, this should not happen.
return;
}
String sdp = payload.getSdp();
String nick = payload.getNick();
webRtcMessageNotifier.notifyOffer(sessionId, roomType, sdp, nick);
return;
}
if ("answer".equals(type)) {
// Message schema: same as offers, but with type "answer".
NCMessagePayload payload = signalingMessage.getPayload();
if (payload == null) {
// Broken message, this should not happen.
return;
}
String sdp = payload.getSdp();
String nick = payload.getNick();
webRtcMessageNotifier.notifyAnswer(sessionId, roomType, sdp, nick);
return;
}
if ("candidate".equals(type)) {
// Message schema (external signaling server):
// {
// "type": "message",
// "message": {
// "sender": {
// ...
// },
// "data": {
// "to": #STRING#,
// "from": #STRING#,
// "type": "candidate",
// "roomType": #STRING#, // "video" or "screen"
// "payload": {
// "candidate": {
// "candidate": #STRING#,
// "sdpMid": #STRING#,
// "sdpMLineIndex": #INTEGER#,
// },
// },
// "sid": #STRING#, // external signaling server >= 0.5.0
// },
// },
// }
//
// Message schema (internal signaling server):
// {
// "type": "message",
// "data": {
// "to": #STRING#,
// "sid": #STRING#,
// "roomType": #STRING#, // "video" or "screen"
// "type": "candidate",
// "payload": {
// "candidate": {
// "candidate": #STRING#,
// "sdpMid": #STRING#,
// "sdpMLineIndex": #INTEGER#,
// },
// },
// "from": #STRING#,
// },
// }
NCMessagePayload payload = signalingMessage.getPayload();
if (payload == null) {
// Broken message, this should not happen.
return;
}
NCIceCandidate ncIceCandidate = payload.getIceCandidate();
if (ncIceCandidate == null) {
// Broken message, this should not happen.
return;
}
webRtcMessageNotifier.notifyCandidate(sessionId,
roomType,
ncIceCandidate.getSdpMid(),
ncIceCandidate.getSdpMLineIndex(),
ncIceCandidate.getCandidate());
return;
}
if ("endOfCandidates".equals(type)) {
webRtcMessageNotifier.notifyEndOfCandidates(sessionId, roomType);
return;
}
}
}

View file

@ -0,0 +1,120 @@
/*
* Nextcloud Talk application
*
* @author Daniel Calviño Sánchez
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.signaling;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Helper class to register and notify WebRtcMessageListeners.
*
* This class is only meant for internal use by SignalingMessageReceiver; listeners must register themselves against
* a SignalingMessageReceiver rather than against a WebRtcMessageNotifier.
*/
class WebRtcMessageNotifier {
/**
* Helper class to associate a WebRtcMessageListener with a session ID and room type.
*/
private static class WebRtcMessageListenerFrom {
public final SignalingMessageReceiver.WebRtcMessageListener listener;
public final String sessionId;
public final String roomType;
private WebRtcMessageListenerFrom(SignalingMessageReceiver.WebRtcMessageListener listener,
String sessionId,
String roomType) {
this.listener = listener;
this.sessionId = sessionId;
this.roomType = roomType;
}
}
private final List<WebRtcMessageListenerFrom> webRtcMessageListenersFrom = new ArrayList<>();
public synchronized void addListener(SignalingMessageReceiver.WebRtcMessageListener listener, String sessionId, String roomType) {
if (listener == null) {
throw new IllegalArgumentException("WebRtcMessageListener can not be null");
}
if (sessionId == null) {
throw new IllegalArgumentException("sessionId can not be null");
}
if (roomType == null) {
throw new IllegalArgumentException("roomType can not be null");
}
removeListener(listener);
webRtcMessageListenersFrom.add(new WebRtcMessageListenerFrom(listener, sessionId, roomType));
}
public synchronized void removeListener(SignalingMessageReceiver.WebRtcMessageListener listener) {
Iterator<WebRtcMessageListenerFrom> it = webRtcMessageListenersFrom.iterator();
while (it.hasNext()) {
WebRtcMessageListenerFrom listenerFrom = it.next();
if (listenerFrom.listener == listener) {
it.remove();
return;
}
}
}
private List<SignalingMessageReceiver.WebRtcMessageListener> getListenersFor(String sessionId, String roomType) {
List<SignalingMessageReceiver.WebRtcMessageListener> webRtcMessageListeners =
new ArrayList<>(webRtcMessageListenersFrom.size());
for (WebRtcMessageListenerFrom listenerFrom : webRtcMessageListenersFrom) {
if (listenerFrom.sessionId.equals(sessionId) && listenerFrom.roomType.equals(roomType)) {
webRtcMessageListeners.add(listenerFrom.listener);
}
}
return webRtcMessageListeners;
}
public synchronized void notifyOffer(String sessionId, String roomType, String sdp, String nick) {
for (SignalingMessageReceiver.WebRtcMessageListener listener : getListenersFor(sessionId, roomType)) {
listener.onOffer(sdp, nick);
}
}
public synchronized void notifyAnswer(String sessionId, String roomType, String sdp, String nick) {
for (SignalingMessageReceiver.WebRtcMessageListener listener : getListenersFor(sessionId, roomType)) {
listener.onAnswer(sdp, nick);
}
}
public synchronized void notifyCandidate(String sessionId, String roomType, String sdpMid, int sdpMLineIndex, String sdp) {
for (SignalingMessageReceiver.WebRtcMessageListener listener : getListenersFor(sessionId, roomType)) {
listener.onCandidate(sdpMid, sdpMLineIndex, sdp);
}
}
public synchronized void notifyEndOfCandidates(String sessionId, String roomType) {
for (SignalingMessageReceiver.WebRtcMessageListener listener : getListenersFor(sessionId, roomType)) {
listener.onEndOfCandidates();
}
}
}

View file

@ -0,0 +1,366 @@
/*
* Nextcloud Talk application
*
* @author Daniel Calviño Sánchez
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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();
}
}