blob: bc600db9bfe41e557211f00bd3669364a64b74cd [file] [log] [blame] [edit]
// Copyright (C) 2026 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
import Foundation
#if compiler(>=6.2)
/// A set of facilities to mimic basic Swift Testing support, until Swift Testing can be directly used.
public enum Testing {
/// The cause of a test failure.
public struct Error: Swift.Error {
/// A descriptive message of why the error occurred.
public let message: Swift.String
/// The name of the function where the error occurred.
public let function: StaticString
/// The line number in the file where the error occurred.
public let line: Int
init(_ message: Swift.String, function: StaticString = #function, line: Int = #line) {
self.message = message
self.function = function
self.line = line
}
}
/// Unwrap an optional value or, if it is `nil`, fail and throw an error.
///
/// - Parameters:
/// - optionalValue: The optional value to be unwrapped.
/// - comment: A comment describing the expectation.
/// - function: The function of the source location where this is called.
/// - line: The line number of the source location where this is called.
/// - Returns: The unwrapped value of `optionalValue`.
/// - Throws: An Error if the value is `nil`
public static func require<T>(
_ optionalValue: T?,
_ comment: @autoclosure () -> Swift.String? = nil,
function: StaticString = #function,
line: Int = #line,
) throws(Error) -> T {
guard let optionalValue else {
let resolvedComment = comment()
throw Error("Found nil value of type \(T.self) : \(resolvedComment ?? "")", function: function, line: line)
}
return optionalValue
}
/// Check that an expectation has passed after a condition has been evaluated.
///
/// If the equality condition evaluates to false, an Error is thrown.
///
/// - Parameters:
/// - actualValue: The actual value to compare.
/// - expectedValue: The expected value to compare.
/// - comment: A comment describing the expectation.
/// - function: The function of the source location where this is called.
/// - line: The line number of the source location where this is called.
/// - Throws: An error if the values are not equal.
public static func expect<T>(
_ actualValue: T?,
toEqual expectedValue: T?,
_ comment: @autoclosure () -> Swift.String? = nil,
function: StaticString = #function,
line: Int = #line,
) throws(Error) where T: Equatable {
guard actualValue == expectedValue else {
let resolvedComment = comment()
throw Error(
"Expected \(Swift.String(describing: actualValue)) to equal \(Swift.String(describing: expectedValue)) : \(resolvedComment ?? "")",
function: function,
line: line
)
}
}
/// Repeatedly invokes a condition until it evaluates to true or until a timeout has been reached.
///
/// - Parameters:
/// - timeout: The timeout to wait until before exiting this function and throwing an Error.
/// - interval: The duration to wait between consecutive evaluations of the condition
/// - function: The function of the source location where this is called.
/// - line: The line number of the source location where this is called.
/// - condition: The predicate condition to evaluate.
/// - Throws: An Error if the condition throws an Error, or if the timeout duration is reached.
public nonisolated(nonsending) static func waitUntil(
timeout: Duration = .seconds(10),
interval: Duration = .milliseconds(100),
function: StaticString = #function,
line: Int = #line,
condition: () async throws -> Bool,
) async throws {
let deadline = ContinuousClock.now + timeout
while ContinuousClock.now < deadline {
if try await condition() {
return
}
try await Task.sleep(for: interval)
}
throw Error("Timed out.", function: function, line: line)
}
}
#endif // compiler(>=6.2)