| /* | |
| * Copyright 2014 The WebRTC Project Authors. All rights reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #import <Foundation/Foundation.h> | |
| #import <OCMock/OCMock.h> | |
| #import <QuartzCore/CoreAnimation.h> | |
| #import <XCTest/XCTest.h> | |
| #include "rtc_base/ssl_adapter.h" | |
| #import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" | |
| #import "sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h" | |
| #import "ARDAppClient+Internal.h" | |
| #import "ARDJoinResponse+Internal.h" | |
| #import "ARDMessageResponse+Internal.h" | |
| #import "ARDSettingsModel.h" | |
| @interface ARDAppClientTest : XCTestCase | |
| @end | |
| @implementation ARDAppClientTest | |
| #pragma mark - Mock helpers | |
| - (id)mockRoomServerClientForRoomId:(NSString *)roomId | |
| clientId:(NSString *)clientId | |
| isInitiator:(BOOL)isInitiator | |
| messages:(NSArray *)messages | |
| messageHandler: | |
| (void (^)(ARDSignalingMessage *))messageHandler { | |
| id mockRoomServerClient = | |
| [OCMockObject mockForProtocol:@protocol(ARDRoomServerClient)]; | |
| // Successful join response. | |
| ARDJoinResponse *joinResponse = [[ARDJoinResponse alloc] init]; | |
| joinResponse.result = kARDJoinResultTypeSuccess; | |
| joinResponse.roomId = roomId; | |
| joinResponse.clientId = clientId; | |
| joinResponse.isInitiator = isInitiator; | |
| joinResponse.messages = messages; | |
| // Successful message response. | |
| ARDMessageResponse *messageResponse = [[ARDMessageResponse alloc] init]; | |
| messageResponse.result = kARDMessageResultTypeSuccess; | |
| // Return join response from above on join. | |
| [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
| __unsafe_unretained void (^completionHandler)(ARDJoinResponse *response, | |
| NSError *error); | |
| [invocation getArgument:&completionHandler atIndex:4]; | |
| completionHandler(joinResponse, nil); | |
| }] joinRoomWithRoomId:roomId isLoopback:NO completionHandler:[OCMArg any]]; | |
| // Return message response from above on join. | |
| [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
| __unsafe_unretained ARDSignalingMessage *message; | |
| __unsafe_unretained void (^completionHandler)(ARDMessageResponse *response, | |
| NSError *error); | |
| [invocation getArgument:&message atIndex:2]; | |
| [invocation getArgument:&completionHandler atIndex:5]; | |
| messageHandler(message); | |
| completionHandler(messageResponse, nil); | |
| }] sendMessage:[OCMArg any] | |
| forRoomId:roomId | |
| clientId:clientId | |
| completionHandler:[OCMArg any]]; | |
| // Do nothing on leave. | |
| [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) { | |
| __unsafe_unretained void (^completionHandler)(NSError *error); | |
| [invocation getArgument:&completionHandler atIndex:4]; | |
| if (completionHandler) { | |
| completionHandler(nil); | |
| } | |
| }] leaveRoomWithRoomId:roomId | |
| clientId:clientId | |
| completionHandler:[OCMArg any]]; | |
| return mockRoomServerClient; | |
| } | |
| - (id)mockSignalingChannelForRoomId:(NSString *)roomId | |
| clientId:(NSString *)clientId | |
| messageHandler:(void (^)(ARDSignalingMessage *message)) | |
| messageHandler { | |
| id mockSignalingChannel = | |
| [OCMockObject niceMockForProtocol:@protocol(ARDSignalingChannel)]; | |
| [[mockSignalingChannel stub] registerForRoomId:roomId clientId:clientId]; | |
| [[[mockSignalingChannel stub] andDo:^(NSInvocation *invocation) { | |
| __unsafe_unretained ARDSignalingMessage *message; | |
| [invocation getArgument:&message atIndex:2]; | |
| messageHandler(message); | |
| }] sendMessage:[OCMArg any]]; | |
| return mockSignalingChannel; | |
| } | |
| - (id)mockTURNClient { | |
| id mockTURNClient = [OCMockObject mockForProtocol:@protocol(ARDTURNClient)]; | |
| [[[mockTURNClient stub] andDo:^(NSInvocation *invocation) { | |
| // Don't return anything in TURN response. | |
| __unsafe_unretained void (^completionHandler)(NSArray *turnServers, | |
| NSError *error); | |
| [invocation getArgument:&completionHandler atIndex:2]; | |
| completionHandler([NSArray array], nil); | |
| }] requestServersWithCompletionHandler:[OCMArg any]]; | |
| return mockTURNClient; | |
| } | |
| - (id)mockSettingsModel { | |
| ARDSettingsModel *model = [[ARDSettingsModel alloc] init]; | |
| id partialMock = [OCMockObject partialMockForObject:model]; | |
| [[[partialMock stub] andReturn:@[ @"640x480", @"960x540", @"1280x720" ]] | |
| availableVideoResolutions]; | |
| return model; | |
| } | |
| - (ARDAppClient *) | |
| createAppClientForRoomId:(NSString *)roomId | |
| clientId:(NSString *)clientId | |
| isInitiator:(BOOL)isInitiator | |
| messages:(NSArray *)messages | |
| messageHandler: | |
| (void (^)(ARDSignalingMessage *message))messageHandler | |
| connectedHandler:(void (^)(void))connectedHandler | |
| localVideoTrackHandler:(void (^)(void))localVideoTrackHandler { | |
| id turnClient = [self mockTURNClient]; | |
| id signalingChannel = [self mockSignalingChannelForRoomId:roomId | |
| clientId:clientId | |
| messageHandler:messageHandler]; | |
| id roomServerClient = [self mockRoomServerClientForRoomId:roomId | |
| clientId:clientId | |
| isInitiator:isInitiator | |
| messages:messages | |
| messageHandler:messageHandler]; | |
| id delegate = | |
| [OCMockObject niceMockForProtocol:@protocol(ARDAppClientDelegate)]; | |
| [[[delegate stub] andDo:^(NSInvocation *invocation) { | |
| connectedHandler(); | |
| }] appClient:[OCMArg any] | |
| didChangeConnectionState:RTCIceConnectionStateConnected]; | |
| [[[delegate stub] andDo:^(NSInvocation *invocation) { | |
| localVideoTrackHandler(); | |
| }] appClient:[OCMArg any] didReceiveLocalVideoTrack:[OCMArg any]]; | |
| return [[ARDAppClient alloc] initWithRoomServerClient:roomServerClient | |
| signalingChannel:signalingChannel | |
| turnClient:turnClient | |
| delegate:delegate]; | |
| } | |
| #pragma mark - Cases | |
| // Tests that an ICE connection is established between two ARDAppClient objects | |
| // where one is set up as a caller and the other the answerer. Network | |
| // components are mocked out and messages are relayed directly from object to | |
| // object. It's expected that both clients reach the | |
| // RTCIceConnectionStateConnected state within a reasonable amount of time. | |
| - (void)testSession { | |
| // Need block arguments here because we're setting up a callbacks before we | |
| // create the clients. | |
| ARDAppClient *caller = nil; | |
| ARDAppClient *answerer = nil; | |
| __block __weak ARDAppClient *weakCaller = nil; | |
| __block __weak ARDAppClient *weakAnswerer = nil; | |
| NSString *roomId = @"testRoom"; | |
| NSString *callerId = @"testCallerId"; | |
| NSString *answererId = @"testAnswererId"; | |
| XCTestExpectation *callerConnectionExpectation = | |
| [self expectationWithDescription:@"Caller PC connected"]; | |
| XCTestExpectation *answererConnectionExpectation = | |
| [self expectationWithDescription:@"Answerer PC connected"]; | |
| caller = [self createAppClientForRoomId:roomId | |
| clientId:callerId | |
| isInitiator:YES | |
| messages:[NSArray array] | |
| messageHandler:^(ARDSignalingMessage *message) { | |
| ARDAppClient *strongAnswerer = weakAnswerer; | |
| [strongAnswerer channel:strongAnswerer.channel | |
| didReceiveMessage:message]; | |
| } | |
| connectedHandler:^{ | |
| [callerConnectionExpectation fulfill]; | |
| } | |
| localVideoTrackHandler:^{ | |
| }]; | |
| // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion | |
| // crash in Debug. | |
| caller.defaultPeerConnectionConstraints = [[RTC_OBJC_TYPE(RTCMediaConstraints) | |
| alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; | |
| weakCaller = caller; | |
| answerer = [self createAppClientForRoomId:roomId | |
| clientId:answererId | |
| isInitiator:NO | |
| messages:[NSArray array] | |
| messageHandler:^(ARDSignalingMessage *message) { | |
| ARDAppClient *strongCaller = weakCaller; | |
| [strongCaller channel:strongCaller.channel didReceiveMessage:message]; | |
| } | |
| connectedHandler:^{ | |
| [answererConnectionExpectation fulfill]; | |
| } | |
| localVideoTrackHandler:^{ | |
| }]; | |
| // TODO(tkchin): Figure out why DTLS-SRTP constraint causes thread assertion | |
| // crash in Debug. | |
| answerer.defaultPeerConnectionConstraints = | |
| [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] | |
| initWithMandatoryConstraints:nil | |
| optionalConstraints:nil]; | |
| weakAnswerer = answerer; | |
| // Kick off connection. | |
| [caller connectToRoomWithId:roomId | |
| settings:[self mockSettingsModel] | |
| isLoopback:NO]; | |
| [answerer connectToRoomWithId:roomId | |
| settings:[self mockSettingsModel] | |
| isLoopback:NO]; | |
| [self waitForExpectationsWithTimeout:20 | |
| handler:^(NSError *error) { | |
| if (error) { | |
| XCTFail(@"Expectation failed with error %@.", | |
| error); | |
| } | |
| }]; | |
| } | |
| // Test to see that we get a local video connection | |
| // Note this will currently pass even when no camera is connected as a local | |
| // video track is created regardless (Perhaps there should be a test for | |
| // that...) | |
| #if !TARGET_IPHONE_SIMULATOR // Expect to fail on simulator due to no camera | |
| // support | |
| - (void)testSessionShouldGetLocalVideoTrackCallback { | |
| ARDAppClient *caller = nil; | |
| NSString *roomId = @"testRoom"; | |
| NSString *callerId = @"testCallerId"; | |
| XCTestExpectation *localVideoTrackExpectation = | |
| [self expectationWithDescription:@"Caller got local video."]; | |
| caller = [self createAppClientForRoomId:roomId | |
| clientId:callerId | |
| isInitiator:YES | |
| messages:[NSArray array] | |
| messageHandler:^(ARDSignalingMessage *message) { | |
| } | |
| connectedHandler:^{ | |
| } | |
| localVideoTrackHandler:^{ | |
| [localVideoTrackExpectation fulfill]; | |
| }]; | |
| caller.defaultPeerConnectionConstraints = [[RTC_OBJC_TYPE(RTCMediaConstraints) | |
| alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; | |
| // Kick off connection. | |
| [caller connectToRoomWithId:roomId | |
| settings:[self mockSettingsModel] | |
| isLoopback:NO]; | |
| [self waitForExpectationsWithTimeout:20 | |
| handler:^(NSError *error) { | |
| if (error) { | |
| XCTFail( | |
| "Expectation timed out with error: %@.", | |
| error); | |
| } | |
| }]; | |
| } | |
| #endif | |
| @end |