| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2013 Google Inc. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 * this list of conditions and the following disclaimer in the documentation | |
| 12 * and/or other materials provided with the distribution. | |
| 13 * 3. The name of the author may not be used to endorse or promote products | |
| 14 * derived from this software without specific prior written permission. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 #import <Foundation/Foundation.h> | |
| 29 | |
| 30 #import "RTCICEServer.h" | |
| 31 #import "RTCMediaConstraints.h" | |
| 32 #import "RTCMediaStream.h" | |
| 33 #import "RTCPair.h" | |
| 34 #import "RTCPeerConnection.h" | |
| 35 #import "RTCPeerConnectionFactory.h" | |
| 36 #import "RTCPeerConnectionSyncObserver.h" | |
| 37 #import "RTCSessionDescription.h" | |
| 38 #import "RTCSessionDescriptionSyncObserver.h" | |
| 39 #import "RTCVideoRenderer.h" | |
| 40 #import "RTCVideoTrack.h" | |
| 41 | |
| 42 #include "webrtc/base/gunit.h" | |
| 43 #include "webrtc/base/ssladapter.h" | |
| 44 | |
| 45 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
| 46 #error "This file requires ARC support." | |
| 47 #endif | |
| 48 | |
| 49 const NSTimeInterval kRTCPeerConnectionTestTimeout = 20; | |
| 50 | |
| 51 @interface RTCFakeRenderer : NSObject <RTCVideoRenderer> | |
| 52 @end | |
| 53 | |
| 54 @implementation RTCFakeRenderer | |
| 55 | |
| 56 - (void)setSize:(CGSize)size {} | |
| 57 - (void)renderFrame:(RTCI420Frame*)frame {} | |
| 58 | |
| 59 @end | |
| 60 | |
| 61 @interface RTCPeerConnectionTest : NSObject | |
| 62 | |
| 63 // Returns whether the two sessions are of the same type. | |
| 64 + (BOOL)isSession:(RTCSessionDescription*)session1 | |
| 65 ofSameTypeAsSession:(RTCSessionDescription*)session2; | |
| 66 | |
| 67 // Create and add tracks to pc, with the given source, label, and IDs | |
| 68 - (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc | |
| 69 withFactory:(RTCPeerConnectionFactory*)factory | |
| 70 videoSource:(RTCVideoSource*)videoSource | |
| 71 streamLabel:(NSString*)streamLabel | |
| 72 videoTrackID:(NSString*)videoTrackID | |
| 73 audioTrackID:(NSString*)audioTrackID; | |
| 74 | |
| 75 - (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory; | |
| 76 | |
| 77 @end | |
| 78 | |
| 79 @implementation RTCPeerConnectionTest | |
| 80 | |
| 81 + (BOOL)isSession:(RTCSessionDescription*)session1 | |
| 82 ofSameTypeAsSession:(RTCSessionDescription*)session2 { | |
| 83 return [session1.type isEqual:session2.type]; | |
| 84 } | |
| 85 | |
| 86 - (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc | |
| 87 withFactory:(RTCPeerConnectionFactory*)factory | |
| 88 videoSource:(RTCVideoSource*)videoSource | |
| 89 streamLabel:(NSString*)streamLabel | |
| 90 videoTrackID:(NSString*)videoTrackID | |
| 91 audioTrackID:(NSString*)audioTrackID { | |
| 92 RTCMediaStream* localMediaStream = [factory mediaStreamWithLabel:streamLabel]; | |
| 93 // TODO(zeke): Fix this test to create a fake video capturer so that a track | |
| 94 // can be created. | |
| 95 if (videoSource) { | |
| 96 RTCVideoTrack* videoTrack = | |
| 97 [factory videoTrackWithID:videoTrackID source:videoSource]; | |
| 98 RTCFakeRenderer* videoRenderer = [[RTCFakeRenderer alloc] init]; | |
| 99 [videoTrack addRenderer:videoRenderer]; | |
| 100 [localMediaStream addVideoTrack:videoTrack]; | |
| 101 // Test that removal/re-add works. | |
| 102 [localMediaStream removeVideoTrack:videoTrack]; | |
| 103 [localMediaStream addVideoTrack:videoTrack]; | |
| 104 } | |
| 105 RTCAudioTrack* audioTrack = [factory audioTrackWithID:audioTrackID]; | |
| 106 [localMediaStream addAudioTrack:audioTrack]; | |
| 107 [pc addStream:localMediaStream]; | |
| 108 return localMediaStream; | |
| 109 } | |
| 110 | |
| 111 - (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory { | |
| 112 NSArray* mandatory = @[ | |
| 113 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"], | |
| 114 [[RTCPair alloc] initWithKey:@"internalSctpDataChannels" value:@"true"], | |
| 115 ]; | |
| 116 RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] init]; | |
| 117 RTCMediaConstraints* pcConstraints = | |
| 118 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory | |
| 119 optionalConstraints:nil]; | |
| 120 | |
| 121 RTCPeerConnectionSyncObserver* offeringExpectations = | |
| 122 [[RTCPeerConnectionSyncObserver alloc] init]; | |
| 123 RTCPeerConnection* pcOffer = | |
| 124 [factory peerConnectionWithICEServers:nil | |
| 125 constraints:pcConstraints | |
| 126 delegate:offeringExpectations]; | |
| 127 | |
| 128 RTCPeerConnectionSyncObserver* answeringExpectations = | |
| 129 [[RTCPeerConnectionSyncObserver alloc] init]; | |
| 130 | |
| 131 RTCPeerConnection* pcAnswer = | |
| 132 [factory peerConnectionWithICEServers:nil | |
| 133 constraints:pcConstraints | |
| 134 delegate:answeringExpectations]; | |
| 135 // TODO(hughv): Create video capturer | |
| 136 RTCVideoCapturer* capturer = nil; | |
| 137 RTCVideoSource* videoSource = | |
| 138 [factory videoSourceWithCapturer:capturer constraints:constraints]; | |
| 139 | |
| 140 // Here and below, "oLMS" refers to offerer's local media stream, and "aLMS" | |
| 141 // refers to the answerer's local media stream, with suffixes of "a0" and "v0" | |
| 142 // for audio and video tracks, resp. These mirror chrome historical naming. | |
| 143 RTCMediaStream* oLMSUnused = [self addTracksToPeerConnection:pcOffer | |
| 144 withFactory:factory | |
| 145 videoSource:videoSource | |
| 146 streamLabel:@"oLMS" | |
| 147 videoTrackID:@"oLMSv0" | |
| 148 audioTrackID:@"oLMSa0"]; | |
| 149 | |
| 150 RTCDataChannel* offerDC = | |
| 151 [pcOffer createDataChannelWithLabel:@"offerDC" | |
| 152 config:[[RTCDataChannelInit alloc] init]]; | |
| 153 EXPECT_TRUE([offerDC.label isEqual:@"offerDC"]); | |
| 154 offerDC.delegate = offeringExpectations; | |
| 155 offeringExpectations.dataChannel = offerDC; | |
| 156 | |
| 157 RTCSessionDescriptionSyncObserver* sdpObserver = | |
| 158 [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 159 [pcOffer createOfferWithDelegate:sdpObserver constraints:constraints]; | |
| 160 [sdpObserver wait]; | |
| 161 EXPECT_TRUE(sdpObserver.success); | |
| 162 RTCSessionDescription* offerSDP = sdpObserver.sessionDescription; | |
| 163 EXPECT_EQ([@"offer" compare:offerSDP.type options:NSCaseInsensitiveSearch], | |
| 164 NSOrderedSame); | |
| 165 EXPECT_GT([offerSDP.description length], 0); | |
| 166 | |
| 167 sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 168 [answeringExpectations expectSignalingChange:RTCSignalingHaveRemoteOffer]; | |
| 169 [answeringExpectations expectAddStream:@"oLMS"]; | |
| 170 [pcAnswer setRemoteDescriptionWithDelegate:sdpObserver | |
| 171 sessionDescription:offerSDP]; | |
| 172 [sdpObserver wait]; | |
| 173 | |
| 174 RTCMediaStream* aLMSUnused = [self addTracksToPeerConnection:pcAnswer | |
| 175 withFactory:factory | |
| 176 videoSource:videoSource | |
| 177 streamLabel:@"aLMS" | |
| 178 videoTrackID:@"aLMSv0" | |
| 179 audioTrackID:@"aLMSa0"]; | |
| 180 | |
| 181 sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 182 [pcAnswer createAnswerWithDelegate:sdpObserver constraints:constraints]; | |
| 183 [sdpObserver wait]; | |
| 184 EXPECT_TRUE(sdpObserver.success); | |
| 185 RTCSessionDescription* answerSDP = sdpObserver.sessionDescription; | |
| 186 EXPECT_EQ([@"answer" compare:answerSDP.type options:NSCaseInsensitiveSearch], | |
| 187 NSOrderedSame); | |
| 188 EXPECT_GT([answerSDP.description length], 0); | |
| 189 | |
| 190 [offeringExpectations expectICECandidates:2]; | |
| 191 // It's possible to only have 1 ICE candidate for the answerer, since we use | |
| 192 // BUNDLE and rtcp-mux by default, and don't provide any ICE servers in this | |
| 193 // test. | |
| 194 [answeringExpectations expectICECandidates:1]; | |
| 195 | |
| 196 sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 197 [answeringExpectations expectSignalingChange:RTCSignalingStable]; | |
| 198 [pcAnswer setLocalDescriptionWithDelegate:sdpObserver | |
| 199 sessionDescription:answerSDP]; | |
| 200 [sdpObserver wait]; | |
| 201 EXPECT_TRUE(sdpObserver.sessionDescription == NULL); | |
| 202 | |
| 203 sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 204 [offeringExpectations expectSignalingChange:RTCSignalingHaveLocalOffer]; | |
| 205 [pcOffer setLocalDescriptionWithDelegate:sdpObserver | |
| 206 sessionDescription:offerSDP]; | |
| 207 [sdpObserver wait]; | |
| 208 EXPECT_TRUE(sdpObserver.sessionDescription == NULL); | |
| 209 | |
| 210 [offeringExpectations expectICEConnectionChange:RTCICEConnectionChecking]; | |
| 211 [offeringExpectations expectICEConnectionChange:RTCICEConnectionConnected]; | |
| 212 // TODO(fischman): figure out why this is flaky and re-introduce (and remove | |
| 213 // special-casing from the observer!). | |
| 214 // [offeringExpectations expectICEConnectionChange:RTCICEConnectionCompleted]; | |
| 215 [answeringExpectations expectICEConnectionChange:RTCICEConnectionChecking]; | |
| 216 [answeringExpectations expectICEConnectionChange:RTCICEConnectionConnected]; | |
| 217 | |
| 218 [offeringExpectations expectStateChange:kRTCDataChannelStateOpen]; | |
| 219 [answeringExpectations expectDataChannel:@"offerDC"]; | |
| 220 [answeringExpectations expectStateChange:kRTCDataChannelStateOpen]; | |
| 221 | |
| 222 [offeringExpectations expectICEGatheringChange:RTCICEGatheringComplete]; | |
| 223 [answeringExpectations expectICEGatheringChange:RTCICEGatheringComplete]; | |
| 224 | |
| 225 sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; | |
| 226 [offeringExpectations expectSignalingChange:RTCSignalingStable]; | |
| 227 [offeringExpectations expectAddStream:@"aLMS"]; | |
| 228 [pcOffer setRemoteDescriptionWithDelegate:sdpObserver | |
| 229 sessionDescription:answerSDP]; | |
| 230 [sdpObserver wait]; | |
| 231 EXPECT_TRUE(sdpObserver.sessionDescription == NULL); | |
| 232 | |
| 233 EXPECT_TRUE([offerSDP.type isEqual:pcOffer.localDescription.type]); | |
| 234 EXPECT_TRUE([answerSDP.type isEqual:pcOffer.remoteDescription.type]); | |
| 235 EXPECT_TRUE([offerSDP.type isEqual:pcAnswer.remoteDescription.type]); | |
| 236 EXPECT_TRUE([answerSDP.type isEqual:pcAnswer.localDescription.type]); | |
| 237 | |
| 238 for (RTCICECandidate* candidate in offeringExpectations | |
| 239 .releaseReceivedICECandidates) { | |
| 240 [pcAnswer addICECandidate:candidate]; | |
| 241 } | |
| 242 for (RTCICECandidate* candidate in answeringExpectations | |
| 243 .releaseReceivedICECandidates) { | |
| 244 [pcOffer addICECandidate:candidate]; | |
| 245 } | |
| 246 | |
| 247 EXPECT_TRUE( | |
| 248 [offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 249 kRTCPeerConnectionTestTimeout]); | |
| 250 EXPECT_TRUE( | |
| 251 [answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 252 kRTCPeerConnectionTestTimeout]); | |
| 253 | |
| 254 EXPECT_EQ(pcOffer.signalingState, RTCSignalingStable); | |
| 255 EXPECT_EQ(pcAnswer.signalingState, RTCSignalingStable); | |
| 256 | |
| 257 // Test send and receive UTF-8 text | |
| 258 NSString* text = @"你好"; | |
| 259 NSData* textData = [text dataUsingEncoding:NSUTF8StringEncoding]; | |
| 260 RTCDataBuffer* buffer = | |
| 261 [[RTCDataBuffer alloc] initWithData:textData isBinary:NO]; | |
| 262 [answeringExpectations expectMessage:[textData copy] isBinary:NO]; | |
| 263 EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]); | |
| 264 EXPECT_TRUE( | |
| 265 [answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 266 kRTCPeerConnectionTestTimeout]); | |
| 267 | |
| 268 // Test send and receive binary data | |
| 269 const size_t byteLength = 5; | |
| 270 char bytes[byteLength] = {1, 2, 3, 4, 5}; | |
| 271 NSData* byteData = [NSData dataWithBytes:bytes length:byteLength]; | |
| 272 buffer = [[RTCDataBuffer alloc] initWithData:byteData isBinary:YES]; | |
| 273 [answeringExpectations expectMessage:[byteData copy] isBinary:YES]; | |
| 274 EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]); | |
| 275 EXPECT_TRUE( | |
| 276 [answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 277 kRTCPeerConnectionTestTimeout]); | |
| 278 | |
| 279 [offeringExpectations expectStateChange:kRTCDataChannelStateClosing]; | |
| 280 [answeringExpectations expectStateChange:kRTCDataChannelStateClosing]; | |
| 281 [offeringExpectations expectStateChange:kRTCDataChannelStateClosed]; | |
| 282 [answeringExpectations expectStateChange:kRTCDataChannelStateClosed]; | |
| 283 | |
| 284 [answeringExpectations.dataChannel close]; | |
| 285 [offeringExpectations.dataChannel close]; | |
| 286 | |
| 287 EXPECT_TRUE( | |
| 288 [offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 289 kRTCPeerConnectionTestTimeout]); | |
| 290 EXPECT_TRUE( | |
| 291 [answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 292 kRTCPeerConnectionTestTimeout]); | |
| 293 // Don't need to listen to further state changes. | |
| 294 // TODO(tkchin): figure out why Closed->Closing without this. | |
| 295 offeringExpectations.dataChannel.delegate = nil; | |
| 296 answeringExpectations.dataChannel.delegate = nil; | |
| 297 | |
| 298 // Let the audio feedback run for 2s to allow human testing and to ensure | |
| 299 // things stabilize. TODO(fischman): replace seconds with # of video frames, | |
| 300 // when we have video flowing. | |
| 301 [[NSRunLoop currentRunLoop] | |
| 302 runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; | |
| 303 | |
| 304 [offeringExpectations expectICEConnectionChange:RTCICEConnectionClosed]; | |
| 305 [answeringExpectations expectICEConnectionChange:RTCICEConnectionClosed]; | |
| 306 [offeringExpectations expectSignalingChange:RTCSignalingClosed]; | |
| 307 [answeringExpectations expectSignalingChange:RTCSignalingClosed]; | |
| 308 | |
| 309 [pcOffer close]; | |
| 310 [pcAnswer close]; | |
| 311 | |
| 312 EXPECT_TRUE( | |
| 313 [offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 314 kRTCPeerConnectionTestTimeout]); | |
| 315 EXPECT_TRUE( | |
| 316 [answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout: | |
| 317 kRTCPeerConnectionTestTimeout]); | |
| 318 | |
| 319 capturer = nil; | |
| 320 videoSource = nil; | |
| 321 pcOffer = nil; | |
| 322 pcAnswer = nil; | |
| 323 // TODO(fischman): be stricter about shutdown checks; ensure thread | |
| 324 // counts return to where they were before the test kicked off, and | |
| 325 // that all objects have in fact shut down. | |
| 326 } | |
| 327 | |
| 328 @end | |
| 329 | |
| 330 // TODO(fischman): move {Initialize,Cleanup}SSL into alloc/dealloc of | |
| 331 // RTCPeerConnectionTest and avoid the appearance of RTCPeerConnectionTest being | |
| 332 // a TestBase since it's not. | |
| 333 TEST(RTCPeerConnectionTest, SessionTest) { | |
| 334 @autoreleasepool { | |
| 335 rtc::InitializeSSL(); | |
| 336 // Since |factory| will own the signaling & worker threads, it's important | |
| 337 // that it outlive the created PeerConnections since they self-delete on the | |
| 338 // signaling thread, and if |factory| is freed first then a last refcount on | |
| 339 // the factory will expire during this teardown, causing the signaling | |
| 340 // thread to try to Join() with itself. This is a hack to ensure that the | |
| 341 // factory outlives RTCPeerConnection:dealloc. | |
| 342 // See https://code.google.com/p/webrtc/issues/detail?id=3100. | |
| 343 RTCPeerConnectionFactory* factory = [[RTCPeerConnectionFactory alloc] init]; | |
| 344 @autoreleasepool { | |
| 345 RTCPeerConnectionTest* pcTest = [[RTCPeerConnectionTest alloc] init]; | |
| 346 [pcTest testCompleteSessionWithFactory:factory]; | |
| 347 } | |
| 348 rtc::CleanupSSL(); | |
| 349 } | |
| 350 } | |
| OLD | NEW |