diff --git a/Sources/Testing/ExitTests/ExitCondition.swift b/Sources/Testing/ExitTests/ExitCondition.swift index e3074740d..d7580b4e6 100644 --- a/Sources/Testing/ExitTests/ExitCondition.swift +++ b/Sources/Testing/ExitTests/ExitCondition.swift @@ -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.") @@ -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") + } } diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index 798e95d0a..3c579c18d 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -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) } } @@ -150,7 +150,7 @@ func callExitTest( } return __checkValue( - expectedExitCondition.matches(actualExitCondition), + expectedExitCondition == actualExitCondition, expression: expression, expressionWithCapturedRuntimeValues: expression.capturingRuntimeValues(actualExitCondition), mismatchedExitConditionDescription: String(describingForTest: expectedExitCondition), diff --git a/Tests/TestingTests/ExitTestTests.swift b/Tests/TestingTests/ExitTestTests.swift index 55c22a9bb..de1756e25 100644 --- a/Tests/TestingTests/ExitTestTests.swift +++ b/Tests/TestingTests/ExitTestTests.swift @@ -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