Skip to content

Commit b0cd443

Browse files
author
Mike Ferris
committed
Merge pull request swiftlang#22 from briancroom/improveAssertionMessages
Print additional details when emitting assertion failure messages
2 parents 1675ce3 + a4b543c commit b0cd443

File tree

3 files changed

+125
-32
lines changed

3 files changed

+125
-32
lines changed

Sources/XCTest/XCTAssert.swift

Lines changed: 121 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,88 @@
1010
// XCTAssert.swift
1111
//
1212

13-
/// The primitive assertion function for XCTest. All other XCTAssert* functions
14-
/// are implemented in terms of this. This function emits a test failure if the
15-
/// general Bool expression passed to it evaluates to false.
13+
private enum _XCTAssertion {
14+
case Equal(Any, Any)
15+
case EqualWithAccuracy(Any, Any, accuracy: Any)
16+
case GreaterThan(Any, Any)
17+
case GreaterThanOrEqual(Any, Any)
18+
case LessThan(Any, Any)
19+
case LessThanOrEqual(Any, Any)
20+
case NotEqual(Any, Any)
21+
case NotEqualWithAccuracy(Any, Any, accuracy: Any)
22+
case Nil(Any)
23+
case NotNil
24+
case True
25+
case False
26+
case Fail
27+
28+
var name: String? {
29+
switch(self) {
30+
case .Equal: return "XCTAssertEqual"
31+
case .EqualWithAccuracy: return "XCTAssertEqualWithAccuracy"
32+
case .GreaterThan: return "XCTAssertGreaterThan"
33+
case .GreaterThanOrEqual: return "XCTAssertGreaterThanOrEqual"
34+
case .LessThan: return "XCTAssertLessThan"
35+
case .LessThanOrEqual: return "XCTAssertLessThanOrEqual"
36+
case .NotEqual: return "XCTAssertNotEqual"
37+
case .NotEqualWithAccuracy: return "XCTAssertNotEqualWithAccuracy"
38+
case .Nil: return "XCTAssertNil"
39+
case .NotNil: return "XCTAssertNotNil"
40+
case .True: return "XCTAssertTrue"
41+
case .False: return "XCTAssertFalse"
42+
case .Fail: return nil
43+
}
44+
}
45+
46+
var failureDetails: String? {
47+
switch(self) {
48+
case .Equal(let value1, let value2):
49+
return "(\"\(value1)\") is not equal to (\"\(value2)\")"
50+
case .EqualWithAccuracy(let value1, let value2, let accuracy):
51+
return "(\"\(value1)\") is not equal to (\"\(value2)\") +/- (\"\(accuracy)\")"
52+
case .GreaterThan(let value1, let value2):
53+
return "(\"\(value1)\") is not greater than (\"\(value2)\")"
54+
case .GreaterThanOrEqual(let value1, let value2):
55+
return "(\"\(value1)\") is less than (\"\(value2)\")"
56+
case .LessThan(let value1, let value2):
57+
return "(\"\(value1)\") is not less than (\"\(value2)\")"
58+
case .LessThanOrEqual(let value1, let value2):
59+
return "(\"\(value1)\") is greater than (\"\(value2)\")"
60+
case .NotEqual(let value1, let value2):
61+
return "(\"\(value1)\") is equal to (\"\(value2)\")"
62+
case .NotEqualWithAccuracy(let value1, let value2, let accuracy):
63+
return "(\"\(value1)\") is equal to (\"\(value2)\") +/- (\"\(accuracy)\")"
64+
case .Nil(let value):
65+
return "\"\(value)\""
66+
case .NotNil: return nil
67+
case .True: return nil
68+
case .False: return nil
69+
case .Fail: return nil
70+
}
71+
}
72+
73+
var failureDescription: String {
74+
switch (name, failureDetails) {
75+
case let (name?, description?):
76+
return "\(name) failed: \(description)"
77+
case let (name?, _):
78+
return "\(name) failed"
79+
default:
80+
return "failed"
81+
}
82+
}
83+
}
84+
85+
private func _XCTAssert(@autoclosure expression: () -> BooleanType, @autoclosure assertion: () -> _XCTAssertion, _ message: String, file: StaticString, line: UInt) {
86+
if !expression().boolValue {
87+
if let test = XCTCurrentTestCase {
88+
test.testFailure(message, failureDescription: assertion().failureDescription, expected: true, file: file, line: line)
89+
}
90+
}
91+
}
92+
93+
/// This function emits a test failure if the general Bool expression passed
94+
/// to it evaluates to false.
1695
///
1796
/// - Requires: This and all other XCTAssert* functions must be called from
1897
/// within a test method, as indicated by `XCTestCaseProvider.allTests`.
@@ -67,93 +146,106 @@
67146
/// Now calling failures in `AssertEmpty` will be reported in the file and on
68147
/// the line that the assert function is *called*, not where it is defined.
69148
public func XCTAssert(@autoclosure expression: () -> BooleanType, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
70-
if !expression().boolValue {
71-
if let test = XCTCurrentTestCase {
72-
test.testFailure(message, expected: true, file: file, line: line)
73-
}
74-
}
149+
XCTAssertTrue(expression, message, file: file, line: line)
75150
}
76151

77152
public func XCTAssertEqual<T : Equatable>(@autoclosure expression1: () -> T?, @autoclosure _ expression2: () -> T?, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
78-
XCTAssert(expression1() == expression2(), message, file: file, line: line)
153+
let (value1, value2) = (expression1(), expression2())
154+
_XCTAssert(value1 == value2, assertion: .Equal(value1, value2), message, file: file, line: line)
79155
}
80156

81157
public func XCTAssertEqual<T : Equatable>(@autoclosure expression1: () -> ArraySlice<T>, @autoclosure _ expression2: () -> ArraySlice<T>, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
82-
XCTAssert(expression1() == expression2(), message, file: file, line: line)
158+
let (value1, value2) = (expression1(), expression2())
159+
_XCTAssert(value1 == value2, assertion: .Equal(value1, value2), message, file: file, line: line)
83160
}
84161

85162
public func XCTAssertEqual<T : Equatable>(@autoclosure expression1: () -> ContiguousArray<T>, @autoclosure _ expression2: () -> ContiguousArray<T>, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
86-
XCTAssert(expression1() == expression2(), message, file: file, line: line)
163+
let (value1, value2) = (expression1(), expression2())
164+
_XCTAssert(value1 == value2, assertion: .Equal(value1, value2), message, file: file, line: line)
87165
}
88166

89167
public func XCTAssertEqual<T : Equatable>(@autoclosure expression1: () -> [T], @autoclosure _ expression2: () -> [T], _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
90-
XCTAssert(expression1() == expression2(), message, file: file, line: line)
168+
let (value1, value2) = (expression1(), expression2())
169+
_XCTAssert(value1 == value2, assertion: .Equal(value1, value2), message, file: file, line: line)
91170
}
92171

93172
public func XCTAssertEqual<T, U : Equatable>(@autoclosure expression1: () -> [T : U], @autoclosure _ expression2: () -> [T : U], _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
94-
XCTAssert(expression1() == expression2(), message, file: file, line: line)
173+
let (value1, value2) = (expression1(), expression2())
174+
_XCTAssert(value1 == value2, assertion: .Equal(value1, value2), message, file: file, line: line)
95175
}
96176

97177
public func XCTAssertEqualWithAccuracy<T : FloatingPointType>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, accuracy: T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
98-
XCTAssert(abs(expression1().distanceTo(expression2())) <= abs(accuracy.distanceTo(T(0))), message, file: file, line: line)
178+
let (value1, value2) = (expression1(), expression2())
179+
_XCTAssert(abs(value1.distanceTo(value2)) <= abs(accuracy.distanceTo(T(0))), assertion: .EqualWithAccuracy(value1, value2, accuracy: accuracy), message, file: file, line: line)
99180
}
100181

101182
public func XCTAssertFalse(@autoclosure expression: () -> BooleanType, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
102-
XCTAssert(!expression().boolValue, message, file: file, line: line)
183+
_XCTAssert(!expression().boolValue, assertion: .False, message, file: file, line: line)
103184
}
104185

105186
public func XCTAssertGreaterThan<T : Comparable>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
106-
XCTAssert(expression1() > expression2(), message, file: file, line: line)
187+
let (value1, value2) = (expression1(), expression2())
188+
_XCTAssert(value1 > value2, assertion: .GreaterThan(value1, value2), message, file: file, line: line)
107189
}
108190

109191
public func XCTAssertGreaterThanOrEqual<T : Comparable>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
110-
XCTAssert(expression1() >= expression2(), message, file: file, line: line)
192+
let (value1, value2) = (expression1(), expression2())
193+
_XCTAssert(value1 >= value2, assertion: .GreaterThanOrEqual(value1, value2), message, file: file, line: line)
111194
}
112195

113196
public func XCTAssertLessThan<T : Comparable>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
114-
XCTAssert(expression1() < expression2(), message, file: file, line: line)
197+
let (value1, value2) = (expression1(), expression2())
198+
_XCTAssert(value1 < value2, assertion: .LessThan(value1, value2), message, file: file, line: line)
115199
}
116200

117201
public func XCTAssertLessThanOrEqual<T : Comparable>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
118-
XCTAssert(expression1() <= expression2(), message, file: file, line: line)
202+
let (value1, value2) = (expression1(), expression2())
203+
_XCTAssert(value1 <= value2, assertion: .LessThanOrEqual(value1, value2), message, file: file, line: line)
119204
}
120205

121206
public func XCTAssertNil(@autoclosure expression: () -> Any?, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
122-
XCTAssert(expression() == nil, message, file: file, line: line)
207+
let value = expression()
208+
_XCTAssert(value == nil, assertion: value == nil ? .Nil(()) : .Nil(value!), message, file: file, line: line)
123209
}
124210

125211
public func XCTAssertNotEqual<T : Equatable>(@autoclosure expression1: () -> T?, @autoclosure _ expression2: () -> T?, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
126-
XCTAssert(expression1() != expression2(), message, file: file, line: line)
212+
let (value1, value2) = (expression1(), expression2())
213+
_XCTAssert(value1 != value2, assertion: .NotEqual(value1, value2), message, file: file, line: line)
127214
}
128215

129216
public func XCTAssertNotEqual<T : Equatable>(@autoclosure expression1: () -> ContiguousArray<T>, @autoclosure _ expression2: () -> ContiguousArray<T>, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
130-
XCTAssert(expression1() != expression2(), message, file: file, line: line)
217+
let (value1, value2) = (expression1(), expression2())
218+
_XCTAssert(value1 != value2, assertion: .NotEqual(value1, value2), message, file: file, line: line)
131219
}
132220

133221
public func XCTAssertNotEqual<T : Equatable>(@autoclosure expression1: () -> ArraySlice<T>, @autoclosure _ expression2: () -> ArraySlice<T>, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
134-
XCTAssert(expression1() != expression2(), message, file: file, line: line)
222+
let (value1, value2) = (expression1(), expression2())
223+
_XCTAssert(value1 != value2, assertion: .NotEqual(value1, value2), message, file: file, line: line)
135224
}
136225

137226
public func XCTAssertNotEqual<T : Equatable>(@autoclosure expression1: () -> [T], @autoclosure _ expression2: () -> [T], _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
138-
XCTAssert(expression1() != expression2(), message, file: file, line: line)
227+
let (value1, value2) = (expression1(), expression2())
228+
_XCTAssert(value1 != value2, assertion: .NotEqual(value1, value2), message, file: file, line: line)
139229
}
140230

141231
public func XCTAssertNotEqual<T, U : Equatable>(@autoclosure expression1: () -> [T : U], @autoclosure _ expression2: () -> [T : U], _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
142-
XCTAssert(expression1() != expression2(), message, file: file, line: line)
232+
let (value1, value2) = (expression1(), expression2())
233+
_XCTAssert(value1 != value2, assertion: .NotEqual(value1, value2), message, file: file, line: line)
143234
}
144235

145236
public func XCTAssertNotEqualWithAccuracy<T : FloatingPointType>(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ accuracy: T, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
146-
XCTAssert(abs(expression1().distanceTo(expression2())) > abs(accuracy.distanceTo(T(0))), message, file: file, line: line)
237+
let (value1, value2) = (expression1(), expression2())
238+
_XCTAssert(abs(value1.distanceTo(value2)) > abs(accuracy.distanceTo(T(0))), assertion: .NotEqualWithAccuracy(value1, value2, accuracy: accuracy), message, file: file, line: line)
147239
}
148240

149241
public func XCTAssertNotNil(@autoclosure expression: () -> Any?, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
150-
XCTAssert(expression() != nil, message, file: file, line: line)
242+
_XCTAssert(expression() != nil, assertion: .NotNil, message, file: file, line: line)
151243
}
152244

153245
public func XCTAssertTrue(@autoclosure expression: () -> BooleanType, _ message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
154-
XCTAssert(expression().boolValue, message, file: file, line: line)
246+
_XCTAssert(expression().boolValue, assertion: .True, message, file: file, line: line)
155247
}
156248

157249
public func XCTFail(message: String = "", file: StaticString = __FILE__, line: UInt = __LINE__) {
158-
XCTAssert(false, message, file: file, line: line)
250+
_XCTAssert(false, assertion: .Fail, message, file: file, line: line)
159251
}

Sources/XCTest/XCTestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ extension XCTestCase {
7676
}
7777

7878
// This function is for the use of XCTestCase only, but we must make it public or clients will get a link failure when using XCTest (23476006)
79-
public func testFailure(message: String, expected: Bool, file: StaticString , line: UInt) {
79+
public func testFailure(message: String, failureDescription: String, expected: Bool, file: StaticString, line: UInt) {
8080
if !continueAfterFailure {
8181
assert(false, message, file: file, line: line)
8282
} else {
83-
XCTCurrentFailures.append(XCTFailure(message: message, expected: expected, file: file, line: line))
83+
XCTCurrentFailures.append(XCTFailure(message: message, failureDescription: failureDescription, expected: expected, file: file, line: line))
8484
}
8585
}
8686

Sources/XCTest/XCTestMain.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import Darwin
2020

2121
struct XCTFailure {
2222
var message: String
23+
var failureDescription: String
2324
var expected: Bool
2425
var file: StaticString
2526
var line: UInt
2627

2728
func emit(method: String) {
28-
print("\(file):\(line): \(expected ? "" : "unexpected ")error: \(method) : \(message)")
29+
print("\(file):\(line): \(expected ? "" : "unexpected ")error: \(method) : \(failureDescription) - \(message)")
2930
}
3031
}
3132

0 commit comments

Comments
 (0)