OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #import <Foundation/Foundation.h> | |
12 #import <OCMock/OCMock.h> | |
13 | |
14 #include "webrtc/base/gunit.h" | |
15 #include "webrtc/base/ssladapter.h" | |
16 | |
17 #import "WebRTC/RTCMediaConstraints.h" | |
18 #import "WebRTC/RTCPeerConnectionFactory.h" | |
19 #import "WebRTC/RTCSessionDescription.h" | |
20 | |
21 #import "ARDAppClient+Internal.h" | |
22 #import "ARDJoinResponse+Internal.h" | |
23 #import "ARDMessageResponse+Internal.h" | |
24 #import "ARDSDPUtils.h" | |
25 | |
26 // These classes mimic XCTest APIs, to make eventual conversion to XCTest | |
27 // easier. Conversion will happen once XCTest is supported well on build bots. | |
28 @interface ARDTestExpectation : NSObject | |
29 | |
30 @property(nonatomic, readonly) NSString *description; | |
31 @property(nonatomic, readonly) BOOL isFulfilled; | |
32 | |
33 - (instancetype)initWithDescription:(NSString *)description; | |
34 - (void)fulfill; | |
35 | |
36 @end | |
37 | |
38 @implementation ARDTestExpectation | |
39 | |
40 @synthesize description = _description; | |
41 @synthesize isFulfilled = _isFulfilled; | |
42 | |
43 - (instancetype)initWithDescription:(NSString *)description { | |
44 if (self = [super init]) { | |
45 _description = description; | |
46 } | |
47 return self; | |
48 } | |
49 | |
50 - (void)fulfill { | |
51 _isFulfilled = YES; | |
52 } | |
53 | |
54 @end | |
55 | |
56 @interface ARDTestCase : NSObject | |
57 | |
58 - (ARDTestExpectation *)expectationWithDescription:(NSString *)description; | |
59 - (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout | |
60 handler:(void (^)(NSError *error))handler; | |
61 | |
62 @end | |
63 | |
64 @implementation ARDTestCase { | |
65 NSMutableArray *_expectations; | |
66 } | |
67 | |
68 - (instancetype)init { | |
69 if (self = [super init]) { | |
70 _expectations = [NSMutableArray array]; | |
71 } | |
72 return self; | |
73 } | |
74 | |
75 - (ARDTestExpectation *)expectationWithDescription:(NSString *)description { | |
76 ARDTestExpectation *expectation = | |
77 [[ARDTestExpectation alloc] initWithDescription:description]; | |
78 [_expectations addObject:expectation]; | |
79 return expectation; | |
80 } | |
81 | |
82 - (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout | |
83 handler:(void (^)(NSError *error))handler { | |
84 NSDate *startDate = [NSDate date]; | |
85 while (![self areExpectationsFulfilled]) { | |
86 NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:startDate]; | |
87 if (duration > timeout) { | |
88 NSAssert(NO, @"Expectation timed out."); | |
89 break; | |
90 } | |
91 [[NSRunLoop currentRunLoop] | |
92 runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; | |
93 } | |
94 handler(nil); | |
95 } | |
96 | |
97 - (BOOL)areExpectationsFulfilled { | |
98 for (ARDTestExpectation *expectation in _expectations) { | |
99 if (!expectation.isFulfilled) { | |
100 return NO; | |
101 } | |
102 } | |
103 return YES; | |
104 } | |
105 | |
106 @end | |
107 | |
108 @interface ARDAppClientTest : ARDTestCase | |
109 @end | |
110 | |
111 @implementation ARDAppClientTest | |
112 | |
113 #pragma mark - Mock helpers | |
114 | |
115 - (id)mockRoomServerClientForRoomId:(NSString *)roomId | |
116 clientId:(NSString *)clientId | |
117 isInitiator:(BOOL)isInitiator | |
118 messages:(NSArray *)messages | |
119 messageHandler: | |
120 (void (^)(ARDSignalingMessage *))messageHandler { | |
121 id mockRoomServerClient = | |
122 [OCMockObject mockForProtocol:@protocol(ARDRoomServerClient)]; | |
123 | |
124 // Successful join response. | |
125 ARDJoinResponse *joinResponse = [[ARDJoinResponse alloc] init]; | |
126 joinResponse.result = kARDJoinResultTypeSuccess; | |
127 joinResponse.roomId = roomId; | |
128 joinResponse.clientId = clientId; | |
129 joinResponse.isInitiator = isInitiator; | |
130 joinResponse.messages = messages; | |
131 | |
132 // Successful message response. | |
133 ARDMessageResponse *messageResponse = [[ARDMessageResponse alloc] init]; | |
134 messageResponse.result = kARDMessageResultTypeSuccess; | |
135 | |
136 // Return join response from above on join. | |
137 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
138 __unsafe_unretained void (^completionHandler)(ARDJoinResponse *response, | |
139 NSError *error); | |
140 [invocation getArgument:&completionHandler atIndex:3]; | |
141 completionHandler(joinResponse, nil); | |
142 }] joinRoomWithRoomId:roomId isLoopback:NO completionHandler:[OCMArg any]]; | |
143 | |
144 // Return message response from above on join. | |
145 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
146 __unsafe_unretained ARDSignalingMessage *message; | |
147 __unsafe_unretained void (^completionHandler)(ARDMessageResponse *response, | |
148 NSError *error); | |
149 [invocation getArgument:&message atIndex:2]; | |
150 [invocation getArgument:&completionHandler atIndex:5]; | |
151 messageHandler(message); | |
152 completionHandler(messageResponse, nil); | |
153 }] sendMessage:[OCMArg any] | |
154 forRoomId:roomId | |
155 clientId:clientId | |
156 completionHandler:[OCMArg any]]; | |
157 | |
158 // Do nothing on leave. | |
159 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
160 __unsafe_unretained void (^completionHandler)(NSError *error); | |
161 [invocation getArgument:&completionHandler atIndex:4]; | |
162 if (completionHandler) { | |
163 completionHandler(nil); | |
164 } | |
165 }] leaveRoomWithRoomId:roomId | |
166 clientId:clientId | |
167 completionHandler:[OCMArg any]]; | |
168 | |
169 return mockRoomServerClient; | |
170 } | |
171 | |
172 - (id)mockSignalingChannelForRoomId:(NSString *)roomId | |
173 clientId:(NSString *)clientId | |
174 messageHandler: | |
175 (void (^)(ARDSignalingMessage *message))messageHandler { | |
176 id mockSignalingChannel = | |
177 [OCMockObject niceMockForProtocol:@protocol(ARDSignalingChannel)]; | |
178 [[mockSignalingChannel stub] registerForRoomId:roomId clientId:clientId]; | |
179 [[[mockSignalingChannel stub] andDo:^(NSInvocation *invocation) { | |
180 __unsafe_unretained ARDSignalingMessage *message; | |
181 [invocation getArgument:&message atIndex:2]; | |
182 messageHandler(message); | |
183 }] sendMessage:[OCMArg any]]; | |
184 return mockSignalingChannel; | |
185 } | |
186 | |
187 - (id)mockTURNClient { | |
188 id mockTURNClient = | |
189 [OCMockObject mockForProtocol:@protocol(ARDTURNClient)]; | |
190 [[[mockTURNClient stub] andDo:^(NSInvocation *invocation) { | |
191 // Don't return anything in TURN response. | |
192 __unsafe_unretained void (^completionHandler)(NSArray *turnServers, | |
193 NSError *error); | |
194 [invocation getArgument:&completionHandler atIndex:2]; | |
195 completionHandler([NSArray array], nil); | |
196 }] requestServersWithCompletionHandler:[OCMArg any]]; | |
197 return mockTURNClient; | |
198 } | |
199 | |
200 - (ARDAppClient *)createAppClientForRoomId:(NSString *)roomId | |
201 clientId:(NSString *)clientId | |
202 isInitiator:(BOOL)isInitiator | |
203 messages:(NSArray *)messages | |
204 messageHandler: | |
205 (void (^)(ARDSignalingMessage *message))messageHandler | |
206 connectedHandler:(void (^)(void))connectedHandler { | |
207 id turnClient = [self mockTURNClient]; | |
208 id signalingChannel = [self mockSignalingChannelForRoomId:roomId | |
209 clientId:clientId | |
210 messageHandler:messageHandler]; | |
211 id roomServerClient = | |
212 [self mockRoomServerClientForRoomId:roomId | |
213 clientId:clientId | |
214 isInitiator:isInitiator | |
215 messages:messages | |
216 messageHandler:messageHandler]; | |
217 id delegate = | |
218 [OCMockObject niceMockForProtocol:@protocol(ARDAppClientDelegate)]; | |
219 [[[delegate stub] andDo:^(NSInvocation *invocation) { | |
220 connectedHandler(); | |
221 }] appClient:[OCMArg any] | |
222 didChangeConnectionState:RTCIceConnectionStateConnected]; | |
223 | |
224 return [[ARDAppClient alloc] initWithRoomServerClient:roomServerClient | |
225 signalingChannel:signalingChannel | |
226 turnClient:turnClient | |
227 delegate:delegate]; | |
228 } | |
229 | |
230 // Tests that an ICE connection is established between two ARDAppClient objects | |
231 // where one is set up as a caller and the other the answerer. Network | |
232 // components are mocked out and messages are relayed directly from object to | |
233 // object. It's expected that both clients reach the | |
234 // RTCIceConnectionStateConnected state within a reasonable amount of time. | |
235 - (void)testSession { | |
236 // Need block arguments here because we're setting up a callbacks before we | |
237 // create the clients. | |
238 ARDAppClient *caller = nil; | |
239 ARDAppClient *answerer = nil; | |
240 __block __weak ARDAppClient *weakCaller = nil; | |
241 __block __weak ARDAppClient *weakAnswerer = nil; | |
242 NSString *roomId = @"testRoom"; | |
243 NSString *callerId = @"testCallerId"; | |
244 NSString *answererId = @"testAnswererId"; | |
245 | |
246 ARDTestExpectation *callerConnectionExpectation = | |
247 [self expectationWithDescription:@"Caller PC connected."]; | |
248 ARDTestExpectation *answererConnectionExpectation = | |
249 [self expectationWithDescription:@"Answerer PC connected."]; | |
250 | |
251 caller = [self createAppClientForRoomId:roomId | |
252 clientId:callerId | |
253 isInitiator:YES | |
254 messages:[NSArray array] | |
255 messageHandler:^(ARDSignalingMessage *message) { | |
256 ARDAppClient *strongAnswerer = weakAnswerer; | |
257 [strongAnswerer channel:strongAnswerer.channel didReceiveMessage:message]; | |
258 } connectedHandler:^{ | |
259 [callerConnectionExpectation fulfill]; | |
260 }]; | |
261 // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion | |
262 // crash in Debug. | |
263 caller.defaultPeerConnectionConstraints = | |
264 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil | |
265 optionalConstraints:nil]; | |
266 weakCaller = caller; | |
267 | |
268 answerer = [self createAppClientForRoomId:roomId | |
269 clientId:answererId | |
270 isInitiator:NO | |
271 messages:[NSArray array] | |
272 messageHandler:^(ARDSignalingMessage *message) { | |
273 ARDAppClient *strongCaller = weakCaller; | |
274 [strongCaller channel:strongCaller.channel didReceiveMessage:message]; | |
275 } connectedHandler:^{ | |
276 [answererConnectionExpectation fulfill]; | |
277 }]; | |
278 // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion | |
279 // crash in Debug. | |
280 answerer.defaultPeerConnectionConstraints = | |
281 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil | |
282 optionalConstraints:nil]; | |
283 weakAnswerer = answerer; | |
284 | |
285 // Kick off connection. | |
286 [caller connectToRoomWithId:roomId | |
287 isLoopback:NO | |
288 isAudioOnly:NO | |
289 shouldMakeAecDump:NO | |
290 shouldUseLevelControl:NO]; | |
291 [answerer connectToRoomWithId:roomId | |
292 isLoopback:NO | |
293 isAudioOnly:NO | |
294 shouldMakeAecDump:NO | |
295 shouldUseLevelControl:NO]; | |
296 [self waitForExpectationsWithTimeout:20 handler:^(NSError *error) { | |
297 if (error) { | |
298 NSLog(@"Expectations error: %@", error); | |
299 } | |
300 }]; | |
301 } | |
302 | |
303 @end | |
304 | |
305 @interface ARDSDPUtilsTest : ARDTestCase | |
306 - (void)testPreferVideoCodec; | |
307 @end | |
308 | |
309 @implementation ARDSDPUtilsTest | |
310 | |
311 - (void)testPreferVideoCodec { | |
312 NSString *sdp = @("m=video 9 RTP/SAVPF 100 116 117 96 120\n" | |
313 "a=rtpmap:120 H264/90000\n"); | |
314 NSString *expectedSdp = @("m=video 9 RTP/SAVPF 120 100 116 117 96\n" | |
315 "a=rtpmap:120 H264/90000\n"); | |
316 RTCSessionDescription* desc = | |
317 [[RTCSessionDescription alloc] initWithType:RTCSdpTypeOffer sdp:sdp]; | |
318 RTCSessionDescription *h264Desc = | |
319 [ARDSDPUtils descriptionForDescription:desc | |
320 preferredVideoCodec:@"H264"]; | |
321 EXPECT_TRUE([h264Desc.description isEqualToString:expectedSdp]); | |
322 } | |
323 | |
324 @end | |
325 | |
326 class SignalingTest : public ::testing::Test { | |
327 protected: | |
328 static void SetUpTestCase() { | |
329 rtc::InitializeSSL(); | |
330 } | |
331 static void TearDownTestCase() { | |
332 rtc::CleanupSSL(); | |
333 } | |
334 }; | |
335 | |
336 TEST_F(SignalingTest, SessionTest) { | |
337 @autoreleasepool { | |
338 ARDAppClientTest *test = [[ARDAppClientTest alloc] init]; | |
339 [test testSession]; | |
340 } | |
341 } | |
342 | |
343 TEST_F(SignalingTest, SDPTest) { | |
344 @autoreleasepool { | |
345 ARDSDPUtilsTest *test = [[ARDSDPUtilsTest alloc] init]; | |
346 [test testPreferVideoCodec]; | |
347 } | |
348 } | |
349 | |
350 | |
OLD | NEW |