From ad88dce5ef02b31ee1b3b125add0b76982e32a91 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 5 Dec 2023 15:25:09 -0500 Subject: [PATCH 1/2] Replace `setTestFilter` with `makeTestFilter`. `setTestFilter` is too rigid because it replaces the existing test filter and that makes composition harder than it needs to be. Let's replace it with a function that returns a test filter which can then be composed: ```swift let idFilter = makeTestFilter(matching: myTestIDs) func canHaveAPony(_ test: Test) -> Bool { ... } configuration.testFilter = { test in idFilter(test) && canHaveAPony(test) } ``` Resolves rdar://119205417. --- Sources/Testing/Running/Configuration.swift | 69 ++++++++++++------- Sources/Testing/Running/XCTestScaffold.swift | 2 +- Tests/TestingTests/PlanTests.swift | 8 +-- .../Runner.Plan.SnapshotTests.swift | 2 +- Tests/TestingTests/RunnerTests.swift | 8 +-- .../TestSupport/TestingAdditions.swift | 6 +- 6 files changed, 58 insertions(+), 37 deletions(-) diff --git a/Sources/Testing/Running/Configuration.swift b/Sources/Testing/Running/Configuration.swift index 9d89258bd..730b7287d 100644 --- a/Sources/Testing/Running/Configuration.swift +++ b/Sources/Testing/Running/Configuration.swift @@ -118,7 +118,7 @@ public struct Configuration: Sendable { /// - Returns: A Boolean value representing if the test satisfied the filter. public typealias TestFilter = @Sendable (_ test: Test) -> Bool - /// Storage for ``testFilter``. + /// Storage for ``testFilter-swift.property``. private var _testFilter: TestFilter = { !$0.isHidden } /// The test filter to which tests should be filtered when run. @@ -130,36 +130,24 @@ public struct Configuration: Sendable { // By default, the test filter should always filter out hidden tests. This // is the appropriate behavior for external clients of this SPI. If the // testing library needs to enable hidden tests in its own test targets, - // it should use setTestFilter(toMatch:includeHiddenTests:) instead. + // it can instead use `uncheckedTestFilter`. _testFilter = { test in !test.isHidden && newValue(test) } } } - /// Filter tests to run to those specified via a set of test IDs. - /// - /// - Parameters: - /// - selection: A set of test IDs to be filtered. - /// - /// By default, all tests are run and no filter is set. - public mutating func setTestFilter(toMatch selection: Set) { - setTestFilter(toMatch: Test.ID.Selection(testIDs: selection), includeHiddenTests: false) - } - - /// Filter tests to run to those specified by a selection of test IDs. - /// - /// - Parameters: - /// - selection: A selection of test IDs to be filtered. - /// - includeHiddenTests: If false, a test annotated with the `.hidden` - /// trait will not be included, even if its ID is present in `selection`. + /// The test filter to which tests should be filtered when run. /// - /// By default, all tests are run and no filter is set. - mutating func setTestFilter(toMatch selection: Test.ID.Selection, includeHiddenTests: Bool) { - if includeHiddenTests { - _testFilter = selection.contains - } else { - testFilter = selection.contains + /// Unlike ``testFilter-swift.property``, this property does not impose any + /// checks for hidden tests. It is used by the testing library to run hidden + /// tests; other callers should always use ``testFilter-swift.property``. + var uncheckedTestFilter: TestFilter { + get { + _testFilter + } + set { + _testFilter = newValue } } @@ -180,3 +168,36 @@ public struct Configuration: Sendable { @_spi(ExperimentalParameterizedTesting) public var testCaseFilter: TestCaseFilter = { _, _ in true } } + +// MARK: - Test filter factory functions + +/// Filter tests to run to those specified via a set of test IDs. +/// +/// - Parameters: +/// - selection: A set of test IDs to be filtered. +/// +/// By default, all tests are run and no filter is set. +@_spi(ExperimentalTestRunning) +public func makeTestFilter(matching selection: some Collection) -> Configuration.TestFilter { + let selection = Test.ID.Selection(testIDs: selection) + return makeTestFilter(matching: selection, includeHiddenTests: false) +} + +/// Filter tests to run to those specified by a selection of test IDs. +/// +/// - Parameters: +/// - selection: A selection of test IDs to be filtered. +/// - includeHiddenTests: If false, a test annotated with the `.hidden` +/// trait will not be included, even if its ID is present in `selection`. +/// +/// By default, all tests are run and no filter is set. +func makeTestFilter(matching selection: Test.ID.Selection, includeHiddenTests: Bool) -> Configuration.TestFilter { + if includeHiddenTests { + return selection.contains + } else { + return { test in + !test.isHidden && selection.contains(test) + } + } +} + diff --git a/Sources/Testing/Running/XCTestScaffold.swift b/Sources/Testing/Running/XCTestScaffold.swift index 7c1a07ec8..72f884b73 100644 --- a/Sources/Testing/Running/XCTestScaffold.swift +++ b/Sources/Testing/Running/XCTestScaffold.swift @@ -211,7 +211,7 @@ public enum XCTestScaffold: Sendable { var configuration = Configuration() configuration.isParallelizationEnabled = false if let testIDs { - configuration.setTestFilter(toMatch: Set(testIDs)) + configuration.testFilter = makeTestFilter(matching: testIDs) } if let tags { // Check if the test's tags intersect the set of selected tags. If there diff --git a/Tests/TestingTests/PlanTests.swift b/Tests/TestingTests/PlanTests.swift index ebdd693dd..27005d5ae 100644 --- a/Tests/TestingTests/PlanTests.swift +++ b/Tests/TestingTests/PlanTests.swift @@ -28,7 +28,7 @@ struct PlanTests { let selection = Test.ID.Selection(testIDs: [innerTestType.id]) var configuration = Configuration() - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let plan = await Runner.Plan(tests: tests, configuration: configuration) #expect(plan.steps.contains(where: { $0.test == outerTestType })) @@ -53,7 +53,7 @@ struct PlanTests { var configuration = Configuration() let selection = Test.ID.Selection(testIDs: [innerTestType.id, outerTestType.id]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let plan = await Runner.Plan(tests: tests, configuration: configuration) let planTests = plan.steps.map(\.test) @@ -73,7 +73,7 @@ struct PlanTests { var configuration = Configuration() let selection = Test.ID.Selection(testIDs: [outerTestType.id, deeplyNestedTest.id]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let plan = await Runner.Plan(tests: tests, configuration: configuration) @@ -92,7 +92,7 @@ struct PlanTests { var configuration = Configuration() let selection = Test.ID.Selection(testIDs: [testSuiteA.id]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let plan = await Runner.Plan(tests: tests, configuration: configuration) let testFuncXWithTraits = try #require(plan.steps.map(\.test).first { $0.name == "x()" }) diff --git a/Tests/TestingTests/Runner.Plan.SnapshotTests.swift b/Tests/TestingTests/Runner.Plan.SnapshotTests.swift index 365c6b3b4..7456f569e 100644 --- a/Tests/TestingTests/Runner.Plan.SnapshotTests.swift +++ b/Tests/TestingTests/Runner.Plan.SnapshotTests.swift @@ -22,7 +22,7 @@ struct Runner_Plan_SnapshotTests { let suite = try #require(await test(for: Runner_Plan_SnapshotFixtures.self)) var configuration = Configuration() - configuration.setTestFilter(toMatch: .init(testIDs: [suite.id]), includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: .init(testIDs: [suite.id]), includeHiddenTests: true) let plan = await Runner.Plan(configuration: configuration) let snapshot = Runner.Plan.Snapshot(snapshotting: plan) diff --git a/Tests/TestingTests/RunnerTests.swift b/Tests/TestingTests/RunnerTests.swift index e061f4eb8..5d17751fe 100644 --- a/Tests/TestingTests/RunnerTests.swift +++ b/Tests/TestingTests/RunnerTests.swift @@ -251,7 +251,7 @@ final class RunnerTests: XCTestCase { var configuration = Configuration() let selection = Test.ID.Selection(testIDs: [testSuite.id]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let runner = await Runner(testing: [ testSuite, @@ -302,7 +302,7 @@ final class RunnerTests: XCTestCase { var configuration = Configuration() let selection = Test.ID.Selection(testIDs: selectedTestIDs) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let runner = await Runner(configuration: configuration) let plan = runner.plan @@ -326,10 +326,10 @@ final class RunnerTests: XCTestCase { ] var configuration1 = Configuration() - configuration1.setTestFilter(toMatch: .init(testIDs: selectedTestIDs), includeHiddenTests: false) + configuration1.testFilter = makeTestFilter(matching: .init(testIDs: selectedTestIDs), includeHiddenTests: false) var configuration2 = Configuration() - configuration2.setTestFilter(toMatch: selectedTestIDs) + configuration2.testFilter = makeTestFilter(matching: selectedTestIDs) for configuration in [configuration1, configuration2] { let runner = await Runner(configuration: configuration) diff --git a/Tests/TestingTests/TestSupport/TestingAdditions.swift b/Tests/TestingTests/TestSupport/TestingAdditions.swift index 89e06176d..5a069bf12 100644 --- a/Tests/TestingTests/TestSupport/TestingAdditions.swift +++ b/Tests/TestingTests/TestSupport/TestingAdditions.swift @@ -68,7 +68,7 @@ func runTest(for containingType: Any.Type, configuration: Configuration = .init( func runTestFunction(named name: String, in containingType: Any.Type, configuration: Configuration = .init()) async { var configuration = configuration let selection = Test.ID.Selection(testIDs: [Test.ID(type: containingType).child(named: name)]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) let runner = await Runner(configuration: configuration) await runner.run() @@ -92,7 +92,7 @@ extension Runner { var configuration = configuration let selection = Test.ID.Selection(testIDs: [Test.ID(moduleName: moduleName, nameComponents: [testName], sourceLocation: nil)]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) await self.init(configuration: configuration) } @@ -107,7 +107,7 @@ extension Runner.Plan { init(selecting containingType: Any.Type, configuration: Configuration = .init()) async { var configuration = configuration let selection = Test.ID.Selection(testIDs: [Test.ID(type: containingType)]) - configuration.setTestFilter(toMatch: selection, includeHiddenTests: true) + configuration.uncheckedTestFilter = makeTestFilter(matching: selection, includeHiddenTests: true) await self.init(configuration: configuration) } From 2b0579800b3e187018ee821d546eac8fed807ed1 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 5 Dec 2023 15:52:01 -0500 Subject: [PATCH 2/2] Update markup for functions --- Sources/Testing/Running/Configuration.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/Testing/Running/Configuration.swift b/Sources/Testing/Running/Configuration.swift index 730b7287d..7a25cc4c9 100644 --- a/Sources/Testing/Running/Configuration.swift +++ b/Sources/Testing/Running/Configuration.swift @@ -171,7 +171,8 @@ public struct Configuration: Sendable { // MARK: - Test filter factory functions -/// Filter tests to run to those specified via a set of test IDs. +/// Make a test filter that filters tests to those specified by a set of test +/// IDs. /// /// - Parameters: /// - selection: A set of test IDs to be filtered. @@ -183,7 +184,8 @@ public func makeTestFilter(matching selection: some Collection) -> Conf return makeTestFilter(matching: selection, includeHiddenTests: false) } -/// Filter tests to run to those specified by a selection of test IDs. +/// Make a test filter that filters tests to those specified by a set of test +/// IDs, optionally including or excluding hidden tests. /// /// - Parameters: /// - selection: A selection of test IDs to be filtered.