webrtc/sdk/objc/unittests/RTCAudioSessionTest.mm
Mirko Bonadei a81e9c82fc Wrap WebRTC OBJC API types with RTC_OBJC_TYPE.
This CL introduced 2 new macros that affect the WebRTC OBJC API symbols:

- RTC_OBJC_TYPE_PREFIX:
  Macro used to prepend a prefix to the API types that are exported with
  RTC_OBJC_EXPORT.

  Clients can patch the definition of this macro locally and build
  WebRTC.framework with their own prefix in case symbol clashing is a
  problem.

  This macro must only be defined by changing the value in
  sdk/objc/base/RTCMacros.h  and not on via compiler flag to ensure
  it has a unique value.

- RCT_OBJC_TYPE:
  Macro used internally to reference API types. Declaring an API type
  without using this macro will not include the declared type in the
  set of types that will be affected by the configurable
  RTC_OBJC_TYPE_PREFIX.

Manual changes:
https://webrtc-review.googlesource.com/c/src/+/173781/5..10

The auto-generated changes in PS#5 have been done with:
https://webrtc-review.googlesource.com/c/src/+/174061.

Bug: None
Change-Id: I0d54ca94db764fb3b6cb4365873f79e14cd879b8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/173781
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31153}
2020-05-04 15:01:26 +00:00

357 lines
12 KiB
Text

/*
* Copyright 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#include <vector>
#include "rtc_base/gunit.h"
#import "components/audio/RTCAudioSession+Private.h"
#import "components/audio/RTCAudioSession.h"
#import "components/audio/RTCAudioSessionConfiguration.h"
@interface RTC_OBJC_TYPE (RTCAudioSession)
(UnitTesting)
@property(nonatomic,
readonly) std::vector<__weak id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)> > delegates;
- (instancetype)initWithAudioSession:(id)audioSession;
@end
@interface MockAVAudioSession : NSObject
@property (nonatomic, readwrite, assign) float outputVolume;
@end
@implementation MockAVAudioSession
@synthesize outputVolume = _outputVolume;
@end
@interface RTCAudioSessionTestDelegate : NSObject <RTC_OBJC_TYPE (RTCAudioSessionDelegate)>
@property (nonatomic, readonly) float outputVolume;
@end
@implementation RTCAudioSessionTestDelegate
@synthesize outputVolume = _outputVolume;
- (instancetype)init {
if (self = [super init]) {
_outputVolume = -1;
}
return self;
}
- (void)audioSessionDidBeginInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
}
- (void)audioSessionDidEndInterruption:(RTC_OBJC_TYPE(RTCAudioSession) *)session
shouldResumeSession:(BOOL)shouldResumeSession {
}
- (void)audioSessionDidChangeRoute:(RTC_OBJC_TYPE(RTCAudioSession) *)session
reason:(AVAudioSessionRouteChangeReason)reason
previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
}
- (void)audioSessionMediaServerTerminated:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
}
- (void)audioSessionMediaServerReset:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
}
- (void)audioSessionShouldConfigure:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
}
- (void)audioSessionShouldUnconfigure:(RTC_OBJC_TYPE(RTCAudioSession) *)session {
}
- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession
didChangeOutputVolume:(float)outputVolume {
_outputVolume = outputVolume;
}
@end
// A delegate that adds itself to the audio session on init and removes itself
// in its dealloc.
@interface RTCTestRemoveOnDeallocDelegate : RTCAudioSessionTestDelegate
@end
@implementation RTCTestRemoveOnDeallocDelegate
- (instancetype)init {
if (self = [super init]) {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
[session addDelegate:self];
}
return self;
}
- (void)dealloc {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
[session removeDelegate:self];
}
@end
@interface RTCAudioSessionTest : NSObject
- (void)testLockForConfiguration;
@end
@implementation RTCAudioSessionTest
- (void)testLockForConfiguration {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
for (size_t i = 0; i < 2; i++) {
[session lockForConfiguration];
EXPECT_TRUE(session.isLocked);
}
for (size_t i = 0; i < 2; i++) {
EXPECT_TRUE(session.isLocked);
[session unlockForConfiguration];
}
EXPECT_FALSE(session.isLocked);
}
- (void)testAddAndRemoveDelegates {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
NSMutableArray *delegates = [NSMutableArray array];
const size_t count = 5;
for (size_t i = 0; i < count; ++i) {
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
[delegates addObject:delegate];
EXPECT_EQ(i + 1, session.delegates.size());
}
[delegates enumerateObjectsUsingBlock:^(RTCAudioSessionTestDelegate *obj,
NSUInteger idx,
BOOL *stop) {
[session removeDelegate:obj];
}];
EXPECT_EQ(0u, session.delegates.size());
}
- (void)testPushDelegate {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
NSMutableArray *delegates = [NSMutableArray array];
const size_t count = 2;
for (size_t i = 0; i < count; ++i) {
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
[delegates addObject:delegate];
}
// Test that it gets added to the front of the list.
RTCAudioSessionTestDelegate *pushedDelegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session pushDelegate:pushedDelegate];
EXPECT_TRUE(pushedDelegate == session.delegates[0]);
// Test that it stays at the front of the list.
for (size_t i = 0; i < count; ++i) {
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
[delegates addObject:delegate];
}
EXPECT_TRUE(pushedDelegate == session.delegates[0]);
// Test that the next one goes to the front too.
pushedDelegate = [[RTCAudioSessionTestDelegate alloc] init];
[session pushDelegate:pushedDelegate];
EXPECT_TRUE(pushedDelegate == session.delegates[0]);
}
// Tests that delegates added to the audio session properly zero out. This is
// checking an implementation detail (that vectors of __weak work as expected).
- (void)testZeroingWeakDelegate {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
@autoreleasepool {
// Add a delegate to the session. There should be one delegate at this
// point.
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
EXPECT_EQ(1u, session.delegates.size());
EXPECT_TRUE(session.delegates[0]);
}
// The previously created delegate should've de-alloced, leaving a nil ptr.
EXPECT_FALSE(session.delegates[0]);
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
// On adding a new delegate, nil ptrs should've been cleared.
EXPECT_EQ(1u, session.delegates.size());
EXPECT_TRUE(session.delegates[0]);
}
// Tests that we don't crash when removing delegates in dealloc.
// Added as a regression test.
- (void)testRemoveDelegateOnDealloc {
@autoreleasepool {
RTCTestRemoveOnDeallocDelegate *delegate =
[[RTCTestRemoveOnDeallocDelegate alloc] init];
EXPECT_TRUE(delegate);
}
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
EXPECT_EQ(0u, session.delegates.size());
}
- (void)testAudioSessionActivation {
RTC_OBJC_TYPE(RTCAudioSession) *audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
EXPECT_EQ(0, audioSession.activationCount);
[audioSession audioSessionDidActivate:[AVAudioSession sharedInstance]];
EXPECT_EQ(1, audioSession.activationCount);
[audioSession audioSessionDidDeactivate:[AVAudioSession sharedInstance]];
EXPECT_EQ(0, audioSession.activationCount);
}
// Hack - fixes OCMVerify link error
// Link error is: Undefined symbols for architecture i386:
// "OCMMakeLocation(objc_object*, char const*, int)", referenced from:
// -[RTCAudioSessionTest testConfigureWebRTCSession] in RTCAudioSessionTest.o
// ld: symbol(s) not found for architecture i386
// REASON: https://github.com/erikdoe/ocmock/issues/238
OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line){
return [OCMLocation locationWithTestCase:testCase
file:[NSString stringWithUTF8String:fileCString]
line:line];
}
- (void)testConfigureWebRTCSession {
NSError *error = nil;
void (^setActiveBlock)(NSInvocation *invocation) = ^(NSInvocation *invocation) {
__autoreleasing NSError **retError;
[invocation getArgument:&retError atIndex:4];
*retError = [NSError errorWithDomain:@"AVAudioSession"
code:AVAudioSessionErrorInsufficientPriority
userInfo:nil];
BOOL failure = NO;
[invocation setReturnValue:&failure];
};
id mockAVAudioSession = OCMPartialMock([AVAudioSession sharedInstance]);
OCMStub([[mockAVAudioSession ignoringNonObjectArgs]
setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
andDo(setActiveBlock);
id mockAudioSession = OCMPartialMock([RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]);
OCMStub([mockAudioSession session]).andReturn(mockAVAudioSession);
RTC_OBJC_TYPE(RTCAudioSession) *audioSession = mockAudioSession;
EXPECT_EQ(0, audioSession.activationCount);
[audioSession lockForConfiguration];
EXPECT_TRUE([audioSession checkLock:nil]);
// configureWebRTCSession is forced to fail in the above mock interface,
// so activationCount should remain 0
OCMExpect([[mockAVAudioSession ignoringNonObjectArgs]
setActive:YES withOptions:0 error:((NSError __autoreleasing **)[OCMArg anyPointer])]).
andDo(setActiveBlock);
OCMExpect([mockAudioSession session]).andReturn(mockAVAudioSession);
EXPECT_FALSE([audioSession configureWebRTCSession:&error]);
EXPECT_EQ(0, audioSession.activationCount);
id session = audioSession.session;
EXPECT_EQ(session, mockAVAudioSession);
EXPECT_EQ(NO, [mockAVAudioSession setActive:YES withOptions:0 error:&error]);
[audioSession unlockForConfiguration];
OCMVerify([mockAudioSession session]);
OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:YES withOptions:0 error:&error]);
OCMVerify([[mockAVAudioSession ignoringNonObjectArgs] setActive:NO withOptions:0 error:&error]);
[mockAVAudioSession stopMocking];
[mockAudioSession stopMocking];
}
- (void)testAudioVolumeDidNotify {
MockAVAudioSession *mockAVAudioSession = [[MockAVAudioSession alloc] init];
RTC_OBJC_TYPE(RTCAudioSession) *session =
[[RTC_OBJC_TYPE(RTCAudioSession) alloc] initWithAudioSession:mockAVAudioSession];
RTCAudioSessionTestDelegate *delegate =
[[RTCAudioSessionTestDelegate alloc] init];
[session addDelegate:delegate];
float expectedVolume = 0.75;
mockAVAudioSession.outputVolume = expectedVolume;
EXPECT_EQ(expectedVolume, delegate.outputVolume);
}
@end
namespace webrtc {
class AudioSessionTest : public ::testing::Test {
protected:
void TearDown() override {
RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
for (id<RTC_OBJC_TYPE(RTCAudioSessionDelegate)> delegate : session.delegates) {
[session removeDelegate:delegate];
}
}
};
TEST_F(AudioSessionTest, LockForConfiguration) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testLockForConfiguration];
}
TEST_F(AudioSessionTest, AddAndRemoveDelegates) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testAddAndRemoveDelegates];
}
TEST_F(AudioSessionTest, PushDelegate) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testPushDelegate];
}
TEST_F(AudioSessionTest, ZeroingWeakDelegate) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testZeroingWeakDelegate];
}
TEST_F(AudioSessionTest, RemoveDelegateOnDealloc) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testRemoveDelegateOnDealloc];
}
TEST_F(AudioSessionTest, AudioSessionActivation) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testAudioSessionActivation];
}
TEST_F(AudioSessionTest, ConfigureWebRTCSession) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testConfigureWebRTCSession];
}
TEST_F(AudioSessionTest, AudioVolumeDidNotify) {
RTCAudioSessionTest *test = [[RTCAudioSessionTest alloc] init];
[test testAudioVolumeDidNotify];
}
} // namespace webrtc