1/*
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>
12
13#include "webrtc/test/gtest.h"
14
15#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
16#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h"
17
18@interface RTCAudioSessionTestDelegate : NSObject <RTCAudioSessionDelegate>
19@end
20
21@implementation RTCAudioSessionTestDelegate
22
23- (void)audioSessionDidBeginInterruption:(RTCAudioSession *)session {
24}
25
26- (void)audioSessionDidEndInterruption:(RTCAudioSession *)session
27                   shouldResumeSession:(BOOL)shouldResumeSession {
28}
29
30- (void)audioSessionDidChangeRoute:(RTCAudioSession *)session
31           reason:(AVAudioSessionRouteChangeReason)reason
32    previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
33}
34
35- (void)audioSessionMediaServicesWereLost:(RTCAudioSession *)session {
36}
37
38- (void)audioSessionMediaServicesWereReset:(RTCAudioSession *)session {
39}
40
41- (void)audioSessionShouldConfigure:(RTCAudioSession *)session {
42}
43
44- (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session {
45}
46
47@end
48
49// A delegate that adds itself to the audio session on init and removes itself
50// in its dealloc.
51@interface RTCTestRemoveOnDeallocDelegate : RTCAudioSessionTestDelegate
52@end
53
54@implementation RTCTestRemoveOnDeallocDelegate
55
56- (instancetype)init {
57  if (self = [super init]) {
58    RTCAudioSession *session = [RTCAudioSession sharedInstance];
59    [session addDelegate:self];
60  }
61  return self;
62}
63
64- (void)dealloc {
65  RTCAudioSession *session = [RTCAudioSession sharedInstance];
66  [session removeDelegate:self];
67}
68
69@end
70
71
72@interface RTCAudioSessionTest : NSObject
73
74- (void)testLockForConfiguration;
75
76@end
77
78@implementation RTCAudioSessionTest
79
80- (void)testLockForConfiguration {
81  RTCAudioSession *session = [RTCAudioSession sharedInstance];
82
83  for (size_t i = 0; i < 2; i++) {
84    [session lockForConfiguration];
85    EXPECT_TRUE(session.isLocked);
86  }
87  for (size_t i = 0; i < 2; i++) {
88    EXPECT_TRUE(session.isLocked);
89    [session unlockForConfiguration];
90  }
91  EXPECT_FALSE(session.isLocked);
92}
93
94- (void)testAddAndRemoveDelegates {
95  RTCAudioSession *session = [RTCAudioSession sharedInstance];
96  NSMutableArray *delegates = [NSMutableArray array];
97  const size_t count = 5;
98  for (size_t i = 0; i < count; ++i) {
99    RTCAudioSessionTestDelegate *delegate =
100        [[RTCAudioSessionTestDelegate alloc] init];
101    [session addDelegate:delegate];
102    [delegates addObject:delegate];
103    EXPECT_EQ(i + 1, session.delegates.size());
104  }
105  [delegates enumerateObjectsUsingBlock:^(RTCAudioSessionTestDelegate *obj,
106                                          NSUInteger idx,
107                                          BOOL *stop) {
108    [session removeDelegate:obj];
109  }];
110  EXPECT_EQ(0u, session.delegates.size());
111}
112
113- (void)testPushDelegate {
114  RTCAudioSession *session = [RTCAudioSession sharedInstance];
115  NSMutableArray *delegates = [NSMutableArray array];
116  const size_t count = 2;
117  for (size_t i = 0; i < count; ++i) {
118    RTCAudioSessionTestDelegate *delegate =
119        [[RTCAudioSessionTestDelegate alloc] init];
120    [session addDelegate:delegate];
121    [delegates addObject:delegate];
122  }
123  // Test that it gets added to the front of the list.
124  RTCAudioSessionTestDelegate *pushedDelegate =
125      [[RTCAudioSessionTestDelegate alloc] init];
126  [session pushDelegate:pushedDelegate];
127  EXPECT_TRUE(pushedDelegate == session.delegates[0]);
128
129  // Test that it stays at the front of the list.
130  for (size_t i = 0; i < count; ++i) {
131    RTCAudioSessionTestDelegate *delegate =
132        [[RTCAudioSessionTestDelegate alloc] init];
133    [session addDelegate:delegate];
134    [delegates addObject:delegate];
135  }
136  EXPECT_TRUE(pushedDelegate == session.delegates[0]);
137
138  // Test that the next one goes to the front too.
139  pushedDelegate = [[RTCAudioSessionTestDelegate alloc] init];
140  [session pushDelegate:pushedDelegate];
141  EXPECT_TRUE(pushedDelegate == session.delegates[0]);
142}
143
144// Tests that delegates added to the audio session properly zero out. This is
145// checking an implementation detail (that vectors of __weak work as expected).
146- (void)testZeroingWeakDelegate {
147  RTCAudioSession *session = [RTCAudioSession sharedInstance];
148  @autoreleasepool {
149    // Add a delegate to the session. There should be one delegate at this
150    // point.
151    RTCAudioSessionTestDelegate *delegate =
152        [[RTCAudioSessionTestDelegate alloc] init];
153    [session addDelegate:delegate];
154    EXPECT_EQ(1u, session.delegates.size());
155    EXPECT_TRUE(session.delegates[0]);
156  }
157  // The previously created delegate should've de-alloced, leaving a nil ptr.
158  EXPECT_FALSE(session.delegates[0]);
159  RTCAudioSessionTestDelegate *delegate =
160      [[RTCAudioSessionTestDelegate alloc] init];
161  [session addDelegate:delegate];
162  // On adding a new delegate, nil ptrs should've been cleared.
163  EXPECT_EQ(1u, session.delegates.size());
164  EXPECT_TRUE(session.delegates[0]);
165}
166
167// Tests that we don't crash when removing delegates in dealloc.
168// Added as a regression test.
169- (void)testRemoveDelegateOnDealloc {
170  @autoreleasepool {
171    RTCTestRemoveOnDeallocDelegate *delegate =
172        [[RTCTestRemoveOnDeallocDelegate alloc] init];
173    EXPECT_TRUE(delegate);
174  }
175  RTCAudioSession *session = [RTCAudioSession sharedInstance];
176  EXPECT_EQ(0u, session.delegates.size());
177}
178
179@end
180
181namespace webrtc {
182
183class AudioSessionTest : public ::testing::Test {
184 protected:
185  void TearDown() {
186    RTCAudioSession *session = [RTCAudioSession sharedInstance];
187    for (id<RTCAudioSessionDelegate> delegate : session.delegates) {
188      [session removeDelegate:delegate];
189    }
190  }
191};
192
193TEST_F(AudioSessionTest, LockForConfiguration) {
194  RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
195  [test testLockForConfiguration];
196}
197
198TEST_F(AudioSessionTest, AddAndRemoveDelegates) {
199  RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
200  [test testAddAndRemoveDelegates];
201}
202
203TEST_F(AudioSessionTest, PushDelegate) {
204  RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
205  [test testPushDelegate];
206}
207
208TEST_F(AudioSessionTest, ZeroingWeakDelegate) {
209  RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
210  [test testZeroingWeakDelegate];
211}
212
213TEST_F(AudioSessionTest, RemoveDelegateOnDealloc) {
214  RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
215  [test testRemoveDelegateOnDealloc];
216}
217
218}  // namespace webrtc
219