Skip to content

Commit

Permalink
Add more failure message context to XCT helpers (#109)
Browse files Browse the repository at this point in the history
* Add more failure message context to XCT helpers

* wip
  • Loading branch information
stephencelis authored Apr 3, 2023
1 parent 3542cda commit fc45e7b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 26 deletions.
12 changes: 12 additions & 0 deletions Sources/CasePaths/EnumReflection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ extension EnumMetadata {

return type
}

@_spi(Reflection) public func caseName(forTag tag: UInt32) -> String? {
self.typeDescriptor.fieldDescriptor?.field(atIndex: tag).name
}
}

@_silgen_name("swift_getTypeByMangledNameInContext")
Expand Down Expand Up @@ -541,6 +545,14 @@ private struct FieldRecord {
.loadRelativePointer()
.map { MangledTypeName(ptr: $0.assumingMemoryBound(to: UInt8.self)) }
}

var name: String? {
self.ptr
.advanced(by: 4)
.advanced(by: 4)
.loadRelativePointer()
.map { String(cString: $0.assumingMemoryBound(to: CChar.self)) }
}
}

extension FieldRecord {
Expand Down
38 changes: 23 additions & 15 deletions Sources/CasePaths/XCTUnwrap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,33 @@ import Foundation
/// Asserts that an enum value matches a particular case and returns the associated value.
///
/// - Parameters:
/// - expression: An enum value.
/// - enum: An enum value.
/// - extract: A closure that attempts to extract a particular case from the enum. You can supply
/// a case path literal here, like '/Enum.case'.
/// - message: An optional description of a failure.
/// - Returns: The unwrapped associated value from the matched case of the enum.
public func XCTUnwrap<Root, Case>(
_ expression: @autoclosure () throws -> Root,
case extract: (Root) -> Case?,
public func XCTUnwrap<Enum, Case>(
_ enum: @autoclosure () throws -> Enum,
case extract: (Enum) -> Case?,
_ message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) throws -> Case {
guard let value = try extract(expression())
let `enum` = try `enum`()
guard let value = extract(`enum`)
else {
#if canImport(ObjectiveC)
_ = XCTCurrentTestCase?.perform(Selector(("setContinueAfterFailure:")), with: false)
#endif
let message = message()
XCTFail(
"""
XCTUnwrap failed: expected non-nil value of type "\(typeName(Case.self))"\
\(message.isEmpty ? "" : " - " + message)
XCTUnwrap failed: expected to extract value of type "\(typeName(Case.self))" from \
"\(typeName(Enum.self))"\
\(message.isEmpty ? "" : " - " + message)
Actual:
\(String(describing: `enum`))
""",
file: file,
line: line
Expand All @@ -38,19 +43,19 @@ public func XCTUnwrap<Root, Case>(
/// Asserts that an enum value matches a particular case and modifies the associated value in place.
///
/// - Parameters:
/// - root: An enum value.
/// - enum: An enum value.
/// - casePath: A case path that can extract and embed the associated value of a particular case.
/// - message: An optional description of a failure.
/// - body: A closure that can modify the associated value of the given case.
public func XCTModify<Root, Case>(
_ root: inout Root,
case casePath: CasePath<Root, Case>,
public func XCTModify<Enum, Case>(
_ enum: inout Enum,
case casePath: CasePath<Enum, Case>,
_ message: @autoclosure () -> String = "",
_ body: (inout Case) throws -> Void,
file: StaticString = #file,
line: UInt = #line
) {
guard var value = casePath.extract(from: root)
guard var value = casePath.extract(from: `enum`)
else {
#if canImport(ObjectiveC)
_ = XCTCurrentTestCase?.perform(Selector(("setContinueAfterFailure:")), with: false)
Expand All @@ -59,8 +64,11 @@ public func XCTModify<Root, Case>(
XCTFail(
"""
XCTModify failed: expected to extract value of type "\(typeName(Case.self))" from \
"\(typeName(Root.self))"\
\(message.isEmpty ? "" : " - " + message)
"\(typeName(Enum.self))"\
\(message.isEmpty ? "" : " - " + message)
Actual:
\(`enum`)
""",
file: file,
line: line
Expand All @@ -85,7 +93,7 @@ public func XCTModify<Root, Case>(
""")
}

root = casePath.embed(value)
`enum` = casePath.embed(value)
}

@_spi(Internals) public enum XCTModifyLocals {
Expand Down
28 changes: 20 additions & 8 deletions Tests/CasePathsTests/XCTModifyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import XCTest

final class XCTModifyTests: XCTestCase {
func testXCTModiftyFailure() throws {
func testXCTModifyFailure() throws {
try XCTSkipIf(ProcessInfo.processInfo.environment["CI"] != nil)

XCTExpectFailure {
$0.compactDescription == """
XCTModify failed: expected to extract value of type "Int" from "Result<Int, Error>"
XCTModify failed: expected to extract value of type "Int" from "Result<Int, Error>"
Actual:
failure(CasePathsTests.SomeError())
"""
}

Expand All @@ -18,13 +21,16 @@
}
}

func testXCTModiftyFailure_OptionalPromotion() throws {
func testXCTModifyFailure_OptionalPromotion() throws {
try XCTSkipIf(ProcessInfo.processInfo.environment["CI"] != nil)

XCTExpectFailure {
$0.compactDescription == """
XCTModify failed: expected to extract value of type "Sheet.State" from \
"Destination.State?"
"Destination.State?"
Actual:
Optional(CasePathsTests.Destination.State.alert)
"""
}

Expand All @@ -34,13 +40,16 @@
}
}

func testXCTModiftyFailure_Nil_OptionalPromotion() throws {
func testXCTModifyFailure_Nil_OptionalPromotion() throws {
try XCTSkipIf(ProcessInfo.processInfo.environment["CI"] != nil)

XCTExpectFailure {
$0.compactDescription == """
XCTModify failed: expected to extract value of type "Int" from \
"Optional<Result<Int, Error>>"
"Optional<Result<Int, Error>>"
Actual:
nil
"""
}

Expand All @@ -56,7 +65,10 @@
XCTExpectFailure {
$0.compactDescription == """
XCTModify failed: expected to extract value of type "Int" from "Result<Int, Error>" - \
Should be success
Should be success …
Actual:
failure(CasePathsTests.SomeError())
"""
}

Expand Down Expand Up @@ -131,7 +143,7 @@
}
}

private struct SomeError: Error, Equatable {}
struct SomeError: Error, Equatable {}

struct Sheet {
struct State {
Expand Down
13 changes: 10 additions & 3 deletions Tests/CasePathsTests/XCTUnwrapTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

XCTExpectFailure {
$0.compactDescription == """
XCTUnwrap failed: expected non-nil value of type "Error"
XCTUnwrap failed: expected to extract value of type "Error" from "Result<Int, Error>"
Actual:
success(2)
"""
}
_ = try XCTUnwrap(Result<Int, Error>.success(2), case: /Result.failure)
Expand All @@ -19,10 +22,14 @@

XCTExpectFailure {
$0.compactDescription == """
XCTUnwrap failed: expected non-nil value of type "Error" - Should be success
XCTUnwrap failed: expected to extract value of type "Error" from "Result<Int, Error>" - \
Should be 'failure' …
Actual:
success(2)
"""
}
_ = try XCTUnwrap(Result<Int, Error>.success(2), case: /Result.failure, "Should be success")
_ = try XCTUnwrap(Result<Int, Error>.success(2), case: /Result.failure, "Should be 'failure'")
}

func testXCTUnwrapPass() throws {
Expand Down

0 comments on commit fc45e7b

Please sign in to comment.