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