Add BGM_Driver tests: get/set the music player bundle ID property.

This commit is contained in:
Kyle Neideck 2016-07-04 16:25:42 +10:00
parent 55e9f60774
commit 679d624860
5 changed files with 116 additions and 10 deletions

View file

@ -32,6 +32,11 @@
// The music player classes written so far use Scripting Bridge to communicate with the music
// player apps (see iTunes.h/Spotify.h) but any other way is fine too.
//
// If you're not sure what bundle ID the music player uses, install a debug build of BGMDriver
// and play something in the music player. The easiest way is to do
// build_and_install.sh -d
// BGMDriver will log the bundle ID to system.log when it becomes aware of the music player.
//
// System Includes
#import <Foundation/Foundation.h>

View file

@ -215,6 +215,8 @@ UInt32 BGM_Device::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientP
void BGM_Device::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
{
ThrowIfNULL(outData, BGM_RuntimeException(), "BGM_Device::GetPropertyData: !outData");
if(inObjectID == mObjectID)
{
Device_GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
@ -235,6 +237,8 @@ void BGM_Device::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, co
void BGM_Device::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
{
ThrowIfNULL(inData, BGM_RuntimeException(), "BGM_Device::SetPropertyData: no data");
if(inObjectID == mObjectID)
{
Device_SetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
@ -1054,6 +1058,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
ThrowIf(inDataSize < sizeof(Float64), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_SetPropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate");
// BGMDevice's input and output sample rates are the same, so we just pass the request to Stream_SetPropertyData for one of them
// TODO: Set the property for both streams just in case that assumption changes? (As unlikely as that is.)
Float64 theNewSampleRate = *reinterpret_cast<const Float64*>(inData);
@ -1067,7 +1072,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
theStreamFormat.mSampleRate = theNewSampleRate;
Stream_SetPropertyData(inObjectID, inClientPID, theStreamPropertyAddress, 0, NULL, theStreamFormatDataSize, &theStreamFormat);
Stream_SetPropertyData(kObjectID_Stream_Input, inClientPID, theStreamPropertyAddress, 0, NULL, theStreamFormatDataSize, &theStreamFormat);
}
break;
@ -1116,15 +1121,17 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
{
ThrowIf(inDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_SetPropertyData: wrong size for the data for kAudioDeviceCustomPropertyMusicPlayerBundleID");
CFStringRef theBundleIDRef = *reinterpret_cast<const CFStringRef*>(inData);
ThrowIfNULL(theBundleIDRef, CAException(kAudioHardwareIllegalOperationError), "BGM_Device::Device_SetPropertyData: kAudioDeviceCustomPropertyMusicPlayerBundleID cannot be set to NULL");
ThrowIf(CFGetTypeID(theBundleIDRef) != CFStringGetTypeID(), CAException(kAudioHardwareIllegalOperationError), "BGM_Device::Device_SetPropertyData: CFType given for kAudioDeviceCustomPropertyMusicPlayerBundleID was not a CFString");
CAMutex::Locker theStateLocker(mStateMutex);
CFRetain(theBundleIDRef);
CACFString bundleID(theBundleIDRef);
bool propertyWasChanged = mClients.SetMusicPlayer(bundleID);
if(propertyWasChanged)
@ -1489,7 +1496,6 @@ void BGM_Device::Stream_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
CAMutex::Locker theStateLocker(mStateMutex);
Float64 theOldSampleRate = _HW_GetSampleRate();
// make sure that the new value is different than the old value
if(theNewSampleRate != theOldSampleRate)
{
mPendingSampleRate = theNewSampleRate;
@ -2281,7 +2287,7 @@ SInt32 BGM_Device::_HW_GetVolumeControlValue(int inObjectID) const
return mOutputMasterVolumeControlRawValueShadow;
};
throw (CAException(kAudioHardwareBadObjectError));
Throw(CAException(kAudioHardwareBadObjectError));
}
kern_return_t BGM_Device::_HW_SetVolumeControlValue(int inObjectID, SInt32 inNewControlValue)

View file

@ -561,7 +561,8 @@ static OSStatus BGM_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioOb
{
// check the arguments
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_SetPropertyData: bad driver reference");
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no address");
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no address");
ThrowIfNULL(inData, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no data");
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))

View file

@ -26,25 +26,118 @@
// Local Includes
#include "BGM_TestUtils.h"
// BGMDriver Includes
#include "BGM_Types.h"
// PublicUtility Includes
#include "CAException.h"
@interface BGM_DeviceTests : XCTestCase
@end
@implementation BGM_DeviceTests {
}
- (void)setUp {
@implementation BGM_DeviceTests
- (void) setUp {
[super setUp];
}
- (void)tearDown {
- (void) tearDown {
// Reminder: add code here, above the super call
[super tearDown];
}
- (void) testCustomPropertyMusicPlayerBundleID {
BGM_Device& device = BGM_Device::GetInstance();
// Convenience wrappers
auto getBundleID = [&](UInt32 inDataSize = sizeof(CFStringRef)){
CFStringRef bundleID = NULL;
UInt32 outDataSize;
device.GetPropertyData(/* inObjectID = */ kObjectID_Device,
/* inClientPID = */ 3,
/* inAddress = */ kBGMMusicPlayerBundleIDAddress,
/* inQualifierDataSize = */ 0,
/* inQualifierData = */ NULL,
/* inDataSize = */ inDataSize,
/* outDataSize = */ outDataSize,
/* outData = */ reinterpret_cast<void* __nonnull>(&bundleID));
// This isn't technically required, but we're unlikely to ever want to return any more/less data from GetPropertyData.
XCTAssertEqual(outDataSize, sizeof(CFStringRef));
return (__bridge_transfer NSString*)bundleID;
};
auto setBundleID = [&](const CFStringRef* __nullable bundleID, UInt32 dataSize = sizeof(CFStringRef)){
device.SetPropertyData(/* inObjectID = */ kObjectID_Device,
/* inClientPID = */ 1234,
/* inAddress = */ kBGMMusicPlayerBundleIDAddress,
/* inQualifierDataSize = */ 0,
/* inQualifierData = */ NULL,
/* inDataSize = */ dataSize,
/* inData = */ reinterpret_cast<const void* __nonnull>(bundleID));
};
// Should be set to the empty string by default.
XCTAssertEqualObjects(getBundleID(), @"");
// Should be able to set the property to an arbitrary string. (Purposefully not using CFSTR for this one just in case it
// makes a difference.)
CFStringRef newID = CFStringCreateWithCString(kCFAllocatorDefault, "test.bundle.ID", kCFStringEncodingUTF8);
setBundleID(&newID);
CFRelease(newID);
XCTAssertEqualObjects(getBundleID(), @"test.bundle.ID");
// Should be able to set the property back to the empty string.
newID = CFSTR("");
setBundleID(&newID);
XCTAssertEqualObjects(getBundleID(), @"");
// Arguments should be null-checked.
BGMShouldThrow<BGM_RuntimeException>(self, [&](){
UInt32 outDataSize;
device.GetPropertyData(kObjectID_Device, 0, kBGMMusicPlayerBundleIDAddress, 0, NULL, sizeof(CFStringRef),
outDataSize, /* outData = */ reinterpret_cast<void* __nonnull>(NULL));
});
BGMShouldThrow<BGM_RuntimeException>(self, [&](){
setBundleID(NULL);
});
// Invalid data should be rejected.
BGMShouldThrow<CAException>(self, [&](){
setBundleID((CFStringRef*)&kCFNull);
});
BGMShouldThrow<CAException>(self, [&](){
CFStringRef nullRef = NULL;
setBundleID(&nullRef);
});
BGMShouldThrow<CAException>(self, [&](){
CFArrayRef array = (__bridge_retained CFArrayRef)@[ @1, @2 ];
setBundleID((CFStringRef*)&array);
});
// Should throw if not given enough space for the return data.
BGMShouldThrow<CAException>(self, [&](){
getBundleID(/* inDataSize = */ 0);
});
newID = CFSTR("bundle");
// Passing more data than needed should be fine as long as it starts with a CFStringRef.
setBundleID(&newID, sizeof(CFStringRef) * 2);
// Should throw if not enough data is passed.
BGMShouldThrow<CAException>(self, [&](){
setBundleID(&newID, sizeof(CFStringRef) - 1);
});
}
// TODO: Performance tests?
- (void)testPerformanceExample {
- (void) testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.

View file

@ -169,6 +169,7 @@ class BGM_InvalidClientException { };
class BGM_InvalidClientPIDException { };
class BGM_InvalidClientRelativeVolumeException { };
class BGM_DeviceNotSetException { };
class BGM_RuntimeException { };
#endif