Swift Testing environment differences from regular executable

I am working on a Swift package which uses CoreAudio, and includes some tests in a testTarget which use the Testing framework, and a couple of executableTarget targets which exercise the same code. I'm using Xcode 16.2 on macOS 15.3.1.

One of the things I do in the test code is create a HAL plugin, then find that plugin using the kAudioHardwarePropertyTranslateUIDToDevice.

Finding the plugin that I just created always fails from within a Swift Testing test, unless I run the test which creates the plugin individually first, then separately, run the test which finds the plugin, by clicking on the little arrows next to the function names.

If I put the tests in a serialized suite (so creation always happens first, then finding), running the suite always fails - it creates the plugin, but can't find it. If I run the 'find my plugin' test again manually, it is always found.

If I call the same functions from a regular executable (the thing created by a "executableTarget" in my .package.swift file), the just-created plugin is always found.

Is there a way to mimic the runtime environment of a regular executable in a Swift Testing target, or am I misunderstanding something?

this my be related to this issue: https://github.com/swiftlang/swift/issues/76882 but I don't understand it well enough to be sure.

Answered by ssmith_c in 825736022

I lied. Inadvertently. Sorry.

I put all the code (create and find) into one function and ran it from a standalone executable - it fails.

If I create two separate standalone executables, one that creates, and another that finds, and run those one after the other, the find executable succeeds.

In the single-executable case, I can add this line between creation and finding to make it work:

RunLoop.main.run(until: Date().addingTimeInterval(0.5))

0.1 seconds was not long enough. I'm not sure about the 0.5s because I've only tried it once.

I'm interested in doing this in a test because I'd like to create and destroy many such plugins to check for leaks. But I'm not keen on hard-coded delays. Is there a way to detect when CoreAudio is ready to be interrogated again (rather like IORegistryWaitQuiet)? And is there a way to run a runloop within a Swift Test?

I’m not sure what’s going on here, but I have a… ahem… test that you might run (-:

If you put the code that creates that plug-in and the code that finds the plug-in within the same test — so within the same function, call create and find — what does that do?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn that's what I originally had - within one test, create, then find. That always fails when run from a Swift Test. Same code (of course without all the #expect stuff) run from a command line executable - always works.

I lied. Inadvertently. Sorry.

I put all the code (create and find) into one function and ran it from a standalone executable - it fails.

If I create two separate standalone executables, one that creates, and another that finds, and run those one after the other, the find executable succeeds.

In the single-executable case, I can add this line between creation and finding to make it work:

RunLoop.main.run(until: Date().addingTimeInterval(0.5))

0.1 seconds was not long enough. I'm not sure about the 0.5s because I've only tried it once.

I'm interested in doing this in a test because I'd like to create and destroy many such plugins to check for leaks. But I'm not keen on hard-coded delays. Is there a way to detect when CoreAudio is ready to be interrogated again (rather like IORegistryWaitQuiet)? And is there a way to run a runloop within a Swift Test?

Written by ssmith_c in 825736022
Is there a way to detect when CoreAudio is ready to be interrogated again … ?

I don’t know enough about Core Audio to answer that. You could try starting a new thread in Media Technologies > Audio that’s specifically focused on that question.

Written by ssmith_c in 825736022
And is there a way to run a runloop within a Swift Test?

That should be feasible, as long as you do it from a synchronous function (async functions can’t access the run loop IIRC). However, without knowing more about Core Audio, it’s not clear whether it’ll actually help.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Swift Testing environment differences from regular executable
 
 
Q