Skip to content

Implement an equality operator for ExitCondition. #613

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
61 changes: 42 additions & 19 deletions Sources/Testing/ExitTests/ExitCondition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ private import _TestingInternals
/// ``expect(exitsWith:_:sourceLocation:performing:)`` or
/// ``require(exitsWith:_:sourceLocation:performing:)`` to configure which exit
/// statuses should be considered successful.
///
/// Two instances of this type can be compared; if either instance is equal to
/// ``failure``, it will compare equal to any instance except ``success``. To
/// check if two instances are exactly equal, use the `===` operator:
///
/// ```swift
/// let lhs: ExitCondition = .failure
/// let rhs: ExitCondition = .signal(SIGINT)
/// print(lhs == rhs) // prints "true"
/// print(lhs === rhs) // prints "false"
/// ```
@_spi(Experimental)
#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
Expand Down Expand Up @@ -68,37 +79,49 @@ public enum ExitCondition: Sendable {
case signal(_ signal: CInt)
}

// MARK: -
// MARK: - Equatable, Hashable

#if SWT_NO_EXIT_TESTS
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitCondition {
/// Check whether this instance matches another.
///
/// - Parameters:
/// - other: The other instance to compare against.
///
/// - Returns: Whether or not this instance is equal to, or at least covers,
/// the other instance.
func matches(_ other: ExitCondition) -> Bool {
return switch (self, other) {
case (.failure, .failure):
true
extension ExitCondition: Equatable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return switch (lhs, rhs) {
case let (.failure, .exitCode(exitCode)), let (.exitCode(exitCode), .failure):
exitCode != EXIT_SUCCESS
#if !os(Windows)
case (.failure, .signal), (.signal, .failure):
// All terminating signals are considered failures.
true
#endif
default:
lhs === rhs
}
}

public static func ===(lhs: Self, rhs: Self) -> Bool {
return switch (lhs, rhs) {
case (.failure, .failure):
true
case let (.exitCode(lhs), .exitCode(rhs)):
lhs == rhs
#if !os(Windows)
case let (.signal(lhs), .signal(rhs)):
lhs == rhs
case (.signal, .failure), (.failure, .signal):
// All terminating signals are considered failures.
true
case (.signal, .exitCode), (.exitCode, .signal):
// Signals do not match exit codes.
false
#endif
default:
false
}
}

public static func !==(lhs: Self, rhs: Self) -> Bool {
!(lhs === rhs)
}
}

@available(*, unavailable, message: "ExitCondition does not conform to Hashable.")
extension ExitCondition: Hashable {
public func hash(into hasher: inout Hasher) {
fatalError("Unsupported")
}
}
4 changes: 2 additions & 2 deletions Sources/Testing/ExitTests/ExitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public struct ExitTest: Sendable {
// Run some glue code that terminates the process with an exit condition
// that does not match the expected one. If the exit test's body doesn't
// terminate, we'll manually call exit() and cause the test to fail.
let expectingFailure = expectedExitCondition.matches(.failure)
let expectingFailure = expectedExitCondition == .failure
exit(expectingFailure ? EXIT_SUCCESS : EXIT_FAILURE)
}
}
Expand Down Expand Up @@ -150,7 +150,7 @@ func callExitTest(
}

return __checkValue(
expectedExitCondition.matches(actualExitCondition),
expectedExitCondition == actualExitCondition,
expression: expression,
expressionWithCapturedRuntimeValues: expression.capturingRuntimeValues(actualExitCondition),
mismatchedExitConditionDescription: String(describingForTest: expectedExitCondition),
Expand Down
18 changes: 18 additions & 0 deletions Tests/TestingTests/ExitTestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,24 @@ private import _TestingInternals
}.run(configuration: configuration)
}
}

@Test("Exit condition exact matching (===)")
func exitConditionMatching() {
#expect(ExitCondition.success === .success)
#expect(ExitCondition.success === .exitCode(EXIT_SUCCESS))
#expect(ExitCondition.success !== .exitCode(EXIT_FAILURE))

#expect(ExitCondition.failure === .failure)

#expect(ExitCondition.exitCode(EXIT_FAILURE &+ 1) !== .exitCode(EXIT_FAILURE))

#if !os(Windows)
#expect(ExitCondition.success !== .exitCode(EXIT_FAILURE))
#expect(ExitCondition.success !== .signal(SIGINT))
#expect(ExitCondition.signal(SIGINT) === .signal(SIGINT))
#expect(ExitCondition.signal(SIGTERM) !== .signal(SIGINT))
#endif
}
}

// MARK: - Fixtures
Expand Down