Merge pull request #1600 from WhisperSystems/mkirk/webrtc/unit-test-peerconnectionclient

test peerconnectionclient
pull/1/head
Michael Kirk 8 years ago committed by GitHub
commit 7b33cbb933

@ -52,6 +52,7 @@
456F6E231E24133500FD2210 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DF2041E0D74AC003D14BE /* Platform.swift */; };
456F6E241E24133E00FD2210 /* CallKitCallUIAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F659721E1BD99C00444429 /* CallKitCallUIAdaptee.swift */; };
456F6E251E24216100FD2210 /* DataChannelMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */; };
456F6E2F1E261D1000FD2210 /* PeerConnectionClientTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */; };
4574A5D61DD6704700C6B692 /* CallService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4574A5D51DD6704700C6B692 /* CallService.swift */; };
45794E861E00620000066731 /* CallUIAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45794E851E00620000066731 /* CallUIAdapter.swift */; };
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
@ -634,6 +635,7 @@
45666F7A1D9C0533008FE134 /* OWSDatabaseMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigration.m; path = Migrations/OWSDatabaseMigration.m; sourceTree = "<group>"; };
45666F7C1D9C0814008FE134 /* OWSDatabaseMigrationRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigrationRunner.h; path = Migrations/OWSDatabaseMigrationRunner.h; sourceTree = "<group>"; };
45666F7D1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigrationRunner.m; path = Migrations/OWSDatabaseMigrationRunner.m; sourceTree = "<group>"; };
456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerConnectionClientTest.swift; sourceTree = "<group>"; };
4574A5D51DD6704700C6B692 /* CallService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallService.swift; sourceTree = "<group>"; };
45794E851E00620000066731 /* CallUIAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CallUIAdapter.swift; path = UserInterface/CallUIAdapter.swift; sourceTree = "<group>"; };
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = "<group>"; };
@ -2146,6 +2148,7 @@
B660F6731C29867F00687D6E /* call */ = {
isa = PBXGroup;
children = (
456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */,
B660F6741C29867F00687D6E /* RecentCallTest.m */,
);
path = call;
@ -3240,6 +3243,7 @@
B660F70D1C29988E00687D6E /* AnonymousAudioCallbackHandler.m in Sources */,
B660F70E1C29988E00687D6E /* RemoteIOAudio.m in Sources */,
B660F70F1C29988E00687D6E /* RemoteIOBufferListWrapper.m in Sources */,
456F6E2F1E261D1000FD2210 /* PeerConnectionClientTest.swift in Sources */,
B660F7101C29988E00687D6E /* SpeexCodec.m in Sources */,
B660F7111C29988E00687D6E /* SoundBoard.m in Sources */,
B660F7121C29988E00687D6E /* SoundInstance.m in Sources */,

@ -75,7 +75,7 @@ enum CallError: Error {
// FIXME TODO do we need to timeout?
fileprivate let timeoutSeconds = 60
@objc class CallService: NSObject, RTCDataChannelDelegate, RTCPeerConnectionDelegate {
@objc class CallService: NSObject, PeerConnectionClientDelegate {
// MARK: - Properties
@ -167,11 +167,13 @@ fileprivate let timeoutSeconds = 60
return getIceServers().then(on: CallService.signalingQueue) { iceServers -> Promise<HardenedRTCSessionDescription> in
Logger.debug("\(self.TAG) got ice servers:\(iceServers)")
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, peerConnectionDelegate: self)
self.peerConnectionClient = peerConnectionClient
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self)
// When placing an outgoing call, it's our responsibility to create the DataChannel. Recipient will not have
// to do this explicitly.
peerConnectionClient.createSignalingDataChannel()
// When calling, it's our responsibility to create the DataChannel. Receivers will not have to do this explicitly.
self.peerConnectionClient!.createSignalingDataChannel(delegate: self)
self.peerConnectionClient = peerConnectionClient
return self.peerConnectionClient!.createOffer()
}.then(on: CallService.signalingQueue) { (sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> in
@ -315,7 +317,7 @@ fileprivate let timeoutSeconds = 60
}.then(on: CallService.signalingQueue) { (iceServers: [RTCIceServer]) -> Promise<HardenedRTCSessionDescription> in
// FIXME for first time call recipients I think we'll see mic/camera permission requests here,
// even though, from the users perspective, no incoming call is yet visible.
self.peerConnectionClient = PeerConnectionClient(iceServers: iceServers, peerConnectionDelegate: self)
self.peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self)
let offerSessionDescription = RTCSessionDescription(type: .offer, sdp: callerSessionDescription)
let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
@ -323,7 +325,6 @@ fileprivate let timeoutSeconds = 60
// Find a sessionDescription compatible with my constraints and the remote sessionDescription
return self.peerConnectionClient!.negotiateSessionDescription(remoteDescription: offerSessionDescription, constraints: constraints)
}.then(on: CallService.signalingQueue) { (negotiatedSessionDescription: HardenedRTCSessionDescription) in
// TODO? WebRtcCallService.this.lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
Logger.debug("\(self.TAG) set the remote description")
let answerMessage = OWSCallAnswerMessage(callId: newCall.signalingId, sessionDescription: negotiatedSessionDescription.sdp)
@ -784,6 +785,46 @@ fileprivate let timeoutSeconds = 60
}
}
// MARK: - PeerConnectionClientDelegate
/**
* The connection has been established. The clients can now communicate.
*/
func peerConnectionClientIceConnected(_ peerconnectionClient: PeerConnectionClient) {
CallService.signalingQueue.async {
self.handleIceConnected()
}
}
/**
* The connection failed to establish. The clients will not be able to communicate.
*/
func peerConnectionClientIceFailed(_ peerconnectionClient: PeerConnectionClient) {
CallService.signalingQueue.async {
self.handleFailedCall(error: CallError.disconnected)
}
}
/**
* During the Signaling process each client generates IceCandidates locally, which contain information about how to
* reach the local client via the internet. The delegate must shuttle these IceCandates to the other (remote) client
* out of band, as part of establishing a connection over WebRTC.
*/
func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, addedLocalIceCandidate iceCandidate: RTCIceCandidate) {
CallService.signalingQueue.async {
self.handleLocalAddedIceCandidate(iceCandidate)
}
}
/**
* Once the peerconnection is established, we can receive messages via the data channel, and notify the delegate.
*/
func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, received dataChannelMessage: OWSWebRTCProtosData) {
CallService.signalingQueue.async {
self.handleDataChannelMessage(dataChannelMessage)
}
}
// MARK: Helpers
/**
@ -799,7 +840,8 @@ fileprivate let timeoutSeconds = 60
}
/**
*
* RTCIceServers are used when attempting to establish an optimal connection to the other party. SignalService supplies
* a list of servers, plus we have fallback servers hardcoded in the app.
*/
private func getIceServers() -> Promise<[RTCIceServer]> {
return firstly {
@ -817,6 +859,11 @@ fileprivate let timeoutSeconds = 60
return RTCIceServer(urlStrings: [url])
}
} + [CallService.fallbackIceServer]
}.recover { error -> [RTCIceServer] in
Logger.error("\(self.TAG) fetching ICE servers failed with error: \(error)")
Logger.warn("\(self.TAG) using fallback ICE Servers")
return [CallService.fallbackIceServer]
}
}
@ -831,38 +878,16 @@ fileprivate let timeoutSeconds = 60
terminateCall()
}
/**
* Clean up any existing call state and get ready to receive a new call.
*/
private func terminateCall() {
assertOnSignalingQueue()
Logger.debug("\(TAG) in \(#function)")
// lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
// NotificationBarManager.setCallEnded(this);
//
// incomingRinger.stop();
// outgoingRinger.stop();
// outgoingRinger.playDisconnected();
//
// if (peerConnection != null) {
// peerConnection.dispose();
// peerConnection = null;
// }
//
// if (eglBase != null && localRenderer != null && remoteRenderer != null) {
// localRenderer.release();
// remoteRenderer.release();
// eglBase.release();
// }
//
// shutdownAudio();
//
// this.callState = CallState.STATE_IDLE;
// this.recipient = null;
// this.callId = null;
// this.audioEnabled = false;
// this.videoEnabled = false;
// this.pendingIceUpdates = null;
// lockManager.updatePhoneState(LockManager.PhoneState.IDLE);
peerConnectionClient?.delegate = nil
peerConnectionClient?.terminate()
peerConnectionClient = nil
call = nil
thread = nil
@ -870,166 +895,6 @@ fileprivate let timeoutSeconds = 60
sendIceUpdatesImmediately = true
pendingIceUpdateMessages = []
}
// MARK: - RTCDataChannelDelegate
/** The data channel state changed. */
public func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
Logger.debug("\(TAG) dataChannelDidChangeState: \(dataChannel)")
// SignalingQueue.dispatch.async {}
}
/** The data channel successfully received a data buffer. */
public func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
Logger.debug("\(TAG) dataChannel didReceiveMessageWith buffer:\(buffer)")
guard let dataChannelMessage = OWSWebRTCProtosData.parse(from:buffer.data) else {
// TODO can't proto parsings throw an exception? Is it just being lost in the Objc->Swift?
Logger.error("\(TAG) failed to parse dataProto")
return
}
CallService.signalingQueue.async {
self.handleDataChannelMessage(dataChannelMessage)
}
}
/** The data channel's |bufferedAmount| changed. */
public func dataChannel(_ dataChannel: RTCDataChannel, didChangeBufferedAmount amount: UInt64) {
Logger.debug("\(TAG) didChangeBufferedAmount: \(amount)")
}
// MARK: - RTCPeerConnectionDelegate
/** Called when the SignalingState changed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
Logger.debug("\(TAG) didChange signalingState:\(stateChanged.debugDescription)")
}
/** Called when media is received on a new stream from remote peer. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
Logger.debug("\(TAG) didAdd stream:\(stream)")
}
/** Called when a remote peer closes a stream. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
Logger.debug("\(TAG) didRemove Stream:\(stream)")
}
/** Called when negotiation is needed, for example ICE has restarted. */
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
Logger.debug("\(TAG) shouldNegotiate")
}
/** Called any time the IceConnectionState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
Logger.debug("\(TAG) didChange IceConnectionState:\(newState.debugDescription)")
CallService.signalingQueue.async {
switch newState {
case .connected, .completed:
self.handleIceConnected()
case .failed:
Logger.warn("\(self.TAG) RTCIceConnection failed.")
guard self.thread != nil else {
Logger.error("\(self.TAG) refusing to hangup for failed IceConnection because there is no current thread")
return
}
self.handleFailedCall(error: CallError.disconnected)
default:
Logger.debug("\(self.TAG) ignoring change IceConnectionState:\(newState.debugDescription)")
}
}
}
/** Called any time the IceGatheringState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
Logger.debug("\(TAG) didChange IceGatheringState:\(newState.debugDescription)")
}
/** New ice candidate has been found. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
Logger.debug("\(TAG) didGenerate IceCandidate:\(candidate.sdp)")
CallService.signalingQueue.async {
self.handleLocalAddedIceCandidate(candidate)
}
}
/** Called when a group of local Ice candidates have been removed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
Logger.debug("\(TAG) didRemove IceCandidates:\(candidates)")
}
/** New data channel has been opened. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
Logger.debug("\(TAG) didOpen dataChannel:\(dataChannel)")
CallService.signalingQueue.async {
guard let peerConnectionClient = self.peerConnectionClient else {
Logger.error("\(self.TAG) surprised to find nil peerConnectionClient in \(#function)")
return
}
Logger.debug("\(self.TAG) set dataChannel")
peerConnectionClient.dataChannel = dataChannel
}
}
}
// Mark: Pretty Print Objc enums.
fileprivate extension RTCSignalingState {
var debugDescription: String {
switch self {
case .stable:
return "stable"
case .haveLocalOffer:
return "haveLocalOffer"
case .haveLocalPrAnswer:
return "haveLocalPrAnswer"
case .haveRemoteOffer:
return "haveRemoteOffer"
case .haveRemotePrAnswer:
return "haveRemotePrAnswer"
case .closed:
return "closed"
}
}
}
fileprivate extension RTCIceGatheringState {
var debugDescription: String {
switch self {
case .new:
return "new"
case .gathering:
return "gathering"
case .complete:
return "complete"
}
}
}
fileprivate extension RTCIceConnectionState {
var debugDescription: String {
switch self {
case .new:
return "new"
case .checking:
return "checking"
case .connected:
return "connected"
case .completed:
return "completed"
case .failed:
return "failed"
case .disconnected:
return "disconnected"
case .closed:
return "closed"
case .count:
return "count"
}
}
}
fileprivate extension MessageSender {

@ -8,14 +8,41 @@ import WebRTC
let kAudioTrackType = kRTCMediaStreamTrackKindAudio
let kVideoTrackType = kRTCMediaStreamTrackKindVideo
/**
* The PeerConnectionClient notifies it's delegate (the CallService) of key events in the call signaling life cycle
*/
protocol PeerConnectionClientDelegate: class {
/**
* The connection has been established. The clients can now communicate.
*/
func peerConnectionClientIceConnected(_ peerconnectionClient: PeerConnectionClient)
/**
* The connection failed to establish. The clients will not be able to communicate.
*/
func peerConnectionClientIceFailed(_ peerconnectionClient: PeerConnectionClient)
/**
* During the Signaling process each client generates IceCandidates locally, which contain information about how to
* reach the local client via the internet. The delegate must shuttle these IceCandates to the other (remote) client
* out of band, as part of establishing a connection over WebRTC.
*/
func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, addedLocalIceCandidate iceCandidate: RTCIceCandidate)
/**
* Once the peerconnection is established, we can receive messages via the data channel, and notify the delegate.
*/
func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, received dataChannelMessage: OWSWebRTCProtosData)
}
/**
* `PeerConnectionClient` is our interface to WebRTC.
*
* It is primarily a wrapper around `RTCPeerConnection`, which is responsible for sending and receiving our call data
* including audio, video, and some signaling - though the bulk of the signaling is *establishing* the connection,
* meaning we can't use the connection to transmit yet.
* including audio, video, and some post-connected signaling (hangup, add video)
*/
class PeerConnectionClient: NSObject {
class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelDelegate {
let TAG = "[PeerConnectionClient]"
enum Identifiers: String {
@ -25,9 +52,12 @@ class PeerConnectionClient: NSObject {
dataChannelSignaling = "signaling"
}
// Delegate is notified of key events in the call lifecycle.
public weak var delegate: PeerConnectionClientDelegate!
// Connection
private let peerConnection: RTCPeerConnection
internal var peerConnection: RTCPeerConnection!
private let iceServers: [RTCIceServer]
private let connectionConstraints: RTCMediaConstraints
private let configuration: RTCConfiguration
@ -51,8 +81,9 @@ class PeerConnectionClient: NSObject {
private var videoTrack: RTCVideoTrack?
private var cameraConstraints: RTCMediaConstraints
init(iceServers: [RTCIceServer], peerConnectionDelegate: RTCPeerConnectionDelegate) {
init(iceServers: [RTCIceServer], delegate: PeerConnectionClientDelegate) {
self.iceServers = iceServers
self.delegate = delegate
configuration = RTCConfiguration()
configuration.iceServers = iceServers
@ -61,24 +92,25 @@ class PeerConnectionClient: NSObject {
let connectionConstraintsDict = ["DtlsSrtpKeyAgreement": "true"]
connectionConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: connectionConstraintsDict)
peerConnection = factory.peerConnection(with: configuration,
constraints: connectionConstraints,
delegate: peerConnectionDelegate)
audioConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints:nil)
cameraConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
super.init()
peerConnection = factory.peerConnection(with: configuration,
constraints: connectionConstraints,
delegate: self)
createAudioSender()
createVideoSender()
}
// MARK: - Media Streams
public func createSignalingDataChannel(delegate: RTCDataChannelDelegate) {
public func createSignalingDataChannel() {
let dataChannel = peerConnection.dataChannel(forLabel: Identifiers.dataChannelSignaling.rawValue,
configuration: RTCDataChannelConfiguration())
dataChannel.delegate = delegate
dataChannel.delegate = self
self.dataChannel = dataChannel
}
@ -255,16 +287,18 @@ class PeerConnectionClient: NSObject {
// audioTrack is a strong property because we need access to it to mute/unmute, but I was seeing it
// become nil when it was only a weak property. So we retain it and manually nil the reference here, because
// we are likely to crash if we retain any peer connection properties when the peerconnection is released
Logger.debug("\(TAG) in \(#function)")
audioTrack = nil
videoTrack = nil
dataChannel = nil
audioSender = nil
videoSender = nil
peerConnection.delegate = nil
peerConnection.close()
}
// MARK: Data Channel
// MARK: - Data Channel
func sendDataChannelMessage(data: Data) -> Bool {
guard let dataChannel = self.dataChannel else {
@ -275,6 +309,92 @@ class PeerConnectionClient: NSObject {
let buffer = RTCDataBuffer(data: data, isBinary: false)
return dataChannel.sendData(buffer)
}
// MARK: RTCDataChannelDelegate
/** The data channel state changed. */
public func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
Logger.debug("\(TAG) dataChannelDidChangeState: \(dataChannel)")
}
/** The data channel successfully received a data buffer. */
public func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
Logger.debug("\(TAG) dataChannel didReceiveMessageWith buffer:\(buffer)")
guard let dataChannelMessage = OWSWebRTCProtosData.parse(from:buffer.data) else {
// TODO can't proto parsings throw an exception? Is it just being lost in the Objc->Swift?
Logger.error("\(TAG) failed to parse dataProto")
return
}
delegate.peerConnectionClient(self, received: dataChannelMessage)
}
/** The data channel's |bufferedAmount| changed. */
public func dataChannel(_ dataChannel: RTCDataChannel, didChangeBufferedAmount amount: UInt64) {
Logger.debug("\(TAG) didChangeBufferedAmount: \(amount)")
}
// MARK: - RTCPeerConnectionDelegate
/** Called when the SignalingState changed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
Logger.debug("\(TAG) didChange signalingState:\(stateChanged.debugDescription)")
}
/** Called when media is received on a new stream from remote peer. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
Logger.debug("\(TAG) didAdd stream:\(stream)")
}
/** Called when a remote peer closes a stream. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
Logger.debug("\(TAG) didRemove Stream:\(stream)")
}
/** Called when negotiation is needed, for example ICE has restarted. */
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
Logger.debug("\(TAG) shouldNegotiate")
}
/** Called any time the IceConnectionState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
Logger.debug("\(TAG) didChange IceConnectionState:\(newState.debugDescription)")
switch newState {
case .connected, .completed:
self.delegate.peerConnectionClientIceConnected(self)
case .failed:
Logger.warn("\(self.TAG) RTCIceConnection failed.")
self.delegate.peerConnectionClientIceFailed(self)
default:
Logger.debug("\(self.TAG) ignoring change IceConnectionState:\(newState.debugDescription)")
}
}
/** Called any time the IceGatheringState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
Logger.debug("\(TAG) didChange IceGatheringState:\(newState.debugDescription)")
}
/** New ice candidate has been found. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
Logger.debug("\(TAG) didGenerate IceCandidate:\(candidate.sdp)")
self.delegate.peerConnectionClient(self, addedLocalIceCandidate: candidate)
}
/** Called when a group of local Ice candidates have been removed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
Logger.debug("\(TAG) didRemove IceCandidates:\(candidates)")
}
/** New data channel has been opened. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
Logger.debug("\(TAG) didOpen dataChannel:\(dataChannel)")
CallService.signalingQueue.async {
Logger.debug("\(self.TAG) set dataChannel")
self.dataChannel = dataChannel
}
}
}
/**
@ -304,3 +424,60 @@ class HardenedRTCSessionDescription {
return RTCSessionDescription.init(type: rtcSessionDescription.type, sdp: description)
}
}
// Mark: Pretty Print Objc enums.
fileprivate extension RTCSignalingState {
var debugDescription: String {
switch self {
case .stable:
return "stable"
case .haveLocalOffer:
return "haveLocalOffer"
case .haveLocalPrAnswer:
return "haveLocalPrAnswer"
case .haveRemoteOffer:
return "haveRemoteOffer"
case .haveRemotePrAnswer:
return "haveRemotePrAnswer"
case .closed:
return "closed"
}
}
}
fileprivate extension RTCIceGatheringState {
var debugDescription: String {
switch self {
case .new:
return "new"
case .gathering:
return "gathering"
case .complete:
return "complete"
}
}
}
fileprivate extension RTCIceConnectionState {
var debugDescription: String {
switch self {
case .new:
return "new"
case .checking:
return "checking"
case .connected:
return "connected"
case .completed:
return "completed"
case .failed:
return "failed"
case .disconnected:
return "disconnected"
case .closed:
return "closed"
case .count:
return "count"
}
}
}

@ -98,7 +98,6 @@ final class CallKitCallManager: NSObject {
}
}
fileprivate extension Array {
mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows {
@ -108,5 +107,4 @@ fileprivate extension Array {
remove(at: index)
}
}

@ -42,7 +42,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
return providerConfiguration
}
init(callService: CallService, notificationsAdapter: CallNotificationsAdapter) {
init(callService: CallService, notificationsAdapter: CallNotificationsAdapter) {
self.callManager = CallKitCallManager()
self.callService = callService
self.notificationsAdapter = notificationsAdapter

@ -0,0 +1,103 @@
// Created by Michael Kirk on 1/11/17.
// Copyright © 2017 Open Whisper Systems. All rights reserved.
import XCTest
import WebRTC
/**
* Playing the role of the call service.
*/
class FakePeerConnectionClientDelegate: PeerConnectionClientDelegate {
enum ConnectionState {
case connected, failed
}
var connectionState: ConnectionState?
var localIceCandidates = [RTCIceCandidate]()
var dataChannelMessages = [OWSWebRTCProtosData]()
internal func peerConnectionClientIceConnected(_ peerconnectionClient: PeerConnectionClient) {
connectionState = .connected
}
internal func peerConnectionClientIceFailed(_ peerconnectionClient: PeerConnectionClient) {
connectionState = .failed
}
internal func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, addedLocalIceCandidate iceCandidate: RTCIceCandidate) {
localIceCandidates.append(iceCandidate)
}
internal func peerConnectionClient(_ peerconnectionClient: PeerConnectionClient, received dataChannelMessage: OWSWebRTCProtosData) {
dataChannelMessages.append(dataChannelMessage)
}
}
class PeerConnectionClientTest: XCTestCase {
var client: PeerConnectionClient!
var clientDelegate: FakePeerConnectionClientDelegate!
var peerConnection: RTCPeerConnection!
var dataChannel: RTCDataChannel!
override func setUp() {
super.setUp()
let iceServers = [RTCIceServer]()
clientDelegate = FakePeerConnectionClientDelegate()
client = PeerConnectionClient(iceServers: iceServers, delegate: clientDelegate)
peerConnection = client.peerConnection
client.createSignalingDataChannel()
dataChannel = client.dataChannel!
}
override func tearDown() {
client.terminate()
super.tearDown()
}
func testIceConnectionStateChange() {
XCTAssertNil(clientDelegate.connectionState)
client.peerConnection(peerConnection, didChange: RTCIceConnectionState.connected)
XCTAssertEqual(FakePeerConnectionClientDelegate.ConnectionState.connected, clientDelegate.connectionState)
client.peerConnection(peerConnection, didChange: RTCIceConnectionState.completed)
XCTAssertEqual(FakePeerConnectionClientDelegate.ConnectionState.connected, clientDelegate.connectionState)
client.peerConnection(peerConnection, didChange: RTCIceConnectionState.failed)
XCTAssertEqual(FakePeerConnectionClientDelegate.ConnectionState.failed, clientDelegate.connectionState)
}
func testIceCandidateAdded() {
XCTAssertEqual(0, clientDelegate.localIceCandidates.count)
let candidate1 = RTCIceCandidate(sdp: "sdp-1", sdpMLineIndex: 0, sdpMid: "sdpMid-1")
let candidate2 = RTCIceCandidate(sdp: "sdp-2", sdpMLineIndex: 0, sdpMid: "sdpMid-2")
let candidate3 = RTCIceCandidate(sdp: "sdp-3", sdpMLineIndex: 0, sdpMid: "sdpMid-3")
client.peerConnection(peerConnection, didGenerate: candidate1)
client.peerConnection(peerConnection, didGenerate: candidate2)
client.peerConnection(peerConnection, didGenerate: candidate3)
XCTAssertEqual(3, clientDelegate.localIceCandidates.count)
}
func testDataChannelMessage() {
XCTAssertEqual(0, clientDelegate.dataChannelMessages.count)
let hangup = DataChannelMessage.forHangup(callId: 123)
let hangupBuffer = RTCDataBuffer(data: hangup.asData(), isBinary: false)
client.dataChannel(dataChannel, didReceiveMessageWith: hangupBuffer)
XCTAssertEqual(1, clientDelegate.dataChannelMessages.count)
let dataChannelMessageProto = clientDelegate.dataChannelMessages[0]
XCTAssert(dataChannelMessageProto.hasHangup())
let hangupProto = dataChannelMessageProto.hangup!
XCTAssertEqual(123, hangupProto.id)
}
}
Loading…
Cancel
Save