Skip to content
Merged
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
1 change: 1 addition & 0 deletions Sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ endif()
include(AvailabilityDefinitions)
include(CompilerSettings)
add_subdirectory(_TestDiscovery)
add_subdirectory(_TestingInterop)
add_subdirectory(_TestingInternals)
add_subdirectory(Overlays)
add_subdirectory(Testing)
24 changes: 24 additions & 0 deletions Sources/_TestingInterop/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2025 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors

add_library(_TestingInterop
FallbackEventHandler.swift)

target_link_libraries(_TestingInterop PRIVATE
_TestingInternals)
if(NOT BUILD_SHARED_LIBS)
# When building a static library, tell clients to autolink the internal
# libraries.
target_compile_options(_TestingInterop PRIVATE
"SHELL:-Xfrontend -public-autolink-library -Xfrontend _TestingInternals")
endif()
target_compile_options(_TestingInterop PRIVATE
-enable-library-evolution
-emit-module-interface -emit-module-interface-path $<TARGET_PROPERTY:_TestingInterop,Swift_MODULE_DIRECTORY>/_TestingInterop.swiftinterface)

_swift_testing_install_target(_TestingInterop)
106 changes: 106 additions & 0 deletions Sources/_TestingInterop/FallbackEventHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//

#if !SWT_NO_INTEROP
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
private import _TestingInternals
#else
private import Synchronization
#endif

#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
/// The installed event handler.
private nonisolated(unsafe) let _fallbackEventHandler = {
let result = ManagedBuffer<FallbackEventHandler?, os_unfair_lock>.create(
minimumCapacity: 1,
makingHeaderWith: { _ in nil }
)
result.withUnsafeMutablePointerToHeader { $0.initialize(to: nil) }
return result
}()
#else
/// The installed event handler.
private nonisolated(unsafe) let _fallbackEventHandler = Atomic<UnsafeRawPointer?>(nil)
#endif

/// A type describing a fallback event handler that testing API can invoke as an
/// alternate method of reporting test events to the current test runner.
///
/// For example, an `XCTAssert` failure in the body of a Swift Testing test
/// cannot record issues directly with the Swift Testing runner. Instead, the
/// framework packages the assertion failure as a JSON `Event` and invokes this
/// handler to report the failure.
///
/// - Parameters:
/// - recordJSONSchemaVersionNumber: The JSON schema version used to encode
/// the event record.
/// - recordJSONBaseAddress: A pointer to the first byte of the encoded event.
/// - recordJSONByteCount: The size of the encoded event in bytes.
/// - reserved: Reserved for future use.
@usableFromInline
package typealias FallbackEventHandler = @Sendable @convention(c) (
_ recordJSONSchemaVersionNumber: UnsafePointer<CChar>,
_ recordJSONBaseAddress: UnsafeRawPointer,
_ recordJSONByteCount: Int,
_ reserved: UnsafeRawPointer?
) -> Void

/// Get the current fallback event handler.
///
/// - Returns: The currently-set handler function, if any.
@_cdecl("_swift_testing_getFallbackEventHandler")
@usableFromInline
package func _swift_testing_getFallbackEventHandler() -> FallbackEventHandler? {
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
os_unfair_lock_lock(lock)
defer {
os_unfair_lock_unlock(lock)
}
return fallbackEventHandler.pointee
}
#else
return _fallbackEventHandler.load(ordering: .sequentiallyConsistent).flatMap { fallbackEventHandler in
unsafeBitCast(fallbackEventHandler, to: FallbackEventHandler?.self)
}
#endif
}

/// Set the current fallback event handler if one has not already been set.
///
/// - Parameters:
/// - handler: The handler function to set.
///
/// - Returns: Whether or not `handler` was installed.
///
/// The fallback event handler can only be installed once per process, typically
/// by the first testing library to run. If this function has already been
/// called and the handler set, it does not replace the previous handler.
@_cdecl("_swift_testing_installFallbackEventHandler")
@usableFromInline
package func _swift_testing_installFallbackEventHandler(_ handler: FallbackEventHandler) -> CBool {
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
os_unfair_lock_lock(lock)
defer {
os_unfair_lock_unlock(lock)
}
guard fallbackEventHandler.pointee == nil else {
return false
}
fallbackEventHandler.pointee = handler
return true
}
#else
let handler = unsafeBitCast(handler, to: UnsafeRawPointer.self)
return _fallbackEventHandler.compareExchange(expected: nil, desired: handler, ordering: .sequentiallyConsistent).exchanged
#endif
}
#endif