From 9f0c83b8e7ffad569691c99cc895f891701fa666 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Mon, 30 Jun 2025 14:02:12 -0500 Subject: [PATCH] Ensure that when .serialized is applied to a parameterized @Test func, its test cases are serialized Fixes rdar://154529146 --- .../Testing/Traits/ParallelizationTrait.swift | 8 +++++++ .../TestSupport/TestingAdditions.swift | 21 +++++++++++++++++-- .../Traits/ParallelizationTraitTests.swift | 13 +++++++++--- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Sources/Testing/Traits/ParallelizationTrait.swift b/Sources/Testing/Traits/ParallelizationTrait.swift index 9eb1bd2a5..c91e01761 100644 --- a/Sources/Testing/Traits/ParallelizationTrait.swift +++ b/Sources/Testing/Traits/ParallelizationTrait.swift @@ -31,6 +31,14 @@ public struct ParallelizationTrait: TestTrait, SuiteTrait {} // MARK: - TestScoping extension ParallelizationTrait: TestScoping { + public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? { + // When applied to a test function, this trait should provide scope to the + // test function itself, not its individual test cases, since that allows + // Runner to correctly interpret the configuration setting to disable + // parallelization. + test.isSuite || testCase == nil ? self : nil + } + 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") diff --git a/Tests/TestingTests/TestSupport/TestingAdditions.swift b/Tests/TestingTests/TestSupport/TestingAdditions.swift index 4648f96af..5c596785e 100644 --- a/Tests/TestingTests/TestSupport/TestingAdditions.swift +++ b/Tests/TestingTests/TestSupport/TestingAdditions.swift @@ -103,6 +103,25 @@ extension Runner { /// - fileID: The `#fileID` string whose module should be used to locate /// the test function to run. /// - configuration: The configuration to use for running. + init( + selecting testName: String, + inModuleOf fileID: String = #fileID, + configuration: Configuration = .init() + ) async { + let plan = await Runner.Plan(selecting: testName, inModuleOf: fileID, configuration: configuration) + self.init(plan: plan, configuration: configuration) + } +} + +extension Runner.Plan { + /// Initialize an instance of this type that selects the free test function + /// named `testName` in the module specified in `fileID`. + /// + /// - Parameters: + /// - testName: The name of the test function this instance should run. + /// - fileID: The `#fileID` string whose module should be used to locate + /// the test function to run. + /// - configuration: The configuration to use for running. init( selecting testName: String, inModuleOf fileID: String = #fileID, @@ -116,9 +135,7 @@ extension Runner { await self.init(configuration: configuration) } -} -extension Runner.Plan { /// Initialize an instance of this type with the specified suite type. /// /// - Parameters: diff --git a/Tests/TestingTests/Traits/ParallelizationTraitTests.swift b/Tests/TestingTests/Traits/ParallelizationTraitTests.swift index 776e5c320..6c4963dc5 100644 --- a/Tests/TestingTests/Traits/ParallelizationTraitTests.swift +++ b/Tests/TestingTests/Traits/ParallelizationTraitTests.swift @@ -12,8 +12,11 @@ @Suite("Parallelization Trait Tests", .tags(.traitRelated)) struct ParallelizationTraitTests { - @Test(".serialized trait serializes parameterized test") - func serializesParameterizedTestFunction() async { + @Test(".serialized trait serializes parameterized test", arguments: await [ + Runner.Plan(selecting: OuterSuite.self), + Runner.Plan(selecting: "globalParameterized(i:)"), + ]) + func serializesParameterizedTestFunction(plan: Runner.Plan) async { var configuration = Configuration() configuration.isParallelizationEnabled = true @@ -33,7 +36,6 @@ struct ParallelizationTraitTests { } } - let plan = await Runner.Plan(selecting: OuterSuite.self, configuration: configuration) let runner = Runner(plan: plan, configuration: configuration) await runner.run() @@ -59,3 +61,8 @@ private struct OuterSuite { } } } + +@Test(.hidden, .serialized, arguments: 0 ..< 10_000) +private func globalParameterized(i: Int) { + Issue.record("PARAMETERIZED\(i)") +}