From 0062e121021d23a964253d040413a21263b10d3d Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 10:26:30 +0100 Subject: [PATCH 1/3] APP-2737 make connect method public --- Projects/SSEKit/SSEKit/SSEManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index e036903..833accb 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -80,7 +80,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// connect to the given SSE endpoint /// /// - Parameter url: url of the product probably http://:15081/notify - func connect(toURL url: URL, completion: CompletionClosure? = nil ) { + open func connect(toURL url: URL, completion: CompletionClosure? = nil ) { _ = self.queue.async { guard self.connectionState == .idle else { @@ -117,7 +117,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// disconnect the SSE connection socket /// /// - Parameter completion: completion closure - public func disconnect(completion:@escaping ()->() = {}) { + open func disconnect(completion:@escaping ()->() = {}) { _ = self.queue.async { guard self.connectionState != .disconnecting && self.connectionState != .idle else { From d1c8174c1ac4b47e957ebbb7c383c0232900c6d2 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 11:43:14 +0100 Subject: [PATCH 2/3] APP-2737 dont require closure on removeAllEventSources --- Projects/SSEKit/SSEKit/SSEManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 833accb..bfe7918 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -217,7 +217,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// remove all the listening event sources /// /// - Parameter completion: completion closure - open func removeAllEventSources(_ completion:@escaping ()->()) { + open func removeAllEventSources(_ completion:@escaping ()->() = {}) { self.queue.async { From 7573c577eba7e626e2118ff1432472513ca14260 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 14:40:28 +0100 Subject: [PATCH 3/3] APP-2737 fix issues with error states and add test cases --- Projects/SSEKit/SSEKit/SSEManager.swift | 14 +++-- Projects/SSEKit/SSEKitTests/SSEKitTests.swift | 52 ++++++++++++++++++- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index bfe7918..43743d8 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -119,7 +119,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// - Parameter completion: completion closure open func disconnect(completion:@escaping ()->() = {}) { - _ = self.queue.async { + self.queue.async { guard self.connectionState != .disconnecting && self.connectionState != .idle else { completion() return @@ -142,10 +142,10 @@ open class SSEManager : NSObject, URLSessionDelegate { self.eventSources.forEach({ (eventSource) in NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) }) + + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) } - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) - completion() } } @@ -251,13 +251,17 @@ extension SSEManager: URLSessionDataDelegate { return } - self.connectionState = .idle // session ended... self.connectionRetries = self.connectionRetries + 1 if (self.connectionRetries < self.maxConnectionRetries) { - self.connect(toURL: url) + self.connectionState = .idle + self.connect(toURL: url, completion: self.connectCompletionClosure) } else { + DispatchQueue.main.sync { + self.connectCompletionClosure?(error as NSError?) + self.connectCompletionClosure = nil + } self.disconnect() { } } diff --git a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift index 523e1ac..f6b24de 100644 --- a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift +++ b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift @@ -76,7 +76,7 @@ class SSEKitTests: XCTestCase { disconnectEventExpectation.fulfill() } - self.waitForExpectations(timeout: 50) { (error) in + self.waitForExpectations(timeout: 15) { (error) in XCTAssertNil(error) NotificationCenter.default.removeObserver(observer) @@ -127,6 +127,56 @@ class SSEKitTests: XCTestCase { NotificationCenter.default.removeObserver(cheeseObserver) } } + + func testConnectNOK() { + let connectResponseExpectation = self.expectation(description: "should call connect closure") + + + let manager = SSEManager() + + manager.maxConnectionRetries = 2 // or test takes too long! + + // connect to an invalid ip (get a request timed out) + manager.connect(toURL: URL(string: "http://999.999.999.999:15081/notify")!, completion: { (error) in + XCTAssert(error != nil) + XCTAssert(Thread.isMainThread, "closure should be on main thread") + connectResponseExpectation.fulfill() + }) + + self.waitForExpectations(timeout: 15) { (error) in + XCTAssertNil(error) + } + } + + + func testConnectOKThenFail() { + let connectResponseExpectation = self.expectation(description: "should call connect closure") + let disconnectEventExpectation = self.expectation(description: "should disconnect") + + let manager = SSEManager() + + manager.maxConnectionRetries = 0 // stop retries + + manager.connect(toURL: DUT_SSE_Endpoint, completion: { (error) in + XCTAssert(error == nil) + XCTAssert(Thread.isMainThread, "closure should be on main thread") + connectResponseExpectation.fulfill() + + // force an error + manager.urlSession(manager.session!, task: manager.sessionTask!, didCompleteWithError: NSError(domain: "com.naim.ssekittest", code: 1, userInfo: [:]) as Error) + }) + + let disconnectObserver = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Disconnected.rawValue), object:manager, queue: nil) { (notification) in + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + disconnectEventExpectation.fulfill() + } + + self.waitForExpectations(timeout: 15) { (error) in + XCTAssertNil(error) + XCTAssert(manager.connectionState == SSEManager.ConnectionState.idle, "should be idle") + NotificationCenter.default.removeObserver(disconnectObserver) + } + } func testPerformanceExample() { // This is an example of a performance test case.