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