blob: a4b49fc1c08129fb4f3f2a1ba0b12397769a9b87 [file] [log] [blame]
tkchin0ce3bf92016-03-12 16:52:04 -08001/*
2 * Copyright 2016 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>
jttehf84c1d62017-04-21 13:56:39 -070012#import <OCMock/OCMock.h>
tkchin0ce3bf92016-03-12 16:52:04 -080013
kwiberg77eab702016-09-28 17:42:01 -070014#include "webrtc/test/gtest.h"
tkchin0ce3bf92016-03-12 16:52:04 -080015
16#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
tkchine54467f2016-03-15 16:54:03 -070017#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h"
jttehf84c1d62017-04-21 13:56:39 -070018#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h"
tkchine54467f2016-03-15 16:54:03 -070019
20@interface RTCAudioSessionTestDelegate : NSObject <RTCAudioSessionDelegate>
21@end
22
23@implementation RTCAudioSessionTestDelegate
24
25- (void)audioSessionDidBeginInterruption:(RTCAudioSession *)session {
26}
27
28- (void)audioSessionDidEndInterruption:(RTCAudioSession *)session
29 shouldResumeSession:(BOOL)shouldResumeSession {
30}
31
32- (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
33 reason:(AVAudioSessionRouteChangeReason)reason
34 previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
35}
36
kthelgason1634e162017-02-07 02:48:55 -080037- (void)audioSessionMediaServerTerminated:(RTCAudioSession *)session {
tkchine54467f2016-03-15 16:54:03 -070038}
39
kthelgason1634e162017-02-07 02:48:55 -080040- (void)audioSessionMediaServerReset:(RTCAudioSession *)session {
tkchine54467f2016-03-15 16:54:03 -070041}
42
43- (void)audioSessionShouldConfigure:(RTCAudioSession *)session {
44}
45
46- (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session {
47}
48
49@end
50
tkchinefdd9302016-04-11 12:00:59 -070051// A delegate that adds itself to the audio session on init and removes itself
52// in its dealloc.
53@interface RTCTestRemoveOnDeallocDelegate : RTCAudioSessionTestDelegate
54@end
55
56@implementation RTCTestRemoveOnDeallocDelegate
57
58- (instancetype)init {
59 if (self = [super init]) {
60 RTCAudioSession *session = [RTCAudioSession sharedInstance];
61 [session addDelegate:self];
62 }
63 return self;
64}
65
66- (void)dealloc {
67 RTCAudioSession *session = [RTCAudioSession sharedInstance];
68 [session removeDelegate:self];
69}
70
71@end
72
tkchin0ce3bf92016-03-12 16:52:04 -080073
74@interface RTCAudioSessionTest : NSObject
75
76- (void)testLockForConfiguration;
77
78@end
79
80@implementation RTCAudioSessionTest
81
82- (void)testLockForConfiguration {
83 RTCAudioSession *session = [RTCAudioSession sharedInstance];
84
85 for (size_t i = 0; i < 2; i++) {
86 [session lockForConfiguration];
87 EXPECT_TRUE(session.isLocked);
88 }
89 for (size_t i = 0; i < 2; i++) {
90 EXPECT_TRUE(session.isLocked);
91 [session unlockForConfiguration];
92 }
93 EXPECT_FALSE(session.isLocked);
94}
95
tkchine54467f2016-03-15 16:54:03 -070096- (void)testAddAndRemoveDelegates {
97 RTCAudioSession *session = [RTCAudioSession sharedInstance];
98 NSMutableArray *delegates = [NSMutableArray array];
99 const size_t count = 5;
100 for (size_t i = 0; i < count; ++i) {
101 RTCAudioSessionTestDelegate *delegate =
102 [[RTCAudioSessionTestDelegate alloc] init];
103 [session addDelegate:delegate];
104 [delegates addObject:delegate];
105 EXPECT_EQ(i + 1, session.delegates.size());
106 }
107 [delegates enumerateObjectsUsingBlock:^(RTCAudioSessionTestDelegate *obj,
108 NSUInteger idx,
109 BOOL *stop) {
110 [session removeDelegate:obj];
111 }];
112 EXPECT_EQ(0u, session.delegates.size());
113}
114
115- (void)testPushDelegate {
116 RTCAudioSession *session = [RTCAudioSession sharedInstance];
117 NSMutableArray *delegates = [NSMutableArray array];
118 const size_t count = 2;
119 for (size_t i = 0; i < count; ++i) {
120 RTCAudioSessionTestDelegate *delegate =
121 [[RTCAudioSessionTestDelegate alloc] init];
122 [session addDelegate:delegate];
123 [delegates addObject:delegate];
124 }
125 // Test that it gets added to the front of the list.
126 RTCAudioSessionTestDelegate *pushedDelegate =
127 [[RTCAudioSessionTestDelegate alloc] init];
128 [session pushDelegate:pushedDelegate];
129 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
130
131 // Test that it stays at the front of the list.
132 for (size_t i = 0; i < count; ++i) {
133 RTCAudioSessionTestDelegate *delegate =
134 [[RTCAudioSessionTestDelegate alloc] init];
135 [session addDelegate:delegate];
136 [delegates addObject:delegate];
137 }
138 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
139
140 // Test that the next one goes to the front too.
141 pushedDelegate = [[RTCAudioSessionTestDelegate alloc] init];
142 [session pushDelegate:pushedDelegate];
143 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
144}
145
146// Tests that delegates added to the audio session properly zero out. This is
147// checking an implementation detail (that vectors of __weak work as expected).
148- (void)testZeroingWeakDelegate {
149 RTCAudioSession *session = [RTCAudioSession sharedInstance];
150 @autoreleasepool {
151 // Add a delegate to the session. There should be one delegate at this
152 // point.
153 RTCAudioSessionTestDelegate *delegate =
154 [[RTCAudioSessionTestDelegate alloc] init];
155 [session addDelegate:delegate];
156 EXPECT_EQ(1u, session.delegates.size());
157 EXPECT_TRUE(session.delegates[0]);
158 }
159 // The previously created delegate should've de-alloced, leaving a nil ptr.
160 EXPECT_FALSE(session.delegates[0]);
161 RTCAudioSessionTestDelegate *delegate =
162 [[RTCAudioSessionTestDelegate alloc] init];
163 [session addDelegate:delegate];
164 // On adding a new delegate, nil ptrs should've been cleared.
165 EXPECT_EQ(1u, session.delegates.size());
166 EXPECT_TRUE(session.delegates[0]);
167}
168
tkchinefdd9302016-04-11 12:00:59 -0700169// Tests that we don't crash when removing delegates in dealloc.
170// Added as a regression test.
171- (void)testRemoveDelegateOnDealloc {
172 @autoreleasepool {
173 RTCTestRemoveOnDeallocDelegate *delegate =
174 [[RTCTestRemoveOnDeallocDelegate alloc] init];
175 EXPECT_TRUE(delegate);
176 }
177 RTCAudioSession *session = [RTCAudioSession sharedInstance];
178 EXPECT_EQ(0u, session.delegates.size());
179}
180
jtteh3c9a6c02017-04-18 09:09:35 -0700181- (void)testAudioSessionActivation {
182 RTCAudioSession *audioSession = [RTCAudioSession sharedInstance];
183 EXPECT_EQ(0, audioSession.activationCount);
184 [audioSession audioSessionDidActivate:[AVAudioSession sharedInstance]];
185 EXPECT_EQ(1, audioSession.activationCount);
186 [audioSession audioSessionDidDeactivate:[AVAudioSession sharedInstance]];
187 EXPECT_EQ(0, audioSession.activationCount);
188}
189
jttehf84c1d62017-04-21 13:56:39 -0700190// Hack - fixes OCMVerify link error
191// Link error is: Undefined symbols for architecture i386:
192// "OCMMakeLocation(objc_object*, char const*, int)", referenced from:
193// -[RTCAudioSessionTest testConfigureWebRTCSession] in RTCAudioSessionTest.o
194// ld: symbol(s) not found for architecture i386
195// REASON: https://github.com/erikdoe/ocmock/issues/238
196OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line){
197 return [OCMLocation locationWithTestCase:testCase
198 file:[NSString stringWithUTF8String:fileCString]
199 line:line];
200}
201
202- (void)testConfigureWebRTCSession {
203 NSError *error = nil;
204
205 void (^setActiveBlock)(NSInvocation *invocation) = ^(NSInvocation *invocation) {
206 __autoreleasing NSError **retError;
207 [invocation getArgument:&retError atIndex:4];
208 *retError = [NSError errorWithDomain:@"AVAudioSession"
209 code:AVAudioSessionErrorInsufficientPriority
210 userInfo:nil];
211 BOOL failure = NO;
212 [invocation setReturnValue:&failure];
213 };
214
215 id mockAVAudioSession = OCMPartialMock([AVAudioSession sharedInstance]);
216 OCMStub([[mockAVAudioSession ignoringNonObjectArgs]
217 setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
218 andDo(setActiveBlock);
219
220 id mockAudioSession = OCMPartialMock([RTCAudioSession sharedInstance]);
221 OCMStub([mockAudioSession session]).andReturn(mockAVAudioSession);
222
223 RTCAudioSession *audioSession = mockAudioSession;
224 EXPECT_EQ(0, audioSession.activationCount);
225 [audioSession lockForConfiguration];
226 EXPECT_TRUE([audioSession checkLock:nil]);
227 // configureWebRTCSession is forced to fail in the above mock interface,
228 // so activationCount should remain 0
229 OCMExpect([[mockAVAudioSession ignoringNonObjectArgs]
230 setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
231 andDo(setActiveBlock);
232 OCMExpect([mockAudioSession session]).andReturn(mockAVAudioSession);
233 EXPECT_FALSE([audioSession configureWebRTCSession:&error]);
234 EXPECT_EQ(0, audioSession.activationCount);
235
236 id session = audioSession.session;
237 EXPECT_EQ(session, mockAVAudioSession);
238 EXPECT_EQ(NO, [mockAVAudioSession setActive:YES withOptions:0 error:&error]);
239 [audioSession unlockForConfiguration];
240
241 OCMVerify([mockAudioSession session]);
242 OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES withOptions:0 error:&error]);
243 OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:NO withOptions:0 error:&error]);
244
245 [mockAVAudioSession stopMocking];
246 [mockAudioSession stopMocking];
247}
248
tkchin0ce3bf92016-03-12 16:52:04 -0800249@end
250
tkchine54467f2016-03-15 16:54:03 -0700251namespace webrtc {
252
253class AudioSessionTest : public ::testing::Test {
254 protected:
255 void TearDown() {
256 RTCAudioSession *session = [RTCAudioSession sharedInstance];
257 for (id<RTCAudioSessionDelegate> delegate : session.delegates) {
258 [session removeDelegate:delegate];
259 }
260 }
261};
262
263TEST_F(AudioSessionTest, LockForConfiguration) {
tkchin0ce3bf92016-03-12 16:52:04 -0800264 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
265 [test testLockForConfiguration];
266}
tkchine54467f2016-03-15 16:54:03 -0700267
268TEST_F(AudioSessionTest, AddAndRemoveDelegates) {
269 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
270 [test testAddAndRemoveDelegates];
271}
272
273TEST_F(AudioSessionTest, PushDelegate) {
274 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
275 [test testPushDelegate];
276}
277
278TEST_F(AudioSessionTest, ZeroingWeakDelegate) {
279 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
280 [test testZeroingWeakDelegate];
281}
282
tkchinefdd9302016-04-11 12:00:59 -0700283TEST_F(AudioSessionTest, RemoveDelegateOnDealloc) {
284 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
285 [test testRemoveDelegateOnDealloc];
286}
287
jtteh3c9a6c02017-04-18 09:09:35 -0700288TEST_F(AudioSessionTest, AudioSessionActivation) {
289 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
290 [test testAudioSessionActivation];
291}
292
jttehf84c1d62017-04-21 13:56:39 -0700293TEST_F(AudioSessionTest, ConfigureWebRTCSession) {
294 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
295 [test testConfigureWebRTCSession];
296}
jtteh3c9a6c02017-04-18 09:09:35 -0700297
tkchine54467f2016-03-15 16:54:03 -0700298} // namespace webrtc