ARDAppClient_xctest.mm (11387B)
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 #import <QuartzCore/CoreAnimation.h> 14 #import <XCTest/XCTest.h> 15 16 #include "rtc_base/ssl_adapter.h" 17 18 #import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" 19 #import "sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h" 20 21 #import "ARDAppClient+Internal.h" 22 #import "ARDJoinResponse+Internal.h" 23 #import "ARDMessageResponse+Internal.h" 24 #import "ARDSettingsModel.h" 25 26 @interface ARDAppClientTest : XCTestCase 27 @end 28 29 @implementation ARDAppClientTest 30 31 #pragma mark - Mock helpers 32 33 - (id)mockRoomServerClientForRoomId:(NSString *)roomId 34 clientId:(NSString *)clientId 35 isInitiator:(BOOL)isInitiator 36 messages:(NSArray *)messages 37 messageHandler: 38 (void (^)(ARDSignalingMessage *))messageHandler { 39 id mockRoomServerClient = 40 [OCMockObject mockForProtocol:@protocol(ARDRoomServerClient)]; 41 42 // Successful join response. 43 ARDJoinResponse *joinResponse = [[ARDJoinResponse alloc] init]; 44 joinResponse.result = kARDJoinResultTypeSuccess; 45 joinResponse.roomId = roomId; 46 joinResponse.clientId = clientId; 47 joinResponse.isInitiator = isInitiator; 48 joinResponse.messages = messages; 49 50 // Successful message response. 51 ARDMessageResponse *messageResponse = [[ARDMessageResponse alloc] init]; 52 messageResponse.result = kARDMessageResultTypeSuccess; 53 54 // Return join response from above on join. 55 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { 56 __unsafe_unretained void (^completionHandler)(ARDJoinResponse *response, 57 NSError *error); 58 [invocation getArgument:&completionHandler atIndex:4]; 59 completionHandler(joinResponse, nil); 60 }] joinRoomWithRoomId:roomId isLoopback:NO completionHandler:[OCMArg any]]; 61 62 // Return message response from above on join. 63 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { 64 __unsafe_unretained ARDSignalingMessage *message; 65 __unsafe_unretained void (^completionHandler)(ARDMessageResponse *response, 66 NSError *error); 67 [invocation getArgument:&message atIndex:2]; 68 [invocation getArgument:&completionHandler atIndex:5]; 69 messageHandler(message); 70 completionHandler(messageResponse, nil); 71 }] sendMessage:[OCMArg any] 72 forRoomId:roomId 73 clientId:clientId 74 completionHandler:[OCMArg any]]; 75 76 // Do nothing on leave. 77 [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { 78 __unsafe_unretained void (^completionHandler)(NSError *error); 79 [invocation getArgument:&completionHandler atIndex:4]; 80 if (completionHandler) { 81 completionHandler(nil); 82 } 83 }] leaveRoomWithRoomId:roomId 84 clientId:clientId 85 completionHandler:[OCMArg any]]; 86 87 return mockRoomServerClient; 88 } 89 90 - (id)mockSignalingChannelForRoomId:(NSString *)roomId 91 clientId:(NSString *)clientId 92 messageHandler:(void (^)(ARDSignalingMessage *message)) 93 messageHandler { 94 id mockSignalingChannel = 95 [OCMockObject niceMockForProtocol:@protocol(ARDSignalingChannel)]; 96 [[mockSignalingChannel stub] registerForRoomId:roomId clientId:clientId]; 97 [[[mockSignalingChannel stub] andDo:^(NSInvocation *invocation) { 98 __unsafe_unretained ARDSignalingMessage *message; 99 [invocation getArgument:&message atIndex:2]; 100 messageHandler(message); 101 }] sendMessage:[OCMArg any]]; 102 return mockSignalingChannel; 103 } 104 105 - (id)mockTURNClient { 106 id mockTURNClient = [OCMockObject mockForProtocol:@protocol(ARDTURNClient)]; 107 [[[mockTURNClient stub] andDo:^(NSInvocation *invocation) { 108 // Don't return anything in TURN response. 109 __unsafe_unretained void (^completionHandler)(NSArray *turnServers, 110 NSError *error); 111 [invocation getArgument:&completionHandler atIndex:2]; 112 completionHandler([NSArray array], nil); 113 }] requestServersWithCompletionHandler:[OCMArg any]]; 114 return mockTURNClient; 115 } 116 117 - (id)mockSettingsModel { 118 ARDSettingsModel *model = [[ARDSettingsModel alloc] init]; 119 id partialMock = [OCMockObject partialMockForObject:model]; 120 [[[partialMock stub] andReturn:@[ @"640x480", @"960x540", @"1280x720" ]] 121 availableVideoResolutions]; 122 123 return model; 124 } 125 126 - (ARDAppClient *) 127 createAppClientForRoomId:(NSString *)roomId 128 clientId:(NSString *)clientId 129 isInitiator:(BOOL)isInitiator 130 messages:(NSArray *)messages 131 messageHandler: 132 (void (^)(ARDSignalingMessage *message))messageHandler 133 connectedHandler:(void (^)(void))connectedHandler 134 localVideoTrackHandler:(void (^)(void))localVideoTrackHandler { 135 id turnClient = [self mockTURNClient]; 136 id signalingChannel = [self mockSignalingChannelForRoomId:roomId 137 clientId:clientId 138 messageHandler:messageHandler]; 139 id roomServerClient = [self mockRoomServerClientForRoomId:roomId 140 clientId:clientId 141 isInitiator:isInitiator 142 messages:messages 143 messageHandler:messageHandler]; 144 id delegate = 145 [OCMockObject niceMockForProtocol:@protocol(ARDAppClientDelegate)]; 146 [[[delegate stub] andDo:^(NSInvocation *invocation) { 147 connectedHandler(); 148 }] appClient:[OCMArg any] 149 didChangeConnectionState:RTCIceConnectionStateConnected]; 150 [[[delegate stub] andDo:^(NSInvocation *invocation) { 151 localVideoTrackHandler(); 152 }] appClient:[OCMArg any] didReceiveLocalVideoTrack:[OCMArg any]]; 153 154 return [[ARDAppClient alloc] initWithRoomServerClient:roomServerClient 155 signalingChannel:signalingChannel 156 turnClient:turnClient 157 delegate:delegate]; 158 } 159 160 #pragma mark - Cases 161 162 // Tests that an ICE connection is established between two ARDAppClient objects 163 // where one is set up as a caller and the other the answerer. Network 164 // components are mocked out and messages are relayed directly from object to 165 // object. It's expected that both clients reach the 166 // RTCIceConnectionStateConnected state within a reasonable amount of time. 167 - (void)testSession { 168 // Need block arguments here because we're setting up a callbacks before we 169 // create the clients. 170 ARDAppClient *caller = nil; 171 ARDAppClient *answerer = nil; 172 __block __weak ARDAppClient *weakCaller = nil; 173 __block __weak ARDAppClient *weakAnswerer = nil; 174 NSString *roomId = @"testRoom"; 175 NSString *callerId = @"testCallerId"; 176 NSString *answererId = @"testAnswererId"; 177 178 XCTestExpectation *callerConnectionExpectation = 179 [self expectationWithDescription:@"Caller PC connected"]; 180 XCTestExpectation *answererConnectionExpectation = 181 [self expectationWithDescription:@"Answerer PC connected"]; 182 183 caller = [self createAppClientForRoomId:roomId 184 clientId:callerId 185 isInitiator:YES 186 messages:[NSArray array] 187 messageHandler:^(ARDSignalingMessage *message) { 188 ARDAppClient *strongAnswerer = weakAnswerer; 189 [strongAnswerer channel:strongAnswerer.channel 190 didReceiveMessage:message]; 191 } 192 connectedHandler:^{ 193 [callerConnectionExpectation fulfill]; 194 } 195 localVideoTrackHandler:^{ 196 }]; 197 // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion 198 // crash in Debug. 199 caller.defaultPeerConnectionConstraints = [[RTC_OBJC_TYPE(RTCMediaConstraints) 200 alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; 201 weakCaller = caller; 202 203 answerer = [self createAppClientForRoomId:roomId 204 clientId:answererId 205 isInitiator:NO 206 messages:[NSArray array] 207 messageHandler:^(ARDSignalingMessage *message) { 208 ARDAppClient *strongCaller = weakCaller; 209 [strongCaller channel:strongCaller.channel didReceiveMessage:message]; 210 } 211 connectedHandler:^{ 212 [answererConnectionExpectation fulfill]; 213 } 214 localVideoTrackHandler:^{ 215 }]; 216 // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion 217 // crash in Debug. 218 answerer.defaultPeerConnectionConstraints = 219 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] 220 initWithMandatoryConstraints:nil 221 optionalConstraints:nil]; 222 weakAnswerer = answerer; 223 224 // Kick off connection. 225 [caller connectToRoomWithId:roomId 226 settings:[self mockSettingsModel] 227 isLoopback:NO]; 228 [answerer connectToRoomWithId:roomId 229 settings:[self mockSettingsModel] 230 isLoopback:NO]; 231 [self waitForExpectationsWithTimeout:20 232 handler:^(NSError *error) { 233 if (error) { 234 XCTFail(@"Expectation failed with error %@.", 235 error); 236 } 237 }]; 238 } 239 240 // Test to see that we get a local video connection 241 // Note this will currently pass even when no camera is connected as a local 242 // video track is created regardless (Perhaps there should be a test for 243 // that...) 244 #if !TARGET_IPHONE_SIMULATOR // Expect to fail on simulator due to no camera 245 // support 246 - (void)testSessionShouldGetLocalVideoTrackCallback { 247 ARDAppClient *caller = nil; 248 NSString *roomId = @"testRoom"; 249 NSString *callerId = @"testCallerId"; 250 251 XCTestExpectation *localVideoTrackExpectation = 252 [self expectationWithDescription:@"Caller got local video."]; 253 254 caller = [self createAppClientForRoomId:roomId 255 clientId:callerId 256 isInitiator:YES 257 messages:[NSArray array] 258 messageHandler:^(ARDSignalingMessage *message) { 259 } 260 connectedHandler:^{ 261 } 262 localVideoTrackHandler:^{ 263 [localVideoTrackExpectation fulfill]; 264 }]; 265 caller.defaultPeerConnectionConstraints = [[RTC_OBJC_TYPE(RTCMediaConstraints) 266 alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; 267 268 // Kick off connection. 269 [caller connectToRoomWithId:roomId 270 settings:[self mockSettingsModel] 271 isLoopback:NO]; 272 [self waitForExpectationsWithTimeout:20 273 handler:^(NSError *error) { 274 if (error) { 275 XCTFail( 276 "Expectation timed out with error: %@.", 277 error); 278 } 279 }]; 280 } 281 #endif 282 283 @end