Skip to content

[XCTestObservation] Add test bundle observation #81

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 1 commit into from
Mar 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions Sources/XCTest/XCTestMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ internal struct XCTRun {
/// - Parameter testCases: An array of test cases run, each produced by a call to the `testCase` function
/// - seealso: `testCase`
@noreturn public func XCTMain(testCases: [XCTestCaseEntry]) {
let observationCenter = XCTestObservationCenter.sharedTestObservationCenter()
let testBundle = NSBundle.mainBundle()
observationCenter.testBundleWillStart(testBundle)

let filter = TestFiltering()

let overallDuration = measureTimeExecutingBlock {
Expand All @@ -99,6 +103,7 @@ internal struct XCTRun {
}

XCTPrint("Total executed \(XCTAllRuns.count) test\(testCountSuffix), with \(totalFailures) failure\(failureSuffix) (\(totalUnexpectedFailures) unexpected) in \(printableStringForTimeInterval(totalDuration)) (\(printableStringForTimeInterval(overallDuration))) seconds")
observationCenter.testBundleDidFinish(testBundle)
exit(totalFailures > 0 ? 1 : 0)
}

Expand Down
21 changes: 21 additions & 0 deletions Sources/XCTest/XCTestObservation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@
// Hooks for being notified about progress during a test run.
//

#if os(Linux) || os(FreeBSD)
import Foundation
#else
import SwiftFoundation
#endif

/// `XCTestObservation` provides hooks for being notified about progress during a
/// test run.
/// - seealso: `XCTestObservationCenter`
public protocol XCTestObservation: class {

/// Sent immediately before tests begin as a hook for any pre-testing setup.
/// - Parameter testBundle: The bundle containing the tests that were
/// executed.
func testBundleWillStart(testBundle: NSBundle)

/// Called just before a test begins executing.
/// - Parameter testCase: The test case that is about to start. Its `name`
/// property can be used to identify it.
Expand All @@ -34,6 +46,15 @@ public protocol XCTestObservation: class {
/// - Parameter testCase: The test case that finished. Its `name` property
/// can be used to identify it.
func testCaseDidFinish(testCase: XCTestCase)

/// Sent immediately after all tests have finished as a hook for any
/// post-testing activity. The test process will generally exit after this
/// method returns, so if there is long running and/or asynchronous work to
/// be done after testing, be sure to implement this method in a way that
/// it blocks until all such activity is complete.
/// - Parameter testBundle: The bundle containing the tests that were
/// executed.
func testBundleDidFinish(testBundle: NSBundle)
}

// All `XCTestObservation` methods are optional, so empty default implementations are provided
Expand Down
13 changes: 13 additions & 0 deletions Sources/XCTest/XCTestObservationCenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
// Notification center for test run progress events.
//

#if os(Linux) || os(FreeBSD)
import Foundation
#else
import SwiftFoundation
#endif

/// Provides a registry for objects wishing to be informed about progress
/// during the course of a test run. Observers must implement the
/// `XCTestObservation` protocol
Expand All @@ -37,6 +43,9 @@ public class XCTestObservationCenter {
observers.remove(testObserver.wrapper)
}

internal func testBundleWillStart(testBundle: NSBundle) {
forEachObserver { $0.testBundleWillStart(testBundle) }
}

internal func testCaseWillStart(testCase: XCTestCase) {
forEachObserver { $0.testCaseWillStart(testCase) }
Expand All @@ -50,6 +59,10 @@ public class XCTestObservationCenter {
forEachObserver { $0.testCaseDidFinish(testCase) }
}

internal func testBundleDidFinish(testBundle: NSBundle) {
forEachObserver { $0.testBundleDidFinish(testBundle) }
}

private func forEachObserver(@noescape body: XCTestObservation -> Void) {
for observer in observers {
body(observer.object)
Expand Down
40 changes: 30 additions & 10 deletions Tests/Functional/Observation/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@

#if os(Linux) || os(FreeBSD)
import XCTest
import Foundation
#else
import SwiftXCTest
import SwiftFoundation
#endif

class Observer: XCTestObservation {
var startedBundlePaths = [String]()
var startedTestCaseNames = [String]()
var failureDescriptions = [String]()
var finishedTestCaseNames = [String]()
var finishedBundlePaths = [String]()

func testBundleWillStart(testBundle: NSBundle) {
startedBundlePaths.append(testBundle.bundlePath)
}

func testCaseWillStart(testCase: XCTestCase) {
startedTestCaseNames.append(testCase.name)
Expand All @@ -24,9 +32,14 @@ class Observer: XCTestObservation {
func testCaseDidFinish(testCase: XCTestCase) {
finishedTestCaseNames.append(testCase.name)
}

func testBundleDidFinish(testBundle: NSBundle) {
print("In \(#function)")
}
}

let observer = Observer()
XCTestObservationCenter.sharedTestObservationCenter().addTestObserver(observer)

class Observation: XCTestCase {
static var allTests: [(String, Observation -> () throws -> Void)] {
Expand All @@ -38,37 +51,44 @@ class Observation: XCTestCase {
}

// CHECK: Test Case 'Observation.test_one' started.
// CHECK: Test Case 'Observation.test_one' passed \(\d+\.\d+ seconds\).
// CHECK: .*/Observation/main.swift:\d+: error: Observation.test_one : failed - fail!
// CHECK: Test Case 'Observation.test_one' failed \(\d+\.\d+ seconds\).
func test_one() {
XCTAssertEqual(observer.startedTestCaseNames, [])
XCTAssertEqual(observer.startedBundlePaths.count, 1)
XCTAssertEqual(observer.startedTestCaseNames, ["Observation.test_one"])
XCTAssertEqual(observer.failureDescriptions, [])
XCTAssertEqual(observer.finishedTestCaseNames, [])
XCTAssertEqual(observer.finishedBundlePaths.count, 0)

XCTestObservationCenter.sharedTestObservationCenter().addTestObserver(observer)
XCTFail("fail!")
XCTAssertEqual(observer.failureDescriptions, ["failed - fail!"])
}

// CHECK: Test Case 'Observation.test_two' started.
// CHECK: .*/Observation/main.swift:\d+: error: Observation.test_two : failed - fail!
// CHECK: Test Case 'Observation.test_two' failed \(\d+\.\d+ seconds\).
// CHECK: Test Case 'Observation.test_two' passed \(\d+\.\d+ seconds\).
func test_two() {
XCTAssertEqual(observer.startedTestCaseNames, ["Observation.test_two"])
XCTAssertEqual(observer.startedBundlePaths.count, 1)
XCTAssertEqual(observer.startedTestCaseNames, ["Observation.test_one", "Observation.test_two"])
XCTAssertEqual(observer.finishedTestCaseNames,["Observation.test_one"])

XCTFail("fail!")
XCTAssertEqual(observer.failureDescriptions, ["failed - fail!"])
XCTAssertEqual(observer.finishedBundlePaths.count, 0)

XCTestObservationCenter.sharedTestObservationCenter().removeTestObserver(observer)
}

// CHECK: Test Case 'Observation.test_three' started.
// CHECK: Test Case 'Observation.test_three' passed \(\d+\.\d+ seconds\).
func test_three() {
XCTAssertEqual(observer.startedTestCaseNames, ["Observation.test_two"])
XCTAssertEqual(observer.startedBundlePaths.count, 1)
XCTAssertEqual(observer.startedTestCaseNames, ["Observation.test_one", "Observation.test_two"])
XCTAssertEqual(observer.finishedTestCaseNames,["Observation.test_one"])
XCTAssertEqual(observer.finishedBundlePaths.count, 0)

XCTestObservationCenter.sharedTestObservationCenter().addTestObserver(observer)
}
}

XCTMain([testCase(Observation.allTests)])

// CHECK: Executed 3 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: Total executed 3 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: In testBundleDidFinish