-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Detect when Swift Testing returns EXIT_NO_TESTS_FOUND
.
#7777
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -869,13 +869,40 @@ final class TestRunner { | |
|
||
/// Executes and returns execution status. Prints test output on standard streams if requested | ||
/// - Returns: Boolean indicating if test execution returned code 0, and the output stream result | ||
public func test(outputHandler: @escaping (String) -> Void) -> Bool { | ||
var success = true | ||
func test(outputHandler: @escaping (String) -> Void) -> Bool { | ||
(test(outputHandler: outputHandler) as Result) != .failure | ||
} | ||
|
||
/// The result of running the test(s). | ||
enum Result: Equatable { | ||
/// The test(s) ran successfully. | ||
case success | ||
|
||
/// The test(s) failed. | ||
case failure | ||
|
||
/// There were no matching tests to run. | ||
/// | ||
/// XCTest does not report this result. It is used by Swift Testing only. | ||
case noMatchingTests | ||
} | ||
|
||
/// Executes and returns execution status. Prints test output on standard streams if requested | ||
/// - Returns: Result of spawning and running the test process, and the output stream result | ||
@_disfavoredOverload | ||
func test(outputHandler: @escaping (String) -> Void) -> Result { | ||
var results = [Result]() | ||
for path in self.bundlePaths { | ||
let testSuccess = self.test(at: path, outputHandler: outputHandler) | ||
success = success && testSuccess | ||
results.append(testSuccess) | ||
} | ||
if results.contains(.failure) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If anything failed, the run failed. If anything passed and had matching tests, the run passed. If everything reported no matching tests, then there were no matching tests. |
||
return .failure | ||
} else if results.isEmpty || results.contains(.success) { | ||
return .success | ||
} else { | ||
return .noMatchingTests | ||
} | ||
return success | ||
} | ||
|
||
/// Constructs arguments to execute XCTest. | ||
|
@@ -899,7 +926,7 @@ final class TestRunner { | |
return args | ||
} | ||
|
||
private func test(at path: AbsolutePath, outputHandler: @escaping (String) -> Void) -> Bool { | ||
private func test(at path: AbsolutePath, outputHandler: @escaping (String) -> Void) -> Result { | ||
let testObservabilityScope = self.observabilityScope.makeChildScope(description: "running test at \(path)") | ||
|
||
do { | ||
|
@@ -914,25 +941,27 @@ final class TestRunner { | |
) | ||
let process = AsyncProcess(arguments: try args(forTestAt: path), environment: self.testEnv, outputRedirection: outputRedirection) | ||
guard let terminationKey = self.cancellator.register(process) else { | ||
return false // terminating | ||
return .failure // terminating | ||
} | ||
defer { self.cancellator.deregister(terminationKey) } | ||
try process.launch() | ||
let result = try process.waitUntilExit() | ||
switch result.exitStatus { | ||
case .terminated(code: 0): | ||
return true | ||
return .success | ||
case .terminated(code: EXIT_NO_TESTS_FOUND) where library == .swiftTesting: | ||
return .noMatchingTests | ||
#if !os(Windows) | ||
case .signalled(let signal) where ![SIGINT, SIGKILL, SIGTERM].contains(signal): | ||
testObservabilityScope.emit(error: "Exited with unexpected signal code \(signal)") | ||
return false | ||
return .failure | ||
#endif | ||
default: | ||
return false | ||
return .failure | ||
} | ||
} catch { | ||
testObservabilityScope.emit(error) | ||
return false | ||
return .failure | ||
} | ||
} | ||
} | ||
|
@@ -1399,6 +1428,24 @@ private extension Basics.Diagnostic { | |
} | ||
} | ||
|
||
/// The exit code returned to Swift Package Manager by Swift Testing when no | ||
/// tests matched the inputs specified by the developer (or, for the case of | ||
/// `swift test list`, when no tests were found.) | ||
/// | ||
/// Because Swift Package Manager does not directly link to the testing library, | ||
/// it duplicates the definition of this constant in its own source. Any changes | ||
/// to this constant in either package must be mirrored in the other. | ||
private var EXIT_NO_TESTS_FOUND: CInt { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copied verbatim from Swift Testing's PR. |
||
#if os(macOS) || os(Linux) | ||
EX_UNAVAILABLE | ||
#elseif os(Windows) | ||
ERROR_NOT_FOUND | ||
#else | ||
#warning("Platform-specific implementation missing: value for EXIT_NO_TESTS_FOUND unavailable") | ||
return 2 // We're assuming that EXIT_SUCCESS = 0 and EXIT_FAILURE = 1. | ||
#endif | ||
} | ||
|
||
/// Builds the "test" target if enabled in options. | ||
/// | ||
/// - Returns: The paths to the build test products. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporarily disfavored until #7766 is in the build in order to preserve the semantics of surrounding code, at which point the overload that returns a
Bool
can be removed.