Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public struct _AttachableURLWrapper: Sendable {
/// thrown if a file descriptor to `url` or `copyURL` cannot be created.
init(url: URL, copiedToFileAt copyURL: URL? = nil, isCompressedDirectory: Bool) throws {
if isCompressedDirectory && copyURL == nil {
preconditionFailure("When attaching a directory to a test, the URL to its compressed copy must be supplied. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
preconditionFailure(reportBugMessage("When attaching a directory to a test, the URL to its compressed copy must be supplied."))
}
self.url = url
self.data = try Data(contentsOf: copyURL ?? url, options: [.mappedIfSafe])
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/ABI/Encoded/ABI.EncodedTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ extension ABI {

init(encoding testCase: borrowing Test.Case) {
guard let arguments = testCase.arguments else {
preconditionFailure("Attempted to initialize an EncodedTestCase encoding a test case which is not parameterized: \(testCase). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
preconditionFailure(reportBugMessage("Attempted to initialize an EncodedTestCase encoding a test case which is not parameterized: \(testCase)."))
}

// TODO: define an encodable form of Test.Case.ID
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Attachments/Attachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ extension Configuration {
}

guard case let .valueAttached(attachment) = event.kind else {
preconditionFailure("Passed the wrong kind of event to \(#function) (expected valueAttached, got \(event.kind)). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
preconditionFailure(reportBugMessage("Passed the wrong kind of event to \(#function) (expected valueAttached, got \(event.kind))."))
}
if attachment.fileSystemPath != nil {
// Somebody already saved this attachment. This isn't necessarily a logic
Expand Down
1 change: 1 addition & 0 deletions Sources/Testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ add_library(Testing
Support/Additions/TaskAdditions.swift
Support/Additions/WinSDKAdditions.swift
Support/Allocated.swift
Support/BugReporting.swift
Support/CartesianProduct.swift
Support/CError.swift
Support/CustomIssueRepresentable.swift
Expand Down
4 changes: 2 additions & 2 deletions Sources/Testing/ExitTests/ExitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ extension ExitTest {

// Set ExitTest.current before the test body runs.
Self._current.withLock { current in
precondition(current == nil, "Set the current exit test twice in the same process. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
precondition(current == nil, reportBugMessage("Set the current exit test twice in the same process."))
current = self.unsafeCopy()
}

Expand Down Expand Up @@ -1095,7 +1095,7 @@ extension ExitTest {
}
let capturedValuesJSON = try fileHandle.readToEnd()
let capturedValuesJSONLines = capturedValuesJSON.split(whereSeparator: \.isASCIINewline)
assert(capturedValues.count == capturedValuesJSONLines.count, "Expected to decode \(capturedValues.count) captured value(s) for the current exit test, but received \(capturedValuesJSONLines.count). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
assert(capturedValues.count == capturedValuesJSONLines.count, reportBugMessage("Expected to decode \(capturedValues.count) captured value(s) for the current exit test, but received \(capturedValuesJSONLines.count)."))

// Walk the list of captured values' types, map them to their JSON blobs,
// and decode them.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Testing/ExitTests/SpawnProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func spawnExecutable(
func inherit(_ fileHandle: borrowing FileHandle, as standardFD: CInt? = nil) throws {
try fileHandle.withUnsafePOSIXFileDescriptor { fd in
guard let fd else {
throw SystemError(description: "A child process cannot inherit a file handle without an associated file descriptor. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
throw SystemError(description: reportBugMessage("A child process cannot inherit a file handle without an associated file descriptor."))
}
if let standardFD, standardFD != fd {
_ = posix_spawn_file_actions_adddup2(fileActions, fd, standardFD)
Expand Down Expand Up @@ -241,7 +241,7 @@ func spawnExecutable(
func inherit(_ fileHandle: borrowing FileHandle) throws -> HANDLE? {
try fileHandle.withUnsafeWindowsHANDLE { windowsHANDLE in
guard let windowsHANDLE else {
throw SystemError(description: "A child process cannot inherit a file handle without an associated Windows handle. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
throw SystemError(description: reportBugMessage("A child process cannot inherit a file handle without an associated Windows handle."))
}

// Ensure the file handle can be inherited by the child process.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Testing/ExitTests/WaitFor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private func _blockAndWait(for pid: consuming pid_t) throws -> ExitStatus {
case .init(CLD_KILLED), .init(CLD_DUMPED):
return .signal(siginfo.si_status)
default:
throw SystemError(description: "Unexpected siginfo_t value. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new and include this information: \(String(reflecting: siginfo))")
throw SystemError(description: reportBugMessage("Unexpected siginfo_t value.", context: String(reflecting: siginfo)))
}
} else if case let errorCode = swt_errno(), errorCode != EINTR {
throw CError(rawValue: errorCode)
Expand Down Expand Up @@ -247,7 +247,7 @@ func wait(for pid: consuming pid_t) async throws -> ExitStatus {
// we add this continuation to the dictionary, then it will simply loop
// and report the status again.
let oldContinuation = childProcessContinuations.updateValue(continuation, forKey: pid)
assert(oldContinuation == nil, "Unexpected continuation found for PID \(pid). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
assert(oldContinuation == nil, reportBugMessage("Unexpected continuation found for PID \(pid)."))

// Wake up the waiter thread if it is waiting for more child processes.
_ = pthread_cond_signal(_waitThreadNoChildrenCondition)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Support/Additions/TaskAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func decorateTaskName(_ taskName: String?, withAction action: String?) -> String
let prefix = "[Swift Testing]"
return taskName.map { taskName in
#if DEBUG
precondition(!taskName.hasPrefix(prefix), "Applied prefix '\(prefix)' to task name '\(taskName)' twice. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
precondition(!taskName.hasPrefix(prefix), reportBugMessage("Applied prefix '\(prefix)' to task name '\(taskName)' twice."))
#endif
let action = action.map { " - \($0)" } ?? ""
return "\(prefix) \(taskName)\(action)"
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Support/Additions/WinSDKAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ let STATUS_SIGNAL_CAUGHT_BITS = {
#if DEBUG
assert(
(result & STATUS_CODE_MASK) == 0,
"Constructed NTSTATUS mask \(String(result, radix: 16)) encroached on code bits. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new"
reportBugMessage("Constructed NTSTATUS mask \(String(result, radix: 16)) encroached on code bits.")
)
#endif

Expand Down
29 changes: 29 additions & 0 deletions Sources/Testing/Support/BugReporting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

/// Construct a message describing a problem and inviting a user to file a bug
/// report.
///
/// - Parameters:
/// - message: A description of the problem encountered.
/// - context: Optional additional diagnostic information to include with the
/// bug report request.
///
/// - Returns: A string combining `message` with a standard request to file a
/// bug report (with a URL provided), optionally followed by `context`.
///
/// This function is not part of the public interface of the testing library.
package func reportBugMessage(_ message: String, context: String? = nil) -> String {
var result = "\(message) Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new"
if let context {
result += " and include this information: \(context)"
}
return result
}
2 changes: 1 addition & 1 deletion Sources/Testing/Test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public struct Test: Sendable {
// error (because the test cannot be run.) If an error was thrown, a
// `Runner.Plan` is expected to record issue for the test, rather than
// attempt to run it, and thus never access this property.
preconditionFailure("Attempting to access test cases with invalid state. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new and include this information: \(String(reflecting: testCasesState))")
preconditionFailure(reportBugMessage("Attempting to access test cases with invalid state.", context: String(reflecting: testCasesState)))
}
return AnySequence(testCases)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Traits/AttachmentSavingTrait.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ extension AttachmentSavingTrait: TestScoping {

public func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
guard var configuration = Configuration.current else {
throw SystemError(description: "There is no current Configuration when attempting to provide scope for test '\(test.name)'. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
throw SystemError(description: reportBugMessage("There is no current Configuration when attempting to provide scope for test '\(test.name)'."))
}
let oldConfiguration = configuration

Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Traits/IssueHandlingTrait.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension IssueHandlingTrait: TestScoping {
/// issue.
func provideScope(performing function: @Sendable () async throws -> Void) async throws {
guard var configuration = Configuration.current else {
preconditionFailure("Configuration.current is nil when calling \(#function). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
preconditionFailure(reportBugMessage("Configuration.current is nil when calling \(#function)."))
}

configuration.eventHandler = { [oldConfiguration = configuration] event, context in
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Traits/ParallelizationTrait.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extension ParallelizationTrait: TestScoping {

public func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws {
guard var configuration = Configuration.current else {
throw SystemError(description: "There is no current Configuration when attempting to provide scope for test '\(test.name)'. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
throw SystemError(description: reportBugMessage("There is no current Configuration when attempting to provide scope for test '\(test.name)'."))
}

configuration.isParallelizationEnabled = false
Expand Down
1 change: 1 addition & 0 deletions Sources/TestingMacros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ target_sources(TestingMacros PRIVATE
Support/Argument.swift
Support/AttributeDiscovery.swift
Support/AvailabilityGuards.swift
Support/BugReporting.swift
Support/ClosureCaptureListParsing.swift
Support/CommentParsing.swift
Support/ConditionArgumentParsing.swift
Expand Down
2 changes: 1 addition & 1 deletion Sources/TestingMacros/ConditionMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ extension ExitTestConditionMacro {
var arguments = argumentList(of: macro, in: context)
let trailingClosureIndex = arguments.firstIndex { $0.label?.tokenKind == _trailingClosureLabel.tokenKind }
guard let trailingClosureIndex else {
fatalError("Could not find the body argument to this exit test. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
fatalError(reportBugMessage("Could not find the body argument to this exit test."))
}

let conditionExpr = arguments.first { $0.label?.tokenKind == .identifier("processExitsWith") }?.expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension DeclGroupSyntax {
} else if let extensionDecl = `as`(ExtensionDeclSyntax.self) {
return extensionDecl.extendedType
}
fatalError("Unexpected DeclGroupSyntax type \(Swift.type(of: self)). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
fatalError(reportBugMessage("Unexpected DeclGroupSyntax type \(Swift.type(of: self))."))
}

/// Check whether or not this instance includes a given type name in its
Expand Down
2 changes: 1 addition & 1 deletion Sources/TestingMacros/Support/AvailabilityGuards.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private func _createAvailabilityTraitExpr(
}

default:
fatalError("Unsupported keyword \(whenKeyword) passed to \(#function). Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new")
fatalError(reportBugMessage("Unsupported keyword \(whenKeyword) passed to \(#function)."))
}
}

Expand Down
27 changes: 27 additions & 0 deletions Sources/TestingMacros/Support/BugReporting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

/// Construct a message describing a problem and inviting a user to file a bug
/// report.
///
/// - Parameters:
/// - message: A description of the problem encountered.
/// - context: Optional additional diagnostic information to include with the
/// bug report request.
///
/// - Returns: A string combining `message` with a standard request to file a
/// bug report (with a URL provided), optionally followed by `context`.
func reportBugMessage(_ message: String, context: String? = nil) -> String {
var result = "\(message) Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new"
if let context {
result += " and include this information: \(context)"
}
return result
}
Loading