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 |