diff --git a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift index e241bd344..2e1b92443 100644 --- a/Sources/Testing/ABI/EntryPoints/EntryPoint.swift +++ b/Sources/Testing/ABI/EntryPoints/EntryPoint.swift @@ -8,6 +8,9 @@ // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // +#if canImport(Foundation) +private import Foundation +#endif private import _TestingInternals #if canImport(Synchronization) @@ -622,9 +625,14 @@ public func configurationForEntryPoint(from args: __CommandLineArguments_v0) thr // Attachment output. if let attachmentsPath = args.attachmentsPath { - guard fileExists(atPath: attachmentsPath) else { - throw _EntryPointError.invalidArgument("---attachments-path", value: attachmentsPath) - } + + #if canImport(Foundation) + try FileManager().createDirectory(atPath: attachmentsPath, withIntermediateDirectories: true) + #else + guard fileExists(atPath: attachmentsPath) else { + throw _EntryPointError.invalidArgument("---attachments-path", value: attachmentsPath) + } + #endif configuration.attachmentsPath = attachmentsPath } diff --git a/Sources/Testing/Testing.docc/Attachments.md b/Sources/Testing/Testing.docc/Attachments.md index 018576d95..9e28633e4 100644 --- a/Sources/Testing/Testing.docc/Attachments.md +++ b/Sources/Testing/Testing.docc/Attachments.md @@ -244,8 +244,8 @@ after your tests finish running: `.build/attachments` by default. Visual Studio Code reports the paths to individual attachments in its Tests Results panel. - When using Swift Package Manager's `swift test` command, you can pass the - `--attachments-path` option. The testing library saves attachments to the - specified directory. + `--attachments-path` option. The testing library creates a directory at the + specified path and saves attachments to it. If you do not pass the `--attachments-path` option, the testing library does not save any attachments you record. diff --git a/Tests/TestingTests/SwiftPMTests.swift b/Tests/TestingTests/SwiftPMTests.swift index 7a8909706..0941a836d 100644 --- a/Tests/TestingTests/SwiftPMTests.swift +++ b/Tests/TestingTests/SwiftPMTests.swift @@ -10,6 +10,9 @@ @testable @_spi(Experimental) @_spi(ForToolsIntegrationOnly) import Testing private import _TestingInternals +#if canImport(Foundation) +private import Foundation +#endif private func configurationForEntryPoint(withArguments args: [String]) throws -> Configuration { let args = try parseCommandLineArguments(from: args) @@ -385,6 +388,57 @@ struct SwiftPMTests { #expect(fileContents.contains(UInt8(ascii: ">"))) } + #if canImport(Foundation) + @Test( + "--attachments-path argument (creates missing directory)", + arguments: ["--attachments-path", "--experimental-attachments-path"] + ) + func attachmentsPathCreatesMissingDirectory(argumentName: String) throws { + let tempDirPath = try temporaryDirectory() + let attachmentsPath = appendPathComponent("swt_attachments_\(UInt64.random(in: 0 ..< .max))", to: tempDirPath) + defer { + _ = remove(attachmentsPath) + } + #expect(!fileExists(atPath: attachmentsPath)) + let configuration = try configurationForEntryPoint(withArguments: ["PATH", argumentName, attachmentsPath]) + #expect(fileExists(atPath: attachmentsPath)) + let actualPath = try #require(configuration.attachmentsPath, "Attachments path is not expected to be nil") + #expect(canonicalizePath(actualPath) == canonicalizePath(attachmentsPath)) + } + #endif + + #if canImport(Foundation) + @Test("--attachments-path argument (bad path)") + func attachmentsPathWithBadPath() throws { + let tempDirPath = try temporaryDirectory() + let attachmentPath = appendPathComponent(UUID().uuidString, to: tempDirPath) + let fileManager = FileManager() + let success = fileManager.createFile(atPath: attachmentPath, contents: nil, ) + if !success { + Issue.record("Test setup failure. Could not create file at \(attachmentPath).") + } + defer { + print("removing \(attachmentPath) ...") + _ = remove(attachmentPath) + } + #expect(throws: (any Error).self, "Attachment path is: \(attachmentPath)") { + _ = try configurationForEntryPoint(withArguments: ["PATH", "--attachments-path", attachmentPath]) + } + } + #endif + + @Test("--attachments-path argument (accepts existing directory)") + func attachmentsPathAcceptsExistingDirectory() throws { + let tempDirPath = try temporaryDirectory() + #expect(fileExists(atPath: tempDirPath)) + let configuration = try configurationForEntryPoint(withArguments: ["PATH", "--attachments-path", tempDirPath]) + let actualPath = try #require(configuration.attachmentsPath, "Attachments path is not expected to be nil") + #expect( + canonicalizePath(actualPath) == canonicalizePath(tempDirPath), + "Canonicalized actual path (\(actualPath)) is not equal to canonicalized expected path (\(tempDirPath))", + ) + } + @Test("--configuration-path argument", arguments: [ "--configuration-path", "--experimental-configuration-path", ])