Skip to content

Commit c70c5f4

Browse files
author
Kyle Yoon
committed
[XCTestExpectation] Adding expectationForNotification constructor
- Prints the same description as Obj C - Uses the default notification center to observer for notifications - Optional handler returns true or false of whether the expectation was fulfilled - Tests check comparing the notification and object arguments for the expectation and notification - Tests check returning true or false in handler
1 parent 2df27c5 commit c70c5f4

File tree

5 files changed

+201
-0
lines changed

5 files changed

+201
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
//
9+
//
10+
// XCNotificationExpectationHandler.swift
11+
// A closure invoked by XCTestCase when a notification for the expectation is
12+
// observed.
13+
//
14+
15+
#if os(Linux) || os(FreeBSD)
16+
import Foundation
17+
#else
18+
import SwiftFoundation
19+
#endif
20+
21+
/// A block to be invoked when a notification specified by the expectation is
22+
/// observed.
23+
///
24+
/// - Parameter notification: The notification that the expectation was
25+
/// observing.
26+
public typealias XCNotificationExpectationHandler = (NSNotification) -> (Bool)

Sources/XCTest/XCTestCase.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,49 @@ extension XCTestCase {
308308
completionHandler(error)
309309
}
310310
}
311+
312+
/// Creates and returns an expectation for a notification.
313+
///
314+
/// - Parameter notificationName: The name of the notification the
315+
/// expectation observes.
316+
/// - Parameter object: The object whose notifications the expectation will
317+
/// receive; that is, only notifications with this object are observed by
318+
/// the test case. If you pass nil, the expectation doesn't use
319+
/// a notification's object to decide whether it is fulfilled.
320+
/// - Parameter handler: If provided, the handler will be invoked when the
321+
/// notification is observed. It will not be invoked on timeout. Use the
322+
/// handler to further investigate if the notification fulfills the
323+
/// expectation.
324+
public func expectationForNotification(notificationName: String, object objectToObserve: AnyObject?, handler: XCNotificationExpectationHandler?) -> XCTestExpectation {
325+
let objectDescription = objectToObserve == nil ? "any object" : "\(objectToObserve!)"
326+
let expectation = self.expectationWithDescription("Expect notification '\(notificationName)' from " + objectDescription)
327+
// Start observing the notification with specified name and object.
328+
var observer: NSObjectProtocol? = nil
329+
observer = NSNotificationCenter
330+
.defaultCenter()
331+
.addObserverForName(notificationName,
332+
object: objectToObserve,
333+
queue: nil,
334+
usingBlock: {
335+
notification in
336+
// If the handler is invoked, the test will
337+
// only pass if true is returned.
338+
if let handler = handler {
339+
if handler(notification) {
340+
expectation.fulfill()
341+
if let observer = observer as? AnyObject {
342+
NSNotificationCenter.defaultCenter().removeObserver(observer)
343+
}
344+
}
345+
} else {
346+
expectation.fulfill()
347+
if let observer = observer as? AnyObject {
348+
NSNotificationCenter.defaultCenter().removeObserver(observer)
349+
}
350+
}
351+
})
352+
353+
return expectation
354+
}
355+
311356
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %{swiftc} %s -o %{built_tests_dir}/Notifications
2+
// RUN: %{built_tests_dir}/Notifications > %t || true
3+
// RUN: %{xctest_checker} %t %s
4+
5+
#if os(Linux) || os(FreeBSD)
6+
import XCTest
7+
import Foundation
8+
#else
9+
import SwiftXCTest
10+
import SwiftFoundation
11+
#endif
12+
13+
class NotificationExpectationsTestCase: XCTestCase {
14+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithName_passes' started.
15+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithName_passes' passed \(\d+\.\d+ seconds\).
16+
func test_observeNotificationWithName_passes() {
17+
let notificationName = "notificationWithNameTest"
18+
expectationForNotification(notificationName, object:nil, handler:nil)
19+
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)
20+
waitForExpectationsWithTimeout(0.0, handler: nil)
21+
}
22+
23+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithNameAndObject_passes' started.
24+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithNameAndObject_passes' passed \(\d+\.\d+ seconds\).
25+
func test_observeNotificationWithNameAndObject_passes() {
26+
let notificationName = "notificationWithNameAndObjectTest"
27+
let dummyObject = NSObject()
28+
expectationForNotification(notificationName, object:dummyObject, handler:nil)
29+
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: dummyObject)
30+
waitForExpectationsWithTimeout(0.0, handler: nil)
31+
}
32+
33+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithNameAndObject_butExpectingNoObject_passes' started.
34+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithNameAndObject_butExpectingNoObject_passes' passed \(\d+\.\d+ seconds\).
35+
func test_observeNotificationWithNameAndObject_butExpectingNoObject_passes() {
36+
let notificationName = "notificationWithNameAndObject_expectNoObjectTest"
37+
expectationForNotification(notificationName, object:nil, handler:nil)
38+
let dummyObject = NSObject()
39+
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: dummyObject)
40+
waitForExpectationsWithTimeout(0.0, handler: nil)
41+
}
42+
43+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithIncorrectName_fails' started.
44+
// CHECK: .*/Tests/Functional/Asynchronous/Notifications/Expectations/main.swift:49: error: NotificationExpectationsTestCase.test_observeNotificationWithIncorrectName_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect notification 'expectedName' from any object
45+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithIncorrectName_fails' failed \(\d+\.\d+ seconds\).
46+
func test_observeNotificationWithIncorrectName_fails() {
47+
expectationForNotification("expectedName", object: nil, handler:nil)
48+
NSNotificationCenter.defaultCenter().postNotificationName("actualName", object: nil)
49+
waitForExpectationsWithTimeout(0.1, handler: nil)
50+
}
51+
52+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithIncorrectObject_fails' started.
53+
// CHECK: .*/Tests/Functional/Asynchronous/Notifications/Expectations/main.swift:61: error: NotificationExpectationsTestCase.test_observeNotificationWithIncorrectObject_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect notification 'notificationWithIncorrectObjectTest' from dummyObject
54+
// CHECK: Test Case 'NotificationExpectationsTestCase.test_observeNotificationWithIncorrectObject_fails' failed \(\d+\.\d+ seconds\).
55+
func test_observeNotificationWithIncorrectObject_fails() {
56+
let notificationName = "notificationWithIncorrectObjectTest"
57+
let dummyObject: NSString = "dummyObject"
58+
let anotherDummyObject = NSObject()
59+
expectationForNotification(notificationName, object: dummyObject, handler: nil)
60+
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object:anotherDummyObject)
61+
waitForExpectationsWithTimeout(0.1, handler: nil)
62+
}
63+
64+
static var allTests: [(String, NotificationExpectationsTestCase -> () throws -> Void)] {
65+
return [
66+
("test_observeNotificationWithName_passes", test_observeNotificationWithName_passes),
67+
("test_observeNotificationWithNameAndObject_passes", test_observeNotificationWithNameAndObject_passes),
68+
("test_observeNotificationWithNameAndObject_butExpectingNoObject_passes", test_observeNotificationWithNameAndObject_butExpectingNoObject_passes),
69+
("test_observeNotificationWithIncorrectName_fails", test_observeNotificationWithIncorrectName_fails),
70+
("test_observeNotificationWithIncorrectObject_fails", test_observeNotificationWithIncorrectObject_fails),
71+
]
72+
}
73+
}
74+
75+
XCTMain([testCase(NotificationExpectationsTestCase.allTests)])
76+
77+
// CHECK: Executed 5 tests, with 2 failures \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
78+
// CHECK: Total executed 5 tests, with 2 failures \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %{swiftc} %s -o %{built_tests_dir}/Handler
2+
// RUN: %{built_tests_dir}/Handler > %t || true
3+
// RUN: %{xctest_checker} %t %s
4+
5+
#if os(Linux) || os(FreeBSD)
6+
import XCTest
7+
import Foundation
8+
#else
9+
import SwiftXCTest
10+
import SwiftFoundation
11+
#endif
12+
13+
class NotificationHandlerTestCase: XCTestCase {
14+
// CHECK: Test Case 'NotificationHandlerTestCase.test_notificationNameIsObserved_handlerReturnsFalse_andFails' started.
15+
// CHECK: .*/Tests/Functional/Asynchronous/Notifications/Handler/main.swift:23: error: NotificationHandlerTestCase.test_notificationNameIsObserved_handlerReturnsFalse_andFails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect notification 'returnFalse' from any object
16+
// CHECK: Test Case 'NotificationHandlerTestCase.test_notificationNameIsObserved_handlerReturnsFalse_andFails' failed \(\d+\.\d+ seconds\).
17+
func test_notificationNameIsObserved_handlerReturnsFalse_andFails() {
18+
let _ = self.expectationForNotification("returnFalse", object: nil, handler: {
19+
notification in
20+
return false
21+
})
22+
NSNotificationCenter.defaultCenter().postNotificationName("returnFalse", object: nil)
23+
waitForExpectationsWithTimeout(0.1, handler: nil)
24+
}
25+
26+
// CHECK: Test Case 'NotificationHandlerTestCase.test_notificationNameIsObserved_handlerReturnsTrue_andPasses' started.
27+
// CHECK: Test Case 'NotificationHandlerTestCase.test_notificationNameIsObserved_handlerReturnsTrue_andPasses' passed \(\d+\.\d+ seconds\).
28+
func test_notificationNameIsObserved_handlerReturnsTrue_andPasses() {
29+
let _ = self.expectationForNotification("returnTrue", object: nil, handler: {
30+
notification in
31+
return true
32+
})
33+
NSNotificationCenter.defaultCenter().postNotificationName("returnTrue", object: nil)
34+
waitForExpectationsWithTimeout(0.1, handler: nil)
35+
}
36+
37+
static var allTests: [(String, NotificationHandlerTestCase -> () throws -> Void)] {
38+
return [
39+
("test_notificationNameIsObserved_handlerReturnsFalse_andFails", test_notificationNameIsObserved_handlerReturnsFalse_andFails),
40+
("test_notificationNameIsObserved_handlerReturnsTrue_andPasses", test_notificationNameIsObserved_handlerReturnsTrue_andPasses),
41+
]
42+
}
43+
}
44+
45+
XCTMain([testCase(NotificationHandlerTestCase.allTests)])
46+
47+
// CHECK: Executed 2 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
48+
// CHECK: Total executed 2 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds

XCTest.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
510957A91CA878410091D1A1 /* XCNotificationExpectationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510957A81CA878410091D1A1 /* XCNotificationExpectationHandler.swift */; };
1011
AE7DD6091C8E81A0006FC722 /* ArgumentParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7DD6071C8E81A0006FC722 /* ArgumentParser.swift */; };
1112
AE7DD60A1C8E81A0006FC722 /* TestFiltering.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7DD6081C8E81A0006FC722 /* TestFiltering.swift */; };
1213
AE7DD60C1C8F0513006FC722 /* XCTestObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE7DD60B1C8F0513006FC722 /* XCTestObservation.swift */; };
@@ -32,6 +33,7 @@
3233
/* End PBXContainerItemProxy section */
3334

3435
/* Begin PBXFileReference section */
36+
510957A81CA878410091D1A1 /* XCNotificationExpectationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCNotificationExpectationHandler.swift; sourceTree = "<group>"; };
3537
5B5D86DB1BBC74AD00234F36 /* SwiftXCTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftXCTest.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3638
AE7DD6061C8DC6C0006FC722 /* Functional */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Functional; sourceTree = "<group>"; };
3739
AE7DD6071C8E81A0006FC722 /* ArgumentParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentParser.swift; sourceTree = "<group>"; };
@@ -118,6 +120,7 @@
118120
AE9596E01C9692B8001A9EF0 /* XCTestObservationCenter.swift */,
119121
C265F66D1C3AEB6A00520CF9 /* XCTimeUtilities.swift */,
120122
DACC94411C8B87B900EC85F5 /* XCWaitCompletionHandler.swift */,
123+
510957A81CA878410091D1A1 /* XCNotificationExpectationHandler.swift */,
121124
);
122125
path = XCTest;
123126
sourceTree = "<group>";
@@ -249,6 +252,7 @@
249252
buildActionMask = 2147483647;
250253
files = (
251254
DACC94421C8B87B900EC85F5 /* XCWaitCompletionHandler.swift in Sources */,
255+
510957A91CA878410091D1A1 /* XCNotificationExpectationHandler.swift in Sources */,
252256
C265F6731C3AEB6A00520CF9 /* XCTimeUtilities.swift in Sources */,
253257
AE7DD60C1C8F0513006FC722 /* XCTestObservation.swift in Sources */,
254258
C265F6701C3AEB6A00520CF9 /* XCTestCase.swift in Sources */,

0 commit comments

Comments
 (0)