Improved assert helper ergonomics
This commit is contained in:
parent
3199fbce33
commit
f0b34e00c5
|
@ -49,12 +49,14 @@ class MatrixTests: XCTestCase {
|
|||
|
||||
func testSetRow() {
|
||||
matrix[row: 0] = [13.0, 14.0, 15.0, 16.0]
|
||||
XCTAssertTrue(matrix == Matrix<Double>([[13, 14, 15, 16], [5, 6, 7, 8], [9, 10, 11, 12]]))
|
||||
let expectedResult: Matrix<Double> = Matrix<Double>([[13, 14, 15, 16], [5, 6, 7, 8], [9, 10, 11, 12]])
|
||||
XCTAssertEqual(matrix, expectedResult)
|
||||
}
|
||||
|
||||
func testSetColumn() {
|
||||
matrix[column: 0] = [20, 30, 40]
|
||||
XCTAssertEqual(matrix, Matrix<Double>([[20, 2, 3, 4], [30, 6, 7, 8], [40, 10, 11, 12]]))
|
||||
let expectedResult: Matrix<Double> = Matrix<Double>([[20, 2, 3, 4], [30, 6, 7, 8], [40, 10, 11, 12]])
|
||||
XCTAssertEqual(matrix, expectedResult)
|
||||
}
|
||||
|
||||
func testMatrixPower() {
|
||||
|
@ -74,7 +76,8 @@ class MatrixTests: XCTestCase {
|
|||
|
||||
func testElementWiseMultiplication() {
|
||||
let matrix2 = Matrix<Double>([[2, 3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13]])
|
||||
XCTAssertEqual(elmul(matrix, matrix2), Matrix<Double>([[2, 6, 12, 20], [30, 42, 56, 72], [90, 110, 132, 156]]))
|
||||
let expectedResult: Matrix<Double> = Matrix<Double>([[2, 6, 12, 20], [30, 42, 56, 72], [90, 110, 132, 156]])
|
||||
XCTAssertEqual(elmul(matrix, matrix2), expectedResult)
|
||||
}
|
||||
|
||||
func testDeterminantFloat() {
|
||||
|
|
|
@ -21,6 +21,127 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
|
||||
@testable import Surge
|
||||
|
||||
private struct ValueError: Swift.Error, CustomStringConvertible {
|
||||
let message: String
|
||||
|
||||
var description: String {
|
||||
return self.message
|
||||
}
|
||||
}
|
||||
|
||||
private enum ArrayError: Swift.Error, CustomStringConvertible {
|
||||
case size(message: String)
|
||||
case content(index: Int, content: ValueError)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .size(message):
|
||||
return message
|
||||
case let .content(index, error):
|
||||
return "Failure at index [\(index)]: \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum GridError: Swift.Error, CustomStringConvertible {
|
||||
case size(message: String)
|
||||
case content(index: Int, content: ArrayError)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .size(message):
|
||||
return message
|
||||
case let .content(gridIndex, arrayError):
|
||||
switch arrayError {
|
||||
case let .size(message):
|
||||
return "Failure at index [\(gridIndex), ..]: \(message)"
|
||||
case let .content(arrayIndex, valueError):
|
||||
return "Failure at index [\(gridIndex), \(arrayIndex)]: \(valueError)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func checkValue<T>(
|
||||
_ actualValue: T,
|
||||
_ expectedValue: T,
|
||||
accuracy: T
|
||||
) -> Result<(), ValueError> where T: FloatingPoint {
|
||||
guard abs(actualValue - expectedValue) <= abs(accuracy) else {
|
||||
let (actual, expected) = (actualValue, expectedValue)
|
||||
let message = "(\(actual)) is not equal to (\(expected)) +/- (\(accuracy))"
|
||||
return .failure(ValueError(message: message))
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
private func checkArray<T, U>(
|
||||
_ actualArray: T,
|
||||
_ expectedArray: T,
|
||||
accuracy: U
|
||||
) -> Result<(), ArrayError> where T: Collection, T.Element == U, U: FloatingPoint {
|
||||
guard actualArray.count == expectedArray.count else {
|
||||
let (actual, expected) = (actualArray.count, expectedArray.count)
|
||||
let message = "Values have different size: (\(actual)) is not equal to (\(expected))"
|
||||
return .failure(.size(message: message))
|
||||
}
|
||||
|
||||
for (index, (actualValue, expectedValue)) in Swift.zip(actualArray, expectedArray).enumerated() {
|
||||
switch checkValue(actualValue, expectedValue, accuracy: accuracy) {
|
||||
case .success:
|
||||
continue
|
||||
case .failure(let error):
|
||||
return .failure(.content(index: index, content: error))
|
||||
}
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
private func checkGrid<T, U, V>(
|
||||
_ actualGrid: T,
|
||||
_ expectedGrid: T,
|
||||
accuracy: V
|
||||
) -> Result<(), GridError> where T: Collection, U: Collection, T.Element == U, U.Element == V, V: FloatingPoint {
|
||||
guard actualGrid.count == expectedGrid.count else {
|
||||
let (actual, expected) = (actualGrid.count, expectedGrid.count)
|
||||
let message = "Values have different size: (\(actual) × _) is not equal to (\(expected) × _)"
|
||||
return .failure(.size(message: message))
|
||||
}
|
||||
|
||||
for (index, (actualArray, expectedArray)) in Swift.zip(actualGrid, expectedGrid).enumerated() {
|
||||
switch checkArray(actualArray, expectedArray, accuracy: accuracy) {
|
||||
case .success:
|
||||
continue
|
||||
case .failure(let error):
|
||||
return .failure(.content(index: index, content: error))
|
||||
}
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
private enum Prefix: String {
|
||||
case assertEqual = "XCTAssertEqual"
|
||||
case assertEqualWithAccuracy = "XCTAssertEqualWithAccuracy"
|
||||
}
|
||||
|
||||
private func fail(
|
||||
prefix: Prefix,
|
||||
failureMessage: String,
|
||||
userMessage: String? = nil,
|
||||
file: StaticString,
|
||||
line: UInt
|
||||
) {
|
||||
let prefix = "\(prefix.rawValue) failed: "
|
||||
let suffix = userMessage.map { " - \($0)" } ?? ""
|
||||
let message = "\(prefix)\(failureMessage)\(suffix)"
|
||||
XCTFail(message, file: file, line: line)
|
||||
}
|
||||
|
||||
/// Allows comparing:
|
||||
///
|
||||
/// ```
|
||||
|
@ -33,41 +154,50 @@ import XCTest
|
|||
/// Useful for comparing:
|
||||
/// - `[Float]`
|
||||
/// - `[Double]`
|
||||
@discardableResult
|
||||
func XCTAssertEqual<T, U>(
|
||||
_ expression1: @autoclosure () throws -> T,
|
||||
_ expression2: @autoclosure () throws -> T,
|
||||
accuracy: U,
|
||||
accuracy: U? = nil,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) -> Bool
|
||||
where T: Collection, T.Element == U, U: FloatingPoint {
|
||||
let (actualValues, expectedValues): (T, T)
|
||||
) where T: Collection, T.Element == U, U: FloatingPoint & ExpressibleByFloatLiteral {
|
||||
XCTAssertEqual1D(
|
||||
try expression1(),
|
||||
try expression2(),
|
||||
accuracy: accuracy,
|
||||
message(),
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
func XCTAssertEqual1D<T, U>(
|
||||
_ expression1: @autoclosure () throws -> T,
|
||||
_ expression2: @autoclosure () throws -> T,
|
||||
accuracy: U? = nil,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where T: Collection, T.Element == U, U: FloatingPoint & ExpressibleByFloatLiteral {
|
||||
let prefix: Prefix = (accuracy == nil) ? .assertEqual : .assertEqualWithAccuracy
|
||||
|
||||
let (actual, expected): (T, T)
|
||||
|
||||
do {
|
||||
(actualValues, expectedValues) = (try expression1(), try expression2())
|
||||
(actual, expected) = (try expression1(), try expression2())
|
||||
} catch let error {
|
||||
XCTFail("Error: \(error)", file: file, line: line)
|
||||
return false
|
||||
let message = String(describing: error)
|
||||
return fail(prefix: prefix, failureMessage: message, file: file, line: line)
|
||||
}
|
||||
|
||||
XCTAssertEqual(actualValues.count, expectedValues.count, file: file, line: line)
|
||||
let result = checkArray(actual, expected, accuracy: accuracy ?? 0.0)
|
||||
|
||||
for (actual, expected) in Swift.zip(actualValues, expectedValues) {
|
||||
guard abs(actual - expected) > abs(accuracy) else {
|
||||
continue
|
||||
}
|
||||
|
||||
let failureMessage = "XCTAssertEqualWithAccuracy failed: (\(actual)) is not equal to (\(expected)) +/- (\(accuracy))"
|
||||
let userMessage = message()
|
||||
let message = "\(failureMessage) - \(userMessage)"
|
||||
XCTFail(message, file: file, line: line)
|
||||
|
||||
return false
|
||||
guard case .failure(let error) = result else {
|
||||
return
|
||||
}
|
||||
|
||||
return true
|
||||
return fail(prefix: prefix, failureMessage: error.description, file: file, line: line)
|
||||
}
|
||||
|
||||
/// Allows comparing:
|
||||
|
@ -86,31 +216,48 @@ func XCTAssertEqual<T, U>(
|
|||
/// - `[[Double]]`
|
||||
/// - `Matrix<Float>`
|
||||
/// - `Matrix<Double>`
|
||||
@discardableResult
|
||||
func XCTAssertEqual<T, U, V>(
|
||||
_ expression1: @autoclosure () throws -> T,
|
||||
_ expression2: @autoclosure () throws -> T,
|
||||
accuracy: V,
|
||||
accuracy: V? = nil,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) -> Bool
|
||||
where T: Collection, U: Collection, T.Element == U, U.Element == V, V: FloatingPoint {
|
||||
let (actualValues, expectedValues): (T, T)
|
||||
) where T: Collection, U: Collection, T.Element == U, U.Element == V, V: FloatingPoint & ExpressibleByFloatLiteral {
|
||||
XCTAssertEqual2D(
|
||||
try expression1(),
|
||||
try expression2(),
|
||||
accuracy: accuracy,
|
||||
message(),
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
func XCTAssertEqual2D<T, U, V>(
|
||||
_ expression1: @autoclosure () throws -> T,
|
||||
_ expression2: @autoclosure () throws -> T,
|
||||
accuracy: V? = nil,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where T: Collection, U: Collection, T.Element == U, U.Element == V, V: FloatingPoint & ExpressibleByFloatLiteral {
|
||||
let prefix: Prefix = (accuracy == nil) ? .assertEqual : .assertEqualWithAccuracy
|
||||
|
||||
let (actual, expected): (T, T)
|
||||
|
||||
do {
|
||||
(actualValues, expectedValues) = (try expression1(), try expression2())
|
||||
(actual, expected) = (try expression1(), try expression2())
|
||||
} catch let error {
|
||||
XCTFail("Error: \(error)", file: file, line: line)
|
||||
return false
|
||||
let message = String(describing: error)
|
||||
return fail(prefix: prefix, failureMessage: message, file: file, line: line)
|
||||
}
|
||||
|
||||
XCTAssertEqual(actualValues.count, expectedValues.count, file: file, line: line)
|
||||
let result = checkGrid(actual, expected, accuracy: accuracy ?? 0.0)
|
||||
|
||||
for (actual, expected) in Swift.zip(actualValues, expectedValues) {
|
||||
guard XCTAssertEqual(actual, expected, accuracy: accuracy) else {
|
||||
return false
|
||||
}
|
||||
guard case .failure(let error) = result else {
|
||||
return
|
||||
}
|
||||
return true
|
||||
|
||||
return fail(prefix: prefix, failureMessage: error.description, file: file, line: line)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue