Automatically accept the input device permission dialog in UI tests.

This commit is contained in:
Kyle Neideck 2022-05-15 02:06:35 +10:00
parent ffd634245c
commit 058af733be
No known key found for this signature in database
GPG key ID: CAA8D9B8E39EC18C
2 changed files with 75 additions and 58 deletions

View file

@ -17,7 +17,7 @@
// BGMAppDelegate.mm
// BGMApp
//
// Copyright © 2016-2020 Kyle Neideck
// Copyright © 2016-2022 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
@ -118,6 +118,43 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
preferredOutputDevices =
[[BGMPreferredOutputDevices alloc] initWithDevices:audioDevices userDefaults:userDefaults];
// Skip this if we're compiling on a version of macOS before 10.14 as won't compile and it
// isn't needed.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 // MAC_OS_X_VERSION_10_14
if (@available(macOS 10.14, *)) {
// On macOS 10.14+ we need to get the user's permission to use input devices before we can
// use BGMDevice for playthrough (see BGMPlayThrough), so we wait until they've given it
// before making BGMDevice the default device. This way, if the user is playing audio when
// they open Background Music, we won't interrupt it while we're waiting for them to click
// OK.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
DebugMsg("BGMAppDelegate::applicationDidFinishLaunching: "
"Permission granted");
[self continueLaunchAfterInputDevicePermissionGranted];
} else {
NSLog(@"BGMAppDelegate::applicationDidFinishLaunching: "
"Permission denied");
// TODO: If they don't accept, Background Music won't work
// at all and the only way to fix it is in System
// Preferences, so we should show an error dialog
// with instructions.
}
});
}];
}
else
#endif
{
// We can change the device immediately on older versions of macOS because they don't
// require user permission for input devices.
[self continueLaunchAfterInputDevicePermissionGranted];
}
}
- (void) continueLaunchAfterInputDevicePermissionGranted {
// Choose an output device for BGMApp to use to play audio.
if (![self setInitialOutputDevice]) {
return;
@ -135,16 +172,16 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
autoPauseMusic = [[BGMAutoPauseMusic alloc] initWithAudioDevices:audioDevices
musicPlayers:musicPlayers];
[self setUpMainMenu];
xpcListener = [[BGMXPCListener alloc] initWithAudioDevices:audioDevices
helperConnectionErrorHandler:^(NSError* error) {
NSLog(@"BGMAppDelegate::applicationDidFinishLaunching: (helperConnectionErrorHandler) "
"BGMXPCHelper connection error: %@",
error);
[self showXPCHelperErrorMessage:error];
}];
NSLog(@"BGMAppDelegate::continueLaunchAfterInputDevicePermissionGranted: "
"(helperConnectionErrorHandler) BGMXPCHelper connection error: %@",
error);
[self showXPCHelperErrorMessage:error];
}];
}
// Returns NO if (and only if) BGMApp is about to terminate because of a fatal error.
@ -182,51 +219,13 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
// Sets the "Background Music" virtual audio device (BGMDevice) as the user's default audio device.
- (void) setBGMDeviceAsDefault {
void (^setDefaultDevice)() = ^{
NSError* error = [audioDevices setBGMDeviceAsOSDefault];
NSError* error = [audioDevices setBGMDeviceAsOSDefault];
if (error) {
[self showSetDeviceAsDefaultError:error
message:@"Could not set the Background Music device as your"
"default audio device."
informativeText:@"You might be able to change it yourself."];
}
};
// Skip this if we're compiling on a version of macOS before 10.14 as won't compile and it
// isn't needed.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 // MAC_OS_X_VERSION_10_14
if (@available(macOS 10.14, *)) {
// On macOS 10.14+ we need to get the user's permission to use input devices before we can
// use BGMDevice for playthrough (see BGMPlayThrough), so we wait until they've given it
// before making BGMDevice the default device. This way, if the user is playing audio when
// they open Background Music, we won't interrupt it while we're waiting for them to click
// OK.
//
// TODO: This isn't a perfect solution because, if the user takes too long to accept,
// BGMPlayThrough will try to use BGMDevice again and log some errors.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted) {
DebugMsg("BGMAppDelegate::setBGMDeviceAsDefault: "
"Permission granted");
setDefaultDevice();
} else {
NSLog(@"BGMAppDelegate::setBGMDeviceAsDefault: "
"Permission denied");
// TODO: If they don't accept, Background Music won't work
// at all and the only way to fix it is in System
// Preferences, so we should show an error dialog
// with instructions.
}
}];
}
else
#endif
{
// We can change the device immediately on older versions of macOS because they don't
// require user permission for input devices.
setDefaultDevice();
if (error) {
[self showSetDeviceAsDefaultError:error
message:@"Could not set the Background Music device as your"
"default audio device."
informativeText:@"You might be able to change it yourself."];
}
}

View file

@ -17,7 +17,7 @@
// BGMAppUITests.mm
// BGMAppUITests
//
// Copyright © 2017, 2018, 2020 Kyle Neideck
// Copyright © 2017, 2018, 2020, 2022 Kyle Neideck
//
// You might want to use Xcode's UI test recording feature if you add new tests.
//
@ -34,7 +34,6 @@
#import <XCTest/XCTest.h>
// TODO: Skip these tests if macOS SDK 10.11 or higher isn't available.
// TODO: Mock BGMDevice and music players.
#if __clang_major__ >= 9
@ -75,15 +74,24 @@
// would fail to start because of a bug in Xcode.
app.launchArguments = @[ @"--no-persistent-data", @"--show-dock-icon" ];
// Make the "Background Music wants to use the microphone" dialog appear every time so the test
// doesn't need logic to handle both cases.
if (@available(macOS 10.15.4, *)) {
[app resetAuthorizationStatusForResource:XCUIProtectedResourceMicrophone];
}
// Launch BGMApp.
[app launch];
[self acceptMicrophoneAuthorizationDialog];
if (![icon waitForExistenceWithTimeout:1.0]) {
// The status bar icon/button has this type when using older versions of XCTest, so try
// both. (Actually, it might depend on the macOS or Xcode version. I'm not sure.)
XCUIElement* iconOldType =
[app.menuBars childrenMatchingType:XCUIElementTypeMenuBarItem].element;
if (![iconOldType waitForExistenceWithTimeout:5.0]) {
if ([iconOldType waitForExistenceWithTimeout:5.0]) {
NSLog(@"icon = iconOldType");
icon = iconOldType;
}
}
@ -93,6 +101,18 @@
XCTAssert([icon waitForExistenceWithTimeout:10.0]);
}
// Clicks the OK button in the "Background Music wants to use the microphone" dialog.
- (void) acceptMicrophoneAuthorizationDialog {
XCUIApplication* unc =
[[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.UserNotificationCenter"];
NSLog(@"UserNotificationCenter: %@", unc);
XCUIElement* okButton = unc.dialogs.buttons[@"OK"];
XCTAssert([okButton waitForExistenceWithTimeout:10.0]);
[okButton click];
}
- (void) tearDown {
// Click the quit menu item.
if (!menuItems.count) {
@ -102,9 +122,7 @@
[menuItems[@"Quit Background Music"] click];
// BGMApp should quit.
for (NSRunningApplication* runningApp : [[NSWorkspace sharedWorkspace] runningApplications]) {
XCTAssertFalse([[runningApp bundleIdentifier] isEqualToString:@kBGMAppBundleID]);
}
XCTAssertTrue([app waitForState:XCUIApplicationStateNotRunning timeout:10.0]);
[super tearDown];
}