Skip to content

Replaced XCTAssertArray(Float|Double)EqualWithAccuracy(_:_:accuracy:) #104

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

Merged
merged 3 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Improved assert helper ergonomics
  • Loading branch information
regexident committed Aug 29, 2019
commit 236313568dffc437c0cd07f4ecf18b7f70542785
9 changes: 6 additions & 3 deletions Tests/SurgeTests/MatrixTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down
217 changes: 182 additions & 35 deletions Tests/SurgeTests/XCTAssert+Surge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
///
/// ```
Expand All @@ -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
) 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
) -> Bool
where T: Collection, T.Element == U, U: FloatingPoint {
let (actualValues, expectedValues): (T, T)
) 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)

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)
let result = checkArray(actual, expected, accuracy: accuracy ?? 0.0)

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:
Expand All @@ -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)
}