451 lines
12 KiB
Objective-C
451 lines
12 KiB
Objective-C
// This Source Code Form is subject to the terms of the Mozilla Public
|
||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
|
||
#import <XCTest/XCTest.h>
|
||
#import <OCMock/OCMock.h>
|
||
#import "OCTCAsserts.h"
|
||
#import "OCTAudioEngine+Private.h"
|
||
#import "OCTAudioQueue.h"
|
||
#import "OCTManagerConstants.h"
|
||
|
||
#include "CoreAudioMocks.h"
|
||
|
||
@import AVFoundation;
|
||
|
||
static void *refToSelf;
|
||
static AudioQueueInputCallback callForInput;
|
||
static AudioQueueOutputCallback callForOutput;
|
||
|
||
const OCTToxAVPCMData pcm[8] = {2, 4, 6, 8, 10, 12, 14, 16};
|
||
|
||
OSStatus PASSING_AudioQueueNewOutput(const AudioStreamBasicDescription *inFormat,
|
||
AudioQueueOutputCallback inCallbackProc,
|
||
void *inUserData,
|
||
CFRunLoopRef inCallbackRunLoop,
|
||
CFStringRef inCallbackRunLoopMode,
|
||
UInt32 inFlags,
|
||
AudioQueueRef *outAQ)
|
||
{
|
||
*outAQ = (void *)0x1234567;
|
||
callForOutput = inCallbackProc;
|
||
return 0;
|
||
}
|
||
|
||
|
||
OSStatus PASSING_AudioQueueNewInput(const AudioStreamBasicDescription *inFormat,
|
||
AudioQueueInputCallback inCallbackProc,
|
||
void *inUserData,
|
||
CFRunLoopRef inCallbackRunLoop,
|
||
CFStringRef inCallbackRunLoopMode,
|
||
UInt32 inFlags,
|
||
AudioQueueRef *outAQ)
|
||
{
|
||
*outAQ = (void *)0x1234567;
|
||
callForInput = inCallbackProc;
|
||
return 0;
|
||
}
|
||
|
||
typedef struct NotAudioQueueBuffer {
|
||
UInt32 mAudioDataBytesCapacity; // 4 (8)
|
||
void *mAudioData; // 8
|
||
UInt32 mAudioDataByteSize; // 4 (8)
|
||
void *__nullable mUserData; // 8
|
||
|
||
const UInt32 mPacketDescriptionCapacity; // 4 (8)
|
||
AudioStreamPacketDescription *const __nullable mPacketDescriptions; // (8)
|
||
UInt32 mPacketDescriptionCount; // 4
|
||
} NotAudioQueueBuffer;
|
||
|
||
OSStatus PASSING_AudioQueueAllocateBuffer(AudioQueueRef inAQ, UInt32 inBufferByteSize, AudioQueueBufferRef *outBuffer)
|
||
{
|
||
NotAudioQueueBuffer *buf = calloc(sizeof(AudioQueueBuffer) + inBufferByteSize, 1);
|
||
buf->mAudioData = ((uint8_t *)buf) + sizeof(struct AudioQueueBuffer);
|
||
buf->mAudioDataBytesCapacity = inBufferByteSize;
|
||
buf->mAudioDataByteSize = inBufferByteSize;
|
||
|
||
*outBuffer = (AudioQueueBufferRef)buf;
|
||
return 0;
|
||
}
|
||
|
||
OSStatus PASSING_AudioQueueFreeBuffer(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||
{
|
||
free(inBuffer);
|
||
return 0;
|
||
}
|
||
|
||
#if ! TARGET_OS_IPHONE
|
||
OSStatus PASSING_AudioObjectGetPropertyData(AudioObjectID inObjectID,
|
||
const AudioObjectPropertyAddress *inAddress,
|
||
UInt32 inQualifierDataSize,
|
||
const void *inQualifierData,
|
||
UInt32 *ioDataSize,
|
||
void *outData)
|
||
{
|
||
if (inObjectID == kAudioObjectSystemObject) {
|
||
CCCAssertTrue(outData != NULL);
|
||
CCCAssertTrue(*ioDataSize == sizeof(AudioDeviceID));
|
||
*(AudioDeviceID *)outData = 0x1234567;
|
||
return 0;
|
||
}
|
||
|
||
if (inAddress->mSelector == kAudioDevicePropertyDeviceUID) {
|
||
CFStringRef ret = CFSTR("PXS-04G");
|
||
*(CFStringRef *)outData = CFStringCreateCopy(nil, ret);
|
||
return 0;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
OSStatus FAILING_AudioObjectGetPropertyData(AudioObjectID inObjectID,
|
||
const AudioObjectPropertyAddress *inAddress,
|
||
UInt32 inQualifierDataSize,
|
||
const void *inQualifierData,
|
||
UInt32 *ioDataSize,
|
||
void *outData)
|
||
{
|
||
if (inObjectID == kAudioObjectSystemObject) {
|
||
return -1;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
OSStatus FAILING_AudioObjectGetPropertyData2(AudioObjectID inObjectID,
|
||
const AudioObjectPropertyAddress *inAddress,
|
||
UInt32 inQualifierDataSize,
|
||
const void *inQualifierData,
|
||
UInt32 *ioDataSize,
|
||
void *outData)
|
||
{
|
||
if (inObjectID == kAudioObjectSystemObject) {
|
||
*(AudioDeviceID *)outData = 0x1234567;
|
||
return 0;
|
||
}
|
||
if (inAddress->mSelector == kAudioDevicePropertyDeviceUID) {
|
||
return -1;
|
||
}
|
||
return -1;
|
||
}
|
||
#endif
|
||
|
||
OSStatus FAILING_AudioQueueSetProperty(AudioQueueRef inAQ,
|
||
AudioQueuePropertyID inID,
|
||
const void *inData,
|
||
UInt32 inDataSize)
|
||
{
|
||
if (inID == kAudioQueueProperty_CurrentDevice) {
|
||
CCCAssertNotEqual(inData, nil);
|
||
CCCAssertEqual(inDataSize, sizeof(CFStringRef *));
|
||
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
OSStatus PASSING_AudioQueueStart(AudioQueueRef inAQ,
|
||
const AudioTimeStamp *inStartTime)
|
||
{
|
||
CCCAssert(inAQ != NULL);
|
||
return 0;
|
||
}
|
||
|
||
|
||
DECLARE_GENERIC_PASS(_AudioQueueDispose)
|
||
DECLARE_GENERIC_PASS(_AudioQueueEnqueueBuffer)
|
||
DECLARE_GENERIC_PASS(_AudioQueueSetProperty)
|
||
DECLARE_GENERIC_PASS(_AudioQueueStop)
|
||
|
||
DECLARE_GENERIC_FAIL(_AudioQueueDispose)
|
||
DECLARE_GENERIC_FAIL(_AudioQueueEnqueueBuffer)
|
||
DECLARE_GENERIC_FAIL(_AudioQueueNewInput)
|
||
DECLARE_GENERIC_FAIL(_AudioQueueNewOutput)
|
||
DECLARE_GENERIC_FAIL(_AudioQueueStart)
|
||
DECLARE_GENERIC_FAIL(_AudioQueueStop)
|
||
|
||
@interface OCTAudioQueueTests : XCTestCase
|
||
|
||
@property (strong, nonatomic) id audioSession;
|
||
|
||
@end
|
||
|
||
@implementation OCTAudioQueueTests
|
||
|
||
- (void)setUp
|
||
{
|
||
[super setUp];
|
||
|
||
refToSelf = (__bridge void *)(self);
|
||
|
||
#if TARGET_OS_IPHONE
|
||
OCMStub([self.audioSession sampleRate]).andReturn(44100.00);
|
||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||
#endif
|
||
|
||
RESTORE_PATCHES
|
||
|
||
#if ! TARGET_OS_IPHONE
|
||
_AudioObjectGetPropertyData = PASSING_AudioObjectGetPropertyData;
|
||
#endif
|
||
}
|
||
|
||
- (void)tearDown
|
||
{
|
||
refToSelf = NULL;
|
||
|
||
self.audioSession = nil;
|
||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||
[super tearDown];
|
||
}
|
||
|
||
- (void)testInitOutput
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Crystinger" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
XCTAssertNotEqual([oq getBufferPointer], NULL);
|
||
}
|
||
|
||
- (void)testInitInput
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Daxx" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
XCTAssertNotEqual([oq getBufferPointer], NULL);
|
||
}
|
||
|
||
- (void)testInitFail
|
||
{
|
||
PATCH_FAILING(_AudioQueueNewOutput);
|
||
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Vint" error:&error];
|
||
|
||
XCTAssertNil(oq);
|
||
XCTAssertNotNil(error);
|
||
|
||
|
||
PATCH_FAILING(_AudioQueueNewInput);
|
||
|
||
error = nil;
|
||
oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Rita" error:&error];
|
||
|
||
XCTAssertNil(oq);
|
||
XCTAssertNotNil(error);
|
||
}
|
||
|
||
- (void)testInitLater
|
||
{
|
||
PATCH_FAILING(_AudioQueueSetProperty);
|
||
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Elgarde" error:&error];
|
||
|
||
XCTAssertNil(oq);
|
||
XCTAssertNotNil(error);
|
||
|
||
error = nil;
|
||
oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Kirin" error:&error];
|
||
|
||
XCTAssertNil(oq);
|
||
XCTAssertNotNil(error);
|
||
}
|
||
|
||
// setting devices only available on OS X
|
||
- (void)testSetDevice
|
||
{
|
||
#if ! TARGET_OS_IPHONE
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Undine" error:&error];
|
||
XCTAssertNotNil(oq);
|
||
|
||
BOOL ok = [oq setDeviceID:@"PXS-04G" error:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
PATCH_FAILING(_AudioQueueSetProperty);
|
||
ok = [oq setDeviceID:@"Nyx" error:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
#endif
|
||
}
|
||
|
||
- (void)testSetDefaultDevice
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Sayle" error:&error];
|
||
XCTAssertNotNil(oq);
|
||
|
||
#if ! TARGET_OS_IPHONE
|
||
BOOL ok = [oq setDeviceID:nil error:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
_AudioObjectGetPropertyData = FAILING_AudioObjectGetPropertyData;
|
||
ok = [oq setDeviceID:nil error:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
|
||
_AudioObjectGetPropertyData = FAILING_AudioObjectGetPropertyData2;
|
||
error = nil;
|
||
ok = [oq setDeviceID:nil error:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
#endif
|
||
}
|
||
|
||
- (void)testStartStop
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Naivy" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
XCTAssertNotEqual([oq getBufferPointer], NULL);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
ok = [oq stop:&error];
|
||
XCTAssertTrue(ok);
|
||
}
|
||
|
||
- (void)testStartFail
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Vervain" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
|
||
PATCH_FAILING(_AudioQueueStart);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
|
||
ok = [oq stop:&error];
|
||
XCTAssertTrue(ok);
|
||
}
|
||
|
||
- (void)testStopFail
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Raijin" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
|
||
PATCH_FAILING(_AudioQueueStop);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
ok = [oq stop:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
}
|
||
|
||
- (void)testChangeSampleRate
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Minari" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
ok = [oq updateSampleRate:96000.0 numberOfChannels:2.0 error:&error];
|
||
XCTAssertTrue(ok);
|
||
}
|
||
|
||
- (void)testChangeSampleRateFail
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Medis" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
PATCH_FAILING(_AudioQueueNewOutput);
|
||
|
||
ok = [oq updateSampleRate:96000.0 numberOfChannels:2.0 error:&error];
|
||
XCTAssertFalse(ok);
|
||
XCTAssertNotNil(error);
|
||
|
||
PATCH_PASSING(_AudioQueueNewOutput);
|
||
|
||
ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
}
|
||
|
||
- (void)testFillOutput
|
||
{
|
||
// TODO investigate failing test on travis
|
||
|
||
// NSError *error = nil;
|
||
// OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithOutputDeviceID:@"Shadogan" error:&error];
|
||
|
||
// XCTAssertNotNil(oq);
|
||
|
||
// BOOL ok = [oq begin:&error];
|
||
// XCTAssertTrue(ok);
|
||
|
||
// AudioQueueBufferRef buf;
|
||
|
||
// // Allocate some extra space because the implementation is supposed to fill
|
||
// // it with 0 if there's not enough data in the ring.
|
||
|
||
// PASSING_AudioQueueAllocateBuffer(nil, 32, &buf);
|
||
// XCTAssertNotEqual(buf, NULL);
|
||
|
||
// TPCircularBufferProduceBytes([oq getBufferPointer], pcm, 16);
|
||
// callForOutput((__bridge void *)(oq), (void *)0x1234567, buf);
|
||
|
||
// OCTToxAVPCMData checkPCM[16];
|
||
// memset((void *)checkPCM, 0, 32);
|
||
// memcpy((void *)checkPCM, pcm, 16);
|
||
|
||
// XCTAssertTrue(memcmp(buf->mAudioData, checkPCM, 32) == 0);
|
||
// PASSING_AudioQueueFreeBuffer(nil, buf);
|
||
}
|
||
|
||
- (void)testFillInput
|
||
{
|
||
NSError *error = nil;
|
||
OCTAudioQueue *oq = [[OCTAudioQueue alloc] initWithInputDeviceID:@"Nelly" error:&error];
|
||
|
||
XCTAssertNotNil(oq);
|
||
|
||
BOOL ok = [oq begin:&error];
|
||
XCTAssertTrue(ok);
|
||
|
||
AudioQueueBufferRef buf;
|
||
PASSING_AudioQueueAllocateBuffer(nil, 4, &buf);
|
||
XCTAssertNotEqual(buf, NULL);
|
||
|
||
__block BOOL sbreak = NO;
|
||
|
||
oq.sendDataBlock = ^(void *data, OCTToxAVSampleCount samples, OCTToxAVSampleRate srate, OCTToxAVChannels nchan) {
|
||
sbreak = YES;
|
||
};
|
||
|
||
int times = 0;
|
||
while (! sbreak && times < 2000) {
|
||
memcpy(buf->mAudioData, pcm, 4);
|
||
callForInput((__bridge void *_Nullable)(oq),
|
||
(void *)0x1234567,
|
||
buf,
|
||
(void *)0x1,
|
||
0,
|
||
NULL);
|
||
}
|
||
|
||
XCTAssertTrue(times < 2000);
|
||
// we just have to make sure sendDataBlock is called
|
||
XCTAssertTrue(sbreak);
|
||
PASSING_AudioQueueFreeBuffer(nil, buf);
|
||
}
|
||
|
||
@end
|