I'm working on converting tests from XCTest to Swift Testing. While that went well for the most part already, I struggle with test cases that run on files on disk - either in resource folders as part of the repository or in temporary directories.
The idea is to use traits that handle the directory switch. However, due to the async nature of the Swift Testing runtime, the current directory (as a global state) can change while a single test case is executed. Whether a suite runs .serialized, entirely on a single actor or with --no-parallel doesn't change this behavior.
My naïve implementation of a .workingDirectory(path) trait is like:
public struct WorkingDirectory: TestScoping, SuiteTrait, TestTrait {
fileprivate let path: String
public func provideScope(
for _: Test,
testCase _: Test.Case?,
performing function: () async throws -> Void
) async throws {
let previousDirectory = FileManager.default.currentDirectoryPath
#expect(FileManager.default.changeCurrentDirectoryPath(path))
try await function()
#expect(FileManager.default.changeCurrentDirectoryPath(previousDirectory))
}
}
public extension Trait where Self == WorkingDirectory {
static func workingDirectory(_ path: @autoclosure () -> String) -> Self { Self(path: path()) }
}
Because of the suspension point at try await function(), test execution can be interrupted by another task that might change the directory as well, so that the test later fails due to an incorrect working directory.
Is there a way to run these tests reliably within Swift Testing or is file system access and its async nature inherent incompatible?