Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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/Testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ add_library(Testing
Traits/Tags/Tag+Macro.swift
Traits/Tags/Tag+Predefined.swift
Traits/TimeLimitTrait.swift
Traits/TaskLocalTrait.swift
Traits/Trait.swift)
target_link_libraries(Testing PRIVATE
_TestDiscovery
Expand Down
57 changes: 57 additions & 0 deletions Sources/Testing/Traits/TaskLocalTrait.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// 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
//

extension Trait {
Comment thread
harlanhaskins marked this conversation as resolved.
/// Constructs a trait that binds a task local value for the duration of a test
/// or suite.
///
/// - Parameters:
/// - taskLocal: The task local to bind the value to.
/// - value: The value to set.
///
/// ```swift
Comment thread
mbrandonw marked this conversation as resolved.
Outdated
/// @Suite(.taskLocal($myValue, 42))
/// struct MyTests {
/// // ...
/// }
/// ```
///
/// - Note: You must define the task local outside the test target where the trait is used.
public static func taskLocal<Value: Sendable>(
_ taskLocal: TaskLocal<Value>,
_ value: @autoclosure @escaping @Sendable () throws -> Value
) -> Self
where Self == TaskLocalTrait<Value> {
Comment thread
grynspan marked this conversation as resolved.
TaskLocalTrait(taskLocal: taskLocal, value: value)
}
}

/// A type that that binds a task local value for the duration of a test or suite.
///
/// To add this trait to a test, use ``Trait/taskLocal(_:_:)``.
public struct TaskLocalTrait<Value: Sendable>: SuiteTrait, TestTrait, TestScoping {
/// This trait's task local.
fileprivate var taskLocal: TaskLocal<Value>

/// This trait's value.
fileprivate var value: @Sendable () throws -> Value

public func provideScope(
for test: Test,
testCase: Test.Case?,
performing function: @concurrent () async throws -> Void

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have @concurrent on other provideScope() implementations in the library. The other implementations do have @Sendable though; can we get away with only @Sendable ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is @concurrent only because it was taken from a library we maintain with this tool, and that library is NonisolatedNonSendingByDefault and Swift Testing is not (I believe). Will remove and bring back @Sendable as that's the TestScoping requirement.

) async throws {
try await taskLocal.withValue(value(), operation: function)
}
}

#if DEBUG
@TaskLocal var dummyLocal = false
Comment thread
stmontgomery marked this conversation as resolved.
Outdated
#endif
22 changes: 22 additions & 0 deletions Tests/TestingTests/Traits/TaskLocalTraitTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// 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
//

@testable @_spi(Experimental) @_spi(ForToolsIntegrationOnly) import Testing

@Suite("Task Local Tests", .tags(.traitRelated))
struct TaskLocalTests {
@Test(
".taskLocal trait",
.taskLocal($dummyLocal, true)
)
func taskLocalBinding() throws {
#expect(dummyLocal == true)
}
}
Loading