Skip to content

Commit 1d51771

Browse files
committed
Changes to allow source-compatible tests and a new instance for each run
* Remove XCTestCase and XCTestCaseProvider protocols * Add XCTestCase class so `setUp` and `tearDown` can be marked `override` * Change `invokeTest` to be static and create new instances for each test * Add a generic type-erasure function adapting a list of static method references on a type to the required signature.
1 parent b1f40a9 commit 1d51771

File tree

5 files changed

+74
-52
lines changed

5 files changed

+74
-52
lines changed

Sources/XCTest/XCTAssert.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private func _XCTEvaluateAssertion(assertion: _XCTAssertion, @autoclosure messag
104104
/// to it evaluates to false.
105105
///
106106
/// - Requires: This and all other XCTAssert* functions must be called from
107-
/// within a test method, as indicated by `XCTestCaseProvider.allTests`.
107+
/// within a test method, as passed to `XCTMain`.
108108
/// Assertion failures that occur outside of a test method will *not* be
109109
/// reported as failures.
110110
///

Sources/XCTest/XCTestCase.swift

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,44 @@
88
//
99
//
1010
// XCTestCase.swift
11-
// Base protocol (and extension with default methods) for test cases
11+
// Base class for test cases
1212
//
1313

14-
public protocol XCTestCase : XCTestCaseProvider {
15-
func setUp()
16-
func tearDown()
14+
/// This is a compound type used by `XCTMain` to represent tests to run. It combines an
15+
/// `XCTestCase` subclass type with the list of test methods to invoke on the test case.
16+
/// This type is intended to be produced by the `testCase` helper function.
17+
/// - seealso: `testCase`
18+
/// - seealso: `XCTMain`
19+
public typealias XCTestCaseEntry = (testCaseClass: XCTestCase.Type, allTests: [(String, XCTestCase throws -> Void)])
20+
21+
public class XCTestCase {
22+
23+
public required init() {
24+
}
25+
26+
public func setUp() {
27+
}
28+
29+
public func tearDown() {
30+
}
31+
}
32+
33+
/// Wrapper function allowing an array of static test case methods to fit
34+
/// the signature required by `XCTMain`
35+
/// - seealso: `XCTMain`
36+
public func testCase<T: XCTestCase>(allTests: [(String, T -> () throws -> Void)]) -> XCTestCaseEntry {
37+
let tests: [(String, XCTestCase throws -> Void)] = allTests.map({ ($0.0, test($0.1)) })
38+
return (T.self, tests)
39+
}
40+
41+
private func test<T: XCTestCase>(testFunc: T -> () throws -> Void) -> XCTestCase throws -> Void {
42+
return { testCaseType in
43+
guard let testCase: T = testCaseType as? T else {
44+
fatalError("Attempt to invoke test on class \(T.self) with incompatible instance type \(testCaseType.dynamicType)")
45+
}
46+
47+
try testFunc(testCase)()
48+
}
1749
}
1850

1951
extension XCTestCase {
@@ -26,55 +58,55 @@ extension XCTestCase {
2658
// TODO: When using the Objective-C runtime, XCTest is able to throw an exception from an assert and then catch it at the frame above the test method. This enables the framework to effectively stop all execution in the current test. There is no such facility in Swift. Until we figure out how to get a compatible behavior, we have decided to hard-code the value of 'true' for continue after failure.
2759
}
2860
}
29-
30-
public func invokeTest() {
31-
let tests = self.allTests
61+
62+
internal static func invokeTests(tests: [(String, XCTestCase throws -> Void)]) {
3263
var totalDuration = 0.0
3364
var totalFailures = 0
3465
var unexpectedFailures = 0
3566
let overallDuration = measureTimeExecutingBlock {
3667
for (name, test) in tests {
37-
let method = "\(self.dynamicType).\(name)"
68+
let testCase = self.init()
69+
let fullName = "\(testCase.dynamicType).\(name)"
3870

3971
var failures = [XCTFailure]()
4072
XCTFailureHandler = { failure in
41-
if !self.continueAfterFailure {
42-
failure.emit(method)
73+
if !testCase.continueAfterFailure {
74+
failure.emit(fullName)
4375
fatalError("Terminating execution due to test failure", file: failure.file, line: failure.line)
4476
} else {
4577
failures.append(failure)
4678
}
4779
}
4880

49-
XCTPrint("Test Case '\(method)' started.")
81+
XCTPrint("Test Case '\(fullName)' started.")
5082

51-
setUp()
83+
testCase.setUp()
5284

5385
let duration = measureTimeExecutingBlock {
5486
do {
55-
try test()
87+
try test(testCase)
5688
} catch {
5789
let unexpectedFailure = XCTFailure(message: "", failureDescription: "threw error \"\(error)\"", expected: false, file: "<EXPR>", line: 0)
5890
XCTFailureHandler!(unexpectedFailure)
5991
}
6092
}
6193

62-
tearDown()
94+
testCase.tearDown()
6395

6496
totalDuration += duration
6597

6698
var result = "passed"
6799
for failure in failures {
68-
failure.emit(method)
100+
failure.emit(fullName)
69101
totalFailures += 1
70102
if !failure.expected {
71103
unexpectedFailures += 1
72104
}
73105
result = failures.count > 0 ? "failed" : "passed"
74106
}
75107

76-
XCTPrint("Test Case '\(method)' \(result) (\(printableStringForTimeInterval(duration)) seconds).")
77-
XCTAllRuns.append(XCTRun(duration: duration, method: method, passed: failures.count == 0, failures: failures))
108+
XCTPrint("Test Case '\(fullName)' \(result) (\(printableStringForTimeInterval(duration)) seconds).")
109+
XCTAllRuns.append(XCTRun(duration: duration, method: fullName, passed: failures.count == 0, failures: failures))
78110
XCTFailureHandler = nil
79111
}
80112
}
@@ -90,12 +122,4 @@ extension XCTestCase {
90122

91123
XCTPrint("Executed \(tests.count) test\(testCountSuffix), with \(totalFailures) failure\(failureSuffix) (\(unexpectedFailures) unexpected) in \(printableStringForTimeInterval(totalDuration)) (\(printableStringForTimeInterval(overallDuration))) seconds")
92124
}
93-
94-
public func setUp() {
95-
96-
}
97-
98-
public func tearDown() {
99-
100-
}
101125
}

Sources/XCTest/XCTestCaseProvider.swift

Lines changed: 0 additions & 18 deletions
This file was deleted.

Sources/XCTest/XCTestMain.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,31 @@ internal struct XCTRun {
4848
/// Starts a test run for the specified test cases.
4949
///
5050
/// This function will not return. If the test cases pass, then it will call `exit(0)`. If there is a failure, then it will call `exit(1)`.
51-
/// - Parameter testCases: An array of test cases to run.
52-
@noreturn public func XCTMain(testCases: [XCTestCase]) {
51+
/// Example usage:
52+
///
53+
/// class TestFoo: XCTestCase {
54+
/// static var allTests : [(String, TestFoo -> () throws -> Void)] {
55+
/// return [
56+
/// ("test_foo", test_foo),
57+
/// ("test_bar", test_bar),
58+
/// ]
59+
/// }
60+
///
61+
/// func test_foo() {
62+
/// // Test things...
63+
/// }
64+
///
65+
/// // etc...
66+
/// }
67+
///
68+
/// XCTMain([ testCase(TestFoo.allTests) ])
69+
///
70+
/// - Parameter testCases: An array of test cases run, each produced by a call to the `testCase` function
71+
/// - seealso: `testCase`
72+
@noreturn public func XCTMain(testCases: [XCTestCaseEntry]) {
5373
let overallDuration = measureTimeExecutingBlock {
54-
for testCase in testCases {
55-
testCase.invokeTest()
74+
for (testCase, tests) in testCases {
75+
testCase.invokeTests(tests)
5676
}
5777
}
5878

XCTest.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
/* Begin PBXBuildFile section */
1010
C265F66F1C3AEB6A00520CF9 /* XCTAssert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C265F6691C3AEB6A00520CF9 /* XCTAssert.swift */; };
1111
C265F6701C3AEB6A00520CF9 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C265F66A1C3AEB6A00520CF9 /* XCTestCase.swift */; };
12-
C265F6711C3AEB6A00520CF9 /* XCTestCaseProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C265F66B1C3AEB6A00520CF9 /* XCTestCaseProvider.swift */; };
1312
C265F6721C3AEB6A00520CF9 /* XCTestMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = C265F66C1C3AEB6A00520CF9 /* XCTestMain.swift */; };
1413
C265F6731C3AEB6A00520CF9 /* XCTimeUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C265F66D1C3AEB6A00520CF9 /* XCTimeUtilities.swift */; };
1514
/* End PBXBuildFile section */
@@ -34,7 +33,6 @@
3433
B1384A431C1B3E8700EDF031 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
3534
C265F6691C3AEB6A00520CF9 /* XCTAssert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTAssert.swift; sourceTree = "<group>"; };
3635
C265F66A1C3AEB6A00520CF9 /* XCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTestCase.swift; sourceTree = "<group>"; };
37-
C265F66B1C3AEB6A00520CF9 /* XCTestCaseProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTestCaseProvider.swift; sourceTree = "<group>"; };
3836
C265F66C1C3AEB6A00520CF9 /* XCTestMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTestMain.swift; sourceTree = "<group>"; };
3937
C265F66D1C3AEB6A00520CF9 /* XCTimeUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCTimeUtilities.swift; sourceTree = "<group>"; };
4038
DA78F7E91C4039410082E15B /* lit.cfg */ = {isa = PBXFileReference; lastKnownFileType = text; path = lit.cfg; sourceTree = "<group>"; };
@@ -131,7 +129,6 @@
131129
children = (
132130
C265F6691C3AEB6A00520CF9 /* XCTAssert.swift */,
133131
C265F66A1C3AEB6A00520CF9 /* XCTestCase.swift */,
134-
C265F66B1C3AEB6A00520CF9 /* XCTestCaseProvider.swift */,
135132
C265F66C1C3AEB6A00520CF9 /* XCTestMain.swift */,
136133
C265F66D1C3AEB6A00520CF9 /* XCTimeUtilities.swift */,
137134
);
@@ -327,7 +324,6 @@
327324
files = (
328325
C265F6731C3AEB6A00520CF9 /* XCTimeUtilities.swift in Sources */,
329326
C265F6701C3AEB6A00520CF9 /* XCTestCase.swift in Sources */,
330-
C265F6711C3AEB6A00520CF9 /* XCTestCaseProvider.swift in Sources */,
331327
C265F66F1C3AEB6A00520CF9 /* XCTAssert.swift in Sources */,
332328
C265F6721C3AEB6A00520CF9 /* XCTestMain.swift in Sources */,
333329
);

0 commit comments

Comments
 (0)