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
19 changes: 15 additions & 4 deletions Sources/Testing/ABI/Encoded/ABI.EncodedEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,14 @@ extension ABI {
kind = .testStarted
case .testCaseStarted:
if eventContext.test?.isParameterized == false {
return nil
if let iteration = eventContext.iteration, iteration > 1 {
kind = .testStarted
} else {
return nil
}
} else {
kind = .testCaseStarted
Comment thread
harlanhaskins marked this conversation as resolved.
}
kind = .testCaseStarted
case let .issueRecorded(recordedIssue):
kind = .issueRecorded
issue = EncodedIssue(encoding: recordedIssue, in: eventContext)
Expand All @@ -129,9 +134,14 @@ extension ABI {
self.attachment = EncodedAttachment(encoding: attachment)
case .testCaseEnded:
if eventContext.test?.isParameterized == false {
return nil
if let iteration = eventContext.iteration, iteration > 1 {
kind = .testEnded
} else {
return nil
}
} else {
kind = .testCaseEnded
}
kind = .testCaseEnded
case .testCaseCancelled:
kind = .testCaseCancelled
case .testEnded:
Expand Down Expand Up @@ -164,6 +174,7 @@ extension ABI {
let .testCancelled(skipInfo):
_comments = Array(skipInfo.comment).map(\.rawValue)
_sourceLocation = skipInfo.sourceLocation.map { EncodedSourceLocation(encoding: $0) }
_iteration = eventContext.iteration
default:
break
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/Testing/ABI/EntryPoints/EntryPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ public struct __CommandLineArguments_v0: Sendable {
/// The value of the `--repeat-until` argument.
public var repeatUntil: String?

var usePerTestCaseRepetition: Bool = false
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.

nit: Can we include documentation aligning with the other Bool properties in this type?


/// The value of the `--attachments-path` argument.
public var attachmentsPath: String?
}
Expand Down Expand Up @@ -537,6 +539,9 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
if let repeatUntil = args.argumentValue(forLabel: "--repeat-until") {
result.repeatUntil = repeatUntil
}
if args.contains("--experimental-per-test-case-repetition") {
result.usePerTestCaseRepetition = true
}

return result
}
Expand Down Expand Up @@ -665,6 +670,9 @@ public func configurationForEntryPoint(from args: __CommandLineArguments_v0) thr
}
configuration.repetitionPolicy = repetitionPolicy

// Opt in to per-test-case repetition
configuration.shouldUseLegacyPlanLevelRepetition = !args.usePerTestCaseRepetition

#if !SWT_NO_EXIT_TESTS
// Enable exit test handling via __swiftPMEntryPoint().
configuration.exitTestHandler = ExitTest.handlerForEntryPoint()
Expand Down
10 changes: 6 additions & 4 deletions Sources/Testing/Events/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@ public struct Event: Sendable {
static func post(
_ kind: Kind,
for testAndTestCase: (Test?, Test.Case?) = currentTestAndTestCase(),
iteration: Int? = nil,
instant: Test.Clock.Instant = .now,
configuration: Configuration? = nil
) {
Expand All @@ -262,7 +261,12 @@ public struct Event: Sendable {
}
}
let event = Event(kind, testID: test?.id, testCaseID: testCase?.id, instant: instant)
let context = Event.Context(test: test, testCase: testCase, iteration: iteration, configuration: nil)
let context = Event.Context(
test: test,
testCase: testCase,
iteration: Test.currentIteration,
configuration: nil
)
event._post(in: context, configuration: configuration)
}
}
Expand Down Expand Up @@ -327,8 +331,6 @@ extension Event {
iteration: Int?,
configuration: Configuration?
) {
// Ensure that if `iteration` is specified, the test is also specified.
precondition(iteration == nil || (iteration != nil && test != nil))
self.test = test
self.testCase = testCase
self.iteration = iteration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,10 @@ extension Event.HumanReadableOutputRecorder {
String(describing: TimeValue(rawValue: end.suspending.rawValue - start.suspending.rawValue))
}

func testStartedMessage(for test: Test) -> String {
"\(_capitalizedTitle(for: test)) \(testName) started"
}

// Finally, produce any messages for the event.
switch event.kind {
case .testDiscovered:
Expand Down Expand Up @@ -425,7 +429,7 @@ extension Event.HumanReadableOutputRecorder {
return [
Message(
symbol: .default,
stringValue: "\(_capitalizedTitle(for: test)) \(testName) started."
stringValue: "\(testStartedMessage(for: test))."
)
]

Expand Down Expand Up @@ -565,15 +569,29 @@ extension Event.HumanReadableOutputRecorder {
return result

case .testCaseStarted:
guard let testCase, testCase.isParameterized, let arguments = testCase.arguments else {
guard let test, let testCase else { break }
let iteration = eventContext.iteration ?? 1

let shouldShowMessage = testCase.isParameterized || iteration > 1

guard shouldShowMessage else {
break
}
Comment thread
harlanhaskins marked this conversation as resolved.
Outdated

var message = if testCase.isParameterized, let arguments = testCase.arguments {
"Test case passing \(arguments.count.counting("argument")) \(testCase.labeledArguments(includingQualifiedTypeNames: verbosity > 0)) to \(testName) started"
} else {
testStartedMessage(for: test)
}

if iteration > 1 {
message += " (repetition \(iteration))"
}

message += "."

return [
Message(
symbol: .default,
stringValue: "Test case passing \(arguments.count.counting("argument")) \(testCase.labeledArguments(includingQualifiedTypeNames: verbosity > 0)) to \(testName) started."
)
Message(symbol: .default, stringValue: message)
]

case .testCaseEnded:
Expand Down
4 changes: 4 additions & 0 deletions Sources/Testing/Running/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ public struct Configuration: Sendable {
}
}

/// Whether to perform test repetition at the plan level or on a per-test-
/// case basis.
public var shouldUseLegacyPlanLevelRepetition: Bool = true
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.

Let's include @_spi(Experimental) too, and that would be something we remove if the proposal becomes accepted. (This will mean initially, it will have that plus inherited @_spi(ForToolsIntegrationOnly), and the latter will remain even post-acceptance.)


/// Whether or not, and how, to iterate the test plan repeatedly.
///
/// By default, the value of this property allows for a single iteration.
Expand Down
24 changes: 24 additions & 0 deletions Sources/Testing/Running/Runner.RuntimeState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ extension Runner {
/// The test case that is running on the current task, if any.
var testCase: Test.Case?

/// The current iteration of the test repetition policy.
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.

Suggested change
/// The current iteration of the test repetition policy.
/// The current iteration of the test repetition policy, if any.

var iteration: Int?

/// The runtime state related to the runner running on the current task,
/// if any.
@TaskLocal
Expand Down Expand Up @@ -233,6 +236,27 @@ extension Test {
try await test.withCancellationHandling(body)
}
}

static var currentIteration: Int? {
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.

Can you add documentation for this property?

Runner.RuntimeState.current?.iteration
}

/// Call a function while the value of ``Test/currentIteration`` is set.
///
/// - Parameters:
/// - iteration: The new value to set for ``Test/currentIteration``.
/// - body: A function to call.
///
/// - Returns: Whatever is returned by `body`.
///
/// - Throws: Whatever is thrown by `body`.
static func withCurrentIteration<R>(_ iteration: Int?, perform body: () async throws -> R) async rethrows -> R {
var runtimeState = Runner.RuntimeState.current ?? .init()
runtimeState.iteration = iteration
return try await Runner.RuntimeState.$current.withValue(runtimeState) {
try await body()
}
}
}

extension Test.Case {
Expand Down
Loading
Loading