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 |