Does swift-testing support custom initialization of its process / threads?

This feature is important on Windows as it allows initializing COM (CoInitialize) or WinRT (RoInitialize), which is necessary for calling certain platform APIs including all modern WinRT APIs. These calls are usually performed once per thread and it's not desirable to initialize and uninitialize multiple times on the same thread as would be the case if the code was in a test case setup/teardown method pair. The calls could be baked into swift-testing, although it can be valuable for the test author to control the arguments passed to those functions.

XCTest does not offer this feature and it makes it problematic for running tests on Windows.

Hey, thanks for the question! swift-testing uses Swift concurrency, but does not directly use threads. A change to support thread initialization/deinitialization may be better-suited for the Swift concurrency library rather than swift-testing specifically.

@compnerd can probably speak more about Swift's capabilities when it comes to Windows-specific threading APIs.

With task executor preference it should be possible to create a custom thread and use it as a task executor which is then used by swift-testing. It might require some APIs in swift-testing that allow you to hook every call to an async test method similar to BeforeEach which other testing libraries offer while allowing the hook to drive the actual test invocation. But this would be an avenue to explore and doesn't require hooking the global executor.

1 Like

This is a really interesting idea, and I think that I find it to be better than an interim solution that we have arrived at with libdispatch. We now do initialise COM on the dispatch worker threads (see [Win32] Initialize COM library on queue threads · apple/swift-corelibs-libdispatch@be275d4 · GitHub). But having a generic mechanism for setting up an actor thread seems very useful and could allow us to eventually see a world where that is no longer baked into the runtime itself and allows us to initialise the necessary support more dynamically.

I expect that @Douglas_Gregor and @John_McCall might have some thoughts about such an extension to the actor model as well as this could tie in very well with custom executors.

Thanks! It looks like we'll need work in Swift concurrency for multithreaded execution scenarios.

What about if we use swift-testing without concurrent test execution? Can the test author control the func main entry point of the test executable, or can we set up a hook to add custom one-time initialization code?

swift-testing's entry points are all necessarily async, so they'll all run on Swift's concurrent thread pool instead of the immediate calling thread even if parallelization is disabled. That means that, if you do thread-specific initialization before calling into swift-testing, it won't affect the state of the thread(s) swift-testing executes on.

This behaviour is not unique to swift-testing and is how Swift concurrency behaves in the general case.

What's been suggested above is to create a custom executor that is bound to some specific thread. That executor can then ensure the thread is set up and torn down correctly. You can then use that executor with a custom global actor and annotate your thread-sensitive test functions so that they are isolated to that actor.

1 Like