blob: d94c63535bec02fd92ea82d085c11f7988de786f [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "rtc_base/gunit.h"
tkchin0ce3bf92016-03-12 16:52:04 -080015
denicija59ee91b2017-06-05 05:48:47 -070016#import "RTCAudioSession+Private.h"
17
18#import "WebRTC/RTCAudioSession.h"
19#import "WebRTC/RTCAudioSessionConfiguration.h"
tkchine54467f2016-03-15 16:54:03 -070020
21@interface RTCAudioSessionTestDelegate : NSObject <RTCAudioSessionDelegate>
jtteh13ae11a2017-05-25 17:52:20 -070022
23@property (nonatomic, readonly) float outputVolume;
24
tkchine54467f2016-03-15 16:54:03 -070025@end
26
27@implementation RTCAudioSessionTestDelegate
28
jtteh13ae11a2017-05-25 17:52:20 -070029@synthesize outputVolume = _outputVolume;
30
31- (instancetype)init {
32 if (self = [super init]) {
33 _outputVolume = -1;
34 }
35 return self;
36}
37
tkchine54467f2016-03-15 16:54:03 -070038- (void)audioSessionDidBeginInterruption:(RTCAudioSession *)session {
39}
40
41- (void)audioSessionDidEndInterruption:(RTCAudioSession *)session
42 shouldResumeSession:(BOOL)shouldResumeSession {
43}
44
45- (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
46 reason:(AVAudioSessionRouteChangeReason)reason
47 previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
48}
49
kthelgason1634e162017-02-07 02:48:55 -080050- (void)audioSessionMediaServerTerminated:(RTCAudioSession *)session {
tkchine54467f2016-03-15 16:54:03 -070051}
52
kthelgason1634e162017-02-07 02:48:55 -080053- (void)audioSessionMediaServerReset:(RTCAudioSession *)session {
tkchine54467f2016-03-15 16:54:03 -070054}
55
56- (void)audioSessionShouldConfigure:(RTCAudioSession *)session {
57}
58
59- (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session {
60}
61
jtteh13ae11a2017-05-25 17:52:20 -070062- (void)audioSession:(RTCAudioSession *)audioSession
63 didChangeOutputVolume:(float)outputVolume {
64 _outputVolume = outputVolume;
65}
66
tkchine54467f2016-03-15 16:54:03 -070067@end
68
tkchinefdd9302016-04-11 12:00:59 -070069// A delegate that adds itself to the audio session on init and removes itself
70// in its dealloc.
71@interface RTCTestRemoveOnDeallocDelegate : RTCAudioSessionTestDelegate
72@end
73
74@implementation RTCTestRemoveOnDeallocDelegate
75
76- (instancetype)init {
77 if (self = [super init]) {
78 RTCAudioSession *session = [RTCAudioSession sharedInstance];
79 [session addDelegate:self];
80 }
81 return self;
82}
83
84- (void)dealloc {
85 RTCAudioSession *session = [RTCAudioSession sharedInstance];
86 [session removeDelegate:self];
87}
88
89@end
90
tkchin0ce3bf92016-03-12 16:52:04 -080091
92@interface RTCAudioSessionTest : NSObject
93
94- (void)testLockForConfiguration;
95
96@end
97
98@implementation RTCAudioSessionTest
99
100- (void)testLockForConfiguration {
101 RTCAudioSession *session = [RTCAudioSession sharedInstance];
102
103 for (size_t i = 0; i < 2; i++) {
104 [session lockForConfiguration];
105 EXPECT_TRUE(session.isLocked);
106 }
107 for (size_t i = 0; i < 2; i++) {
108 EXPECT_TRUE(session.isLocked);
109 [session unlockForConfiguration];
110 }
111 EXPECT_FALSE(session.isLocked);
112}
113
tkchine54467f2016-03-15 16:54:03 -0700114- (void)testAddAndRemoveDelegates {
115 RTCAudioSession *session = [RTCAudioSession sharedInstance];
116 NSMutableArray *delegates = [NSMutableArray array];
117 const size_t count = 5;
118 for (size_t i = 0; i < count; ++i) {
119 RTCAudioSessionTestDelegate *delegate =
120 [[RTCAudioSessionTestDelegate alloc] init];
121 [session addDelegate:delegate];
122 [delegates addObject:delegate];
123 EXPECT_EQ(i + 1, session.delegates.size());
124 }
125 [delegates enumerateObjectsUsingBlock:^(RTCAudioSessionTestDelegate *obj,
126 NSUInteger idx,
127 BOOL *stop) {
128 [session removeDelegate:obj];
129 }];
130 EXPECT_EQ(0u, session.delegates.size());
131}
132
133- (void)testPushDelegate {
134 RTCAudioSession *session = [RTCAudioSession sharedInstance];
135 NSMutableArray *delegates = [NSMutableArray array];
136 const size_t count = 2;
137 for (size_t i = 0; i < count; ++i) {
138 RTCAudioSessionTestDelegate *delegate =
139 [[RTCAudioSessionTestDelegate alloc] init];
140 [session addDelegate:delegate];
141 [delegates addObject:delegate];
142 }
143 // Test that it gets added to the front of the list.
144 RTCAudioSessionTestDelegate *pushedDelegate =
145 [[RTCAudioSessionTestDelegate alloc] init];
146 [session pushDelegate:pushedDelegate];
147 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
148
149 // Test that it stays at the front of the list.
150 for (size_t i = 0; i < count; ++i) {
151 RTCAudioSessionTestDelegate *delegate =
152 [[RTCAudioSessionTestDelegate alloc] init];
153 [session addDelegate:delegate];
154 [delegates addObject:delegate];
155 }
156 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
157
158 // Test that the next one goes to the front too.
159 pushedDelegate = [[RTCAudioSessionTestDelegate alloc] init];
160 [session pushDelegate:pushedDelegate];
161 EXPECT_TRUE(pushedDelegate == session.delegates[0]);
162}
163
164// Tests that delegates added to the audio session properly zero out. This is
165// checking an implementation detail (that vectors of __weak work as expected).
166- (void)testZeroingWeakDelegate {
167 RTCAudioSession *session = [RTCAudioSession sharedInstance];
168 @autoreleasepool {
169 // Add a delegate to the session. There should be one delegate at this
170 // point.
171 RTCAudioSessionTestDelegate *delegate =
172 [[RTCAudioSessionTestDelegate alloc] init];
173 [session addDelegate:delegate];
174 EXPECT_EQ(1u, session.delegates.size());
175 EXPECT_TRUE(session.delegates[0]);
176 }
177 // The previously created delegate should've de-alloced, leaving a nil ptr.
178 EXPECT_FALSE(session.delegates[0]);
179 RTCAudioSessionTestDelegate *delegate =
180 [[RTCAudioSessionTestDelegate alloc] init];
181 [session addDelegate:delegate];
182 // On adding a new delegate, nil ptrs should've been cleared.
183 EXPECT_EQ(1u, session.delegates.size());
184 EXPECT_TRUE(session.delegates[0]);
185}
186
tkchinefdd9302016-04-11 12:00:59 -0700187// Tests that we don't crash when removing delegates in dealloc.
188// Added as a regression test.
189- (void)testRemoveDelegateOnDealloc {
190 @autoreleasepool {
191 RTCTestRemoveOnDeallocDelegate *delegate =
192 [[RTCTestRemoveOnDeallocDelegate alloc] init];
193 EXPECT_TRUE(delegate);
194 }
195 RTCAudioSession *session = [RTCAudioSession sharedInstance];
196 EXPECT_EQ(0u, session.delegates.size());
197}
198
jtteh3c9a6c02017-04-18 09:09:35 -0700199- (void)testAudioSessionActivation {
200 RTCAudioSession *audioSession = [RTCAudioSession sharedInstance];
201 EXPECT_EQ(0, audioSession.activationCount);
202 [audioSession audioSessionDidActivate:[AVAudioSession sharedInstance]];
203 EXPECT_EQ(1, audioSession.activationCount);
204 [audioSession audioSessionDidDeactivate:[AVAudioSession sharedInstance]];
205 EXPECT_EQ(0, audioSession.activationCount);
206}
207
jttehf84c1d62017-04-21 13:56:39 -0700208// Hack - fixes OCMVerify link error
209// Link error is: Undefined symbols for architecture i386:
210// "OCMMakeLocation(objc_object*, char const*, int)", referenced from:
211// -[RTCAudioSessionTest testConfigureWebRTCSession] in RTCAudioSessionTest.o
212// ld: symbol(s) not found for architecture i386
213// REASON: https://github.com/erikdoe/ocmock/issues/238
214OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line){
215 return [OCMLocation locationWithTestCase:testCase
216 file:[NSString stringWithUTF8String:fileCString]
217 line:line];
218}
219
220- (void)testConfigureWebRTCSession {
221 NSError *error = nil;
222
223 void (^setActiveBlock)(NSInvocation *invocation) = ^(NSInvocation *invocation) {
224 __autoreleasing NSError **retError;
225 [invocation getArgument:&retError atIndex:4];
226 *retError = [NSError errorWithDomain:@"AVAudioSession"
227 code:AVAudioSessionErrorInsufficientPriority
228 userInfo:nil];
229 BOOL failure = NO;
230 [invocation setReturnValue:&failure];
231 };
232
233 id mockAVAudioSession = OCMPartialMock([AVAudioSession sharedInstance]);
234 OCMStub([[mockAVAudioSession ignoringNonObjectArgs]
235 setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
236 andDo(setActiveBlock);
237
238 id mockAudioSession = OCMPartialMock([RTCAudioSession sharedInstance]);
239 OCMStub([mockAudioSession session]).andReturn(mockAVAudioSession);
240
241 RTCAudioSession *audioSession = mockAudioSession;
242 EXPECT_EQ(0, audioSession.activationCount);
243 [audioSession lockForConfiguration];
244 EXPECT_TRUE([audioSession checkLock:nil]);
245 // configureWebRTCSession is forced to fail in the above mock interface,
246 // so activationCount should remain 0
247 OCMExpect([[mockAVAudioSession ignoringNonObjectArgs]
248 setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
249 andDo(setActiveBlock);
250 OCMExpect([mockAudioSession session]).andReturn(mockAVAudioSession);
251 EXPECT_FALSE([audioSession configureWebRTCSession:&error]);
252 EXPECT_EQ(0, audioSession.activationCount);
253
254 id session = audioSession.session;
255 EXPECT_EQ(session, mockAVAudioSession);
256 EXPECT_EQ(NO, [mockAVAudioSession setActive:YES withOptions:0 error:&error]);
257 [audioSession unlockForConfiguration];
258
259 OCMVerify([mockAudioSession session]);
260 OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES withOptions:0 error:&error]);
261 OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:NO withOptions:0 error:&error]);
262
263 [mockAVAudioSession stopMocking];
264 [mockAudioSession stopMocking];
265}
266
jtteh13ae11a2017-05-25 17:52:20 -0700267- (void)testAudioVolumeDidNotify {
268 RTCAudioSession *session = [RTCAudioSession sharedInstance];
269 RTCAudioSessionTestDelegate *delegate =
270 [[RTCAudioSessionTestDelegate alloc] init];
271 [session addDelegate:delegate];
272
273 [session observeValueForKeyPath:@"outputVolume"
274 ofObject:[AVAudioSession sharedInstance]
275 change:
276 @{NSKeyValueChangeNewKey :
277 @([AVAudioSession sharedInstance].outputVolume) }
278 context:nil];
279
280 EXPECT_NE(delegate.outputVolume, -1);
281 EXPECT_EQ([AVAudioSession sharedInstance].outputVolume, delegate.outputVolume);
282}
283
tkchin0ce3bf92016-03-12 16:52:04 -0800284@end
285
tkchine54467f2016-03-15 16:54:03 -0700286namespace webrtc {
287
288class AudioSessionTest : public ::testing::Test {
289 protected:
290 void TearDown() {
291 RTCAudioSession *session = [RTCAudioSession sharedInstance];
292 for (id<RTCAudioSessionDelegate> delegate : session.delegates) {
293 [session removeDelegate:delegate];
294 }
295 }
296};
297
298TEST_F(AudioSessionTest, LockForConfiguration) {
tkchin0ce3bf92016-03-12 16:52:04 -0800299 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
300 [test testLockForConfiguration];
301}
tkchine54467f2016-03-15 16:54:03 -0700302
303TEST_F(AudioSessionTest, AddAndRemoveDelegates) {
304 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
305 [test testAddAndRemoveDelegates];
306}
307
308TEST_F(AudioSessionTest, PushDelegate) {
309 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
310 [test testPushDelegate];
311}
312
313TEST_F(AudioSessionTest, ZeroingWeakDelegate) {
314 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
315 [test testZeroingWeakDelegate];
316}
317
tkchinefdd9302016-04-11 12:00:59 -0700318TEST_F(AudioSessionTest, RemoveDelegateOnDealloc) {
319 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
320 [test testRemoveDelegateOnDealloc];
321}
322
jtteh3c9a6c02017-04-18 09:09:35 -0700323TEST_F(AudioSessionTest, AudioSessionActivation) {
324 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
325 [test testAudioSessionActivation];
326}
327
jttehf84c1d62017-04-21 13:56:39 -0700328TEST_F(AudioSessionTest, ConfigureWebRTCSession) {
329 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
330 [test testConfigureWebRTCSession];
331}
jtteh3c9a6c02017-04-18 09:09:35 -0700332
jtteh13ae11a2017-05-25 17:52:20 -0700333TEST_F(AudioSessionTest, AudioVolumeDidNotify) {
334 RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
335 [test testAudioVolumeDidNotify];
336}
337
tkchine54467f2016-03-15 16:54:03 -0700338} // namespace webrtc