From a15a14a378ba08da1a8179c7a5106d06a861ad6d Mon Sep 17 00:00:00 2001 From: jguz-pubnub Date: Fri, 1 Dec 2023 14:01:21 +0100 Subject: [PATCH] Work in progress --- .../PubNub/EventEngine/Core/Dispatcher.swift | 18 ++-- .../EventEngine/Core/EffectHandler.swift | 10 +-- .../PubNub/EventEngine/Core/EventEngine.swift | 12 +-- .../EventEngine/Core/EventEngineFactory.swift | 21 +++-- .../Effects/PresenceEffectFactory.swift | 22 +++-- .../EventEngine/Presence/Presence.swift | 2 +- .../Effects/SubscribeEffectFactory.swift | 22 +++-- .../EventEngine/Subscribe/Subscribe.swift | 2 +- Sources/PubNub/PubNub.swift | 56 ++++++++----- ...entEngineSubscriptionSessionStrategy.swift | 26 +++--- .../LegacySubscriptionSessionStrategy.swift | 2 +- .../SubscribeSessionFactory.swift | 83 ++++++++++--------- .../PubNubEventEngineContractTestSteps.swift | 10 --- ...ubNubPresenceEngineContractTestSteps.swift | 23 ++--- ...NubSubscribeEngineContractTestsSteps.swift | 34 ++++---- .../Steps/PubNubEventEngineTestsHelpers.swift | 4 +- .../EventEngine/DispatcherTests.swift | 18 ++-- .../DelayedHeartbeatEffectTests.swift | 2 +- .../Presence/HeartbeatEffectTests.swift | 4 +- .../Presence/LeaveEffectTests.swift | 4 +- .../Presence/WaitEffectTests.swift | 6 +- .../Subscribe/SubscribeEffectsTests.swift | 52 ++++++------ 22 files changed, 226 insertions(+), 207 deletions(-) diff --git a/Sources/PubNub/EventEngine/Core/Dispatcher.swift b/Sources/PubNub/EventEngine/Core/Dispatcher.swift index caf33c56..224f09db 100644 --- a/Sources/PubNub/EventEngine/Core/Dispatcher.swift +++ b/Sources/PubNub/EventEngine/Core/Dispatcher.swift @@ -35,25 +35,25 @@ struct DispatcherListener { // MARK: - Dispatcher -protocol Dispatcher { +protocol Dispatcher { associatedtype Invocation: AnyEffectInvocation associatedtype Event - associatedtype Input + associatedtype Dependencies func dispatch( invocations: [EffectInvocation], - with customInput: EventEngineCustomInput, + with dependencies: EventEngineDependencies, notify listener: DispatcherListener ) } // MARK: - EffectDispatcher -class EffectDispatcher: Dispatcher { - private let factory: any EffectHandlerFactory +class EffectDispatcher: Dispatcher { + private let factory: any EffectHandlerFactory private let effectsCache = EffectsCache() - init(factory: some EffectHandlerFactory) { + init(factory: some EffectHandlerFactory) { self.factory = factory } @@ -63,20 +63,20 @@ class EffectDispatcher: Dispatche func dispatch( invocations: [EffectInvocation], - with customInput: EventEngineCustomInput, + with dependencies: EventEngineDependencies, notify listener: DispatcherListener ) { invocations.forEach { switch $0 { case .managed(let invocation): executeEffect( - effect: factory.effect(for: invocation, with: customInput), + effect: factory.effect(for: invocation, with: dependencies), storageId: invocation.id, notify: listener ) case .regular(let invocation): executeEffect( - effect: factory.effect(for: invocation, with: customInput), + effect: factory.effect(for: invocation, with: dependencies), storageId: UUID().uuidString, notify: listener ) diff --git a/Sources/PubNub/EventEngine/Core/EffectHandler.swift b/Sources/PubNub/EventEngine/Core/EffectHandler.swift index a3c0f257..01eab89f 100644 --- a/Sources/PubNub/EventEngine/Core/EffectHandler.swift +++ b/Sources/PubNub/EventEngine/Core/EffectHandler.swift @@ -29,14 +29,14 @@ import Foundation // MARK: - EffectHandlerFactory -protocol EffectHandlerFactory { - associatedtype EffectInvocation +protocol EffectHandlerFactory { + associatedtype Invocation associatedtype Event - associatedtype Input + associatedtype Dependencies func effect( - for invocation: EffectInvocation, - with customInput: EventEngineCustomInput + for invocation: Invocation, + with dependencies: EventEngineDependencies ) -> any EffectHandler } diff --git a/Sources/PubNub/EventEngine/Core/EventEngine.swift b/Sources/PubNub/EventEngine/Core/EventEngine.swift index 75cc4049..8aafb613 100644 --- a/Sources/PubNub/EventEngine/Core/EventEngine.swift +++ b/Sources/PubNub/EventEngine/Core/EventEngine.swift @@ -27,8 +27,8 @@ import Foundation -struct EventEngineCustomInput { - let value: Value +struct EventEngineDependencies { + let value: Dependencies } class EventEngine { @@ -36,7 +36,7 @@ class EventEngine { private let dispatcher: any Dispatcher private(set) var state: State - var customInput: EventEngineCustomInput + var dependencies: EventEngineDependencies var onStateUpdated: ((State) -> Void)? init( @@ -44,13 +44,13 @@ class EventEngine { transition: some TransitionProtocol, onStateUpdated: ((State) -> Void)? = nil, dispatcher: some Dispatcher, - customInput: EventEngineCustomInput + dependencies: EventEngineDependencies ) { self.state = state self.onStateUpdated = onStateUpdated self.transition = transition self.dispatcher = dispatcher - self.customInput = customInput + self.dependencies = dependencies } func send(event: Event) { @@ -81,7 +81,7 @@ class EventEngine { ) dispatcher.dispatch( invocations: invocations, - with: customInput, + with: dependencies, notify: listener ) } diff --git a/Sources/PubNub/EventEngine/Core/EventEngineFactory.swift b/Sources/PubNub/EventEngine/Core/EventEngineFactory.swift index 210f14b9..6806e0c4 100644 --- a/Sources/PubNub/EventEngine/Core/EventEngineFactory.swift +++ b/Sources/PubNub/EventEngine/Core/EventEngineFactory.swift @@ -27,33 +27,38 @@ import Foundation -typealias SubscribeEngine = EventEngine<(any SubscribeState), Subscribe.Event, Subscribe.Invocation, Subscribe.EngineInput> -typealias PresenceEngine = EventEngine<(any PresenceState), Presence.Event, Presence.Invocation, Presence.EngineInput> +typealias SubscribeEngine = EventEngine<(any SubscribeState), Subscribe.Event, Subscribe.Invocation, Subscribe.Dependencies> +typealias PresenceEngine = EventEngine<(any PresenceState), Presence.Event, Presence.Invocation, Presence.Dependencies> + +typealias SubscribeTransitions = TransitionProtocol<(any SubscribeState), Subscribe.Event, Subscribe.Invocation> +typealias PresenceTransitions = TransitionProtocol<(any PresenceState), Presence.Event, Presence.Invocation> +typealias SubscribeDispatcher = Dispatcher +typealias PresenceDispatcher = Dispatcher class EventEngineFactory { func subscribeEngine( with configuration: PubNubConfiguration, - dispatcher: some Dispatcher, - transition: some TransitionProtocol + dispatcher: some SubscribeDispatcher = EffectDispatcher(factory: SubscribeEffectFactory.defaultFactory()), + transition: some SubscribeTransitions = SubscribeTransition() ) -> SubscribeEngine { EventEngine( state: Subscribe.UnsubscribedState(), transition: transition, dispatcher: dispatcher, - customInput: EventEngineCustomInput(value: Subscribe.EngineInput(configuration: configuration)) + dependencies: EventEngineDependencies(value: Subscribe.Dependencies(configuration: configuration)) ) } func presenceEngine( with configuration: PubNubConfiguration, - dispatcher: some Dispatcher, - transition: some TransitionProtocol + dispatcher: some PresenceDispatcher = EffectDispatcher(factory: PresenceEffectFactory.defaultFactory()), + transition: some PresenceTransitions ) -> PresenceEngine { EventEngine( state: Presence.HeartbeatInactive(), transition: transition, dispatcher: dispatcher, - customInput: EventEngineCustomInput(value: Presence.EngineInput(configuration: configuration)) + dependencies: EventEngineDependencies(value: Presence.Dependencies(configuration: configuration)) ) } } diff --git a/Sources/PubNub/EventEngine/Presence/Effects/PresenceEffectFactory.swift b/Sources/PubNub/EventEngine/Presence/Effects/PresenceEffectFactory.swift index 63bd1b0e..ab59a9f3 100644 --- a/Sources/PubNub/EventEngine/Presence/Effects/PresenceEffectFactory.swift +++ b/Sources/PubNub/EventEngine/Presence/Effects/PresenceEffectFactory.swift @@ -36,9 +36,19 @@ class PresenceEffectFactory: EffectHandlerFactory { self.sessionResponseQueue = sessionResponseQueue } + static func defaultFactory() -> PresenceEffectFactory { + PresenceEffectFactory( + session: HTTPSession( + configuration: .pubnub, + sessionQueue: DispatchQueue(label: "Presence Response Queue"), + sessionStream: SessionListener() + ) + ) + } + func effect( for invocation: Presence.Invocation, - with customInput: EventEngineCustomInput + with dependencies: EventEngineDependencies ) -> any EffectHandler { switch invocation { case .heartbeat(let channels, let groups): @@ -46,7 +56,7 @@ class PresenceEffectFactory: EffectHandlerFactory { request: PresenceHeartbeatRequest( channels: channels, groups: groups, - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, session: session, sessionResponseQueue: sessionResponseQueue ) @@ -56,26 +66,26 @@ class PresenceEffectFactory: EffectHandlerFactory { request: PresenceHeartbeatRequest( channels: channels, groups: groups, - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, session: session, sessionResponseQueue: sessionResponseQueue ), retryAttempt: retryAttempt, reason: reason, - configuration: customInput.value.configuration + configuration: dependencies.value.configuration ) case .leave(let channels, let groups): return LeaveEffect( request: PresenceLeaveRequest( channels: channels, groups: groups, - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, session: session, sessionResponseQueue: sessionResponseQueue ) ) case .wait: - return WaitEffect(configuration: customInput.value.configuration) + return WaitEffect(configuration: dependencies.value.configuration) } } } diff --git a/Sources/PubNub/EventEngine/Presence/Presence.swift b/Sources/PubNub/EventEngine/Presence/Presence.swift index 4ba07fbe..ce8bd915 100644 --- a/Sources/PubNub/EventEngine/Presence/Presence.swift +++ b/Sources/PubNub/EventEngine/Presence/Presence.swift @@ -95,7 +95,7 @@ extension Presence { } extension Presence { - struct EngineInput { + struct Dependencies { let configuration: PubNubConfiguration } } diff --git a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift index 652861a8..6626ff16 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Effects/SubscribeEffectFactory.swift @@ -42,15 +42,23 @@ class SubscribeEffectFactory: EffectHandlerFactory { self.messageCache = messageCache } + static func defaultFactory() -> SubscribeEffectFactory { + SubscribeEffectFactory(session: HTTPSession( + configuration: URLSessionConfiguration.subscription, + sessionQueue: DispatchQueue(label: "Subscribe Response Queue"), + sessionStream: SessionListener() + )) + } + func effect( for invocation: Subscribe.Invocation, - with customInput: EventEngineCustomInput + with dependencies: EventEngineDependencies ) -> any EffectHandler { switch invocation { case .handshakeRequest(let channels, let groups): return HandshakeEffect( request: SubscribeRequest( - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, channels: channels, groups: groups, timetoken: 0, @@ -61,7 +69,7 @@ class SubscribeEffectFactory: EffectHandlerFactory { case .handshakeReconnect(let channels, let groups, let retryAttempt, let reason): return HandshakeReconnectEffect( request: SubscribeRequest( - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, channels: channels, groups: groups, timetoken: 0, @@ -74,7 +82,7 @@ class SubscribeEffectFactory: EffectHandlerFactory { case .receiveMessages(let channels, let groups, let cursor): return ReceivingEffect( request: SubscribeRequest( - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, channels: channels, groups: groups, timetoken: cursor.timetoken, @@ -86,7 +94,7 @@ class SubscribeEffectFactory: EffectHandlerFactory { case .receiveReconnect(let channels, let groups, let cursor, let retryAttempt, let reason): return ReceiveReconnectEffect( request: SubscribeRequest( - configuration: customInput.value.configuration, + configuration: dependencies.value.configuration, channels: channels, groups: groups, timetoken: cursor.timetoken, @@ -101,13 +109,13 @@ class SubscribeEffectFactory: EffectHandlerFactory { return EmitMessagesEffect( messages: messages, cursor: cursor, - listeners: customInput.value.listeners, + listeners: dependencies.value.listeners, messageCache: messageCache ) case .emitStatus(let statusChange): return EmitStatusEffect( statusChange: statusChange, - listeners: customInput.value.listeners + listeners: dependencies.value.listeners ) } } diff --git a/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift b/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift index 63c3805c..360341d3 100644 --- a/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift +++ b/Sources/PubNub/EventEngine/Subscribe/Subscribe.swift @@ -141,7 +141,7 @@ extension Subscribe { } extension Subscribe { - struct EngineInput { + struct Dependencies { let configuration: PubNubConfiguration let listeners: [BaseSubscriptionListener] diff --git a/Sources/PubNub/PubNub.swift b/Sources/PubNub/PubNub.swift index 46330466..5eaf5c74 100644 --- a/Sources/PubNub/PubNub.swift +++ b/Sources/PubNub/PubNub.swift @@ -57,16 +57,15 @@ public class PubNub { /// - configuration: The default configurations that will be used /// - session: Session used for performing request/response REST calls /// - subscribeSession: The network session used for Subscription only - public init( + /// - fileSession: The network session used for File uploading/downloading only + public convenience init( configuration: PubNubConfiguration, session: SessionReplaceable? = nil, subscribeSession: SessionReplaceable? = nil, - fileSession: URLSessionReplaceable? = nil, - subscriptionSession: SubscriptionSession? = nil + fileSession: URLSessionReplaceable? = nil ) { - instanceID = UUID() - self.configuration = configuration - + let instanceID = UUID() + // Default operators based on config var operators = [RequestOperator]() if let retryOperator = configuration.automaticRetry { @@ -88,26 +87,41 @@ public class PubNub { .defaultRequestOperator? .merge(requestOperator: MultiplexRequestOperator(operators: operators)) } - - // Immutable session - self.networkSession = networkSession - + + let fileSession = fileSession ?? URLSession( + configuration: .pubnubBackground, + delegate: FileSessionManager(), + delegateQueue: .main + ) + // Set initial session also based on configuration - subscription = subscriptionSession ?? SubscribeSessionFactory.shared.getSession( + let subscriptionSession = SubscribeSessionFactory.shared.getSession( from: configuration, with: subscribeSession, presenceSession: session ) - - if let fileSession = fileSession { - fileURLSession = fileSession - } else { - fileURLSession = URLSession( - configuration: .pubnubBackground, - delegate: FileSessionManager(), - delegateQueue: .main - ) - } + + self.init( + instanceID: instanceID, + configuration: configuration, + session: networkSession, + fileSession: fileSession, + subscriptionSession: subscriptionSession + ) + } + + init( + instanceID: UUID = UUID(), + configuration: PubNubConfiguration, + session: SessionReplaceable, + fileSession: URLSessionReplaceable, + subscriptionSession: SubscriptionSession + ) { + self.instanceID = instanceID + self.configuration = configuration + self.subscription = subscriptionSession + self.networkSession = session + self.fileURLSession = fileSession } func route( diff --git a/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift b/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift index 119b6d59..8400c530 100644 --- a/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift +++ b/Sources/PubNub/Subscription/Strategy/EventEngineSubscriptionSessionStrategy.swift @@ -29,11 +29,11 @@ import Foundation class EventEngineSubscriptionSessionStrategy: SubscriptionSessionStrategy { let uuid = UUID() - + let subscribeEngine: SubscribeEngine + let presenceEngine: PresenceEngine + var privateListeners: WeakSet = WeakSet([]) var configuration: PubNubConfiguration - var subscribeEngine: SubscribeEngine - var presenceEngine: PresenceEngine var previousTokenResponse: SubscribeCursor? internal init( @@ -77,9 +77,9 @@ class EventEngineSubscriptionSessionStrategy: SubscriptionSessionStrategy { } } - private func updateSubscribeEngineInput() { - subscribeEngine.customInput = EventEngineCustomInput( - value: Subscribe.EngineInput( + private func updateSubscribeEngineDependencies() { + subscribeEngine.dependencies = EventEngineDependencies( + value: Subscribe.Dependencies( configuration: configuration, listeners: privateListeners.allObjects ) @@ -87,20 +87,20 @@ class EventEngineSubscriptionSessionStrategy: SubscriptionSessionStrategy { } private func sendSubscribeEvent(event: Subscribe.Event) { - updateSubscribeEngineInput() + updateSubscribeEngineDependencies() subscribeEngine.send(event: event) } - private func updatePresenceEngineInput() { - presenceEngine.customInput = EventEngineCustomInput( - value: Presence.EngineInput( + private func updatePresenceEngineDependencies() { + presenceEngine.dependencies = EventEngineDependencies( + value: Presence.Dependencies( configuration: configuration ) ) } private func sendPresenceEvent(event: Presence.Event) { - updatePresenceEngineInput() + updatePresenceEngineDependencies() presenceEngine.send(event: event) } @@ -188,11 +188,11 @@ extension EventEngineSubscriptionSessionStrategy: EventStreamEmitter { listener.token = ListenerToken { [weak self, weak listener] in if let listener = listener { self?.privateListeners.remove(listener) - self?.updateSubscribeEngineInput() + self?.updateSubscribeEngineDependencies() } } privateListeners.update(listener) - updateSubscribeEngineInput() + updateSubscribeEngineDependencies() } func notify(listeners closure: (ListenerType) -> Void) { diff --git a/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift b/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift index 0a57e5d2..32bee48c 100644 --- a/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift +++ b/Sources/PubNub/Subscription/Strategy/LegacySubscriptionSessionStrategy.swift @@ -136,7 +136,7 @@ class LegacySubscriptionSessionStrategy: SubscriptionSessionStrategy { } if subscribeChange.didChange { - //notify { $0.emit(subscribe: .subscriptionChanged(subscribeChange)) } + // notify { $0.emit(subscribe: .subscriptionChanged(subscribeChange)) } } if subscribeChange.didChange || !connectionStatus.isActive { diff --git a/Sources/PubNub/Subscription/SubscribeSessionFactory.swift b/Sources/PubNub/Subscription/SubscribeSessionFactory.swift index c33572ba..0b4546b3 100644 --- a/Sources/PubNub/Subscription/SubscribeSessionFactory.swift +++ b/Sources/PubNub/Subscription/SubscribeSessionFactory.swift @@ -63,15 +63,40 @@ public class SubscribeSessionFactory { with subscribeSession: SessionReplaceable? = nil, presenceSession: SessionReplaceable? = nil ) -> SubscriptionSession { + guard let config = config as? PubNubConfiguration else { + preconditionFailure("Unexpected configuration that doesn't match PubNubConfiguration") + } + // The hash value for the given configuration let configHash = config.subscriptionHashValue + // Returns a session (if any) that matches the hash value if let session = sessions.lockedRead({ $0[configHash]?.underlying }) { PubNub.log.debug("Found existing session for config hash \(config.subscriptionHashValue)") return session } - + PubNub.log.debug("Creating new session for with hash value \(config.subscriptionHashValue)") return sessions.lockedWrite { dictionary in + let subscriptionSession = SubscriptionSession( + strategy: resolveStrategy( + configuration: config, + subscribeSession: subscribeSession, + presenceSession: presenceSession + ) + ) + dictionary.updateValue( + WeakBox(subscriptionSession), + forKey: configHash + ) + return subscriptionSession + } + + func resolveStrategy( + configuration: PubNubConfiguration, + subscribeSession: SessionReplaceable?, + presenceSession: SessionReplaceable? + ) -> any SubscriptionSessionStrategy { + // Creates default network session objects if they're not provided let finalSubscribeSession = subscribeSession ?? HTTPSession( configuration: URLSessionConfiguration.subscription, sessionQueue: subscribeQueue, @@ -83,52 +108,28 @@ public class SubscribeSessionFactory { sessionStream: SessionListener() ) - guard let config = config as? PubNubConfiguration else { - preconditionFailure("Unexpected configuration that doesn't match PubNubConfiguration") - } - guard config.enableEventEngine else { - let subscriptionSession = SubscriptionSession( - strategy: LegacySubscriptionSessionStrategy( - configuration: config, - network: finalSubscribeSession, - presenceSession: finalPresenceSession - ) + if configuration.enableEventEngine { + let subscribeEngine = EventEngineFactory().subscribeEngine( + with: configuration, + dispatcher: EffectDispatcher(factory: SubscribeEffectFactory(session: finalSubscribeSession)), + transition: SubscribeTransition() ) - dictionary.updateValue( - WeakBox(subscriptionSession), - forKey: configHash + let presenceEngine = EventEngineFactory().presenceEngine( + with: configuration, + dispatcher: EffectDispatcher(factory: PresenceEffectFactory(session: finalPresenceSession)), + transition: PresenceTransition(configuration: configuration) ) - return subscriptionSession - } - let subscribeDispatcher = EffectDispatcher( - factory: SubscribeEffectFactory(session: finalSubscribeSession) - ) - let subscribeEngine = EventEngineFactory().subscribeEngine( - with: config, - dispatcher: subscribeDispatcher, - transition: SubscribeTransition() - ) - let presenceDispatcher = EffectDispatcher( - factory: PresenceEffectFactory(session: finalPresenceSession) - ) - let presenceEngine = EventEngineFactory().presenceEngine( - with: config, - dispatcher: presenceDispatcher, - transition: PresenceTransition(configuration: config) - ) - let subscriptionSession = SubscriptionSession( - strategy: EventEngineSubscriptionSessionStrategy( - configuration: config, + return EventEngineSubscriptionSessionStrategy( + configuration: configuration, subscribeEngine: subscribeEngine, presenceEngine: presenceEngine ) + } + return LegacySubscriptionSessionStrategy( + configuration: configuration, + network: finalSubscribeSession, + presenceSession: finalPresenceSession ) - - dictionary.updateValue( - WeakBox(subscriptionSession), - forKey: configHash - ) - return subscriptionSession } } diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift index 4b97e8d0..f83e36cf 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubEventEngineContractTestSteps.swift @@ -40,13 +40,3 @@ class PubNubEventEngineContractTestsSteps: PubNubContractTestCase { return (events: events, invocations: invocations) } } - -struct EmptyDispatcher: Dispatcher { - func dispatch( - invocations: [EffectInvocation], - with customInput: EventEngineCustomInput, - notify listener: DispatcherListener - ) { - PubNub.log.warn("Ignoring any Invocations for EmptyDispatcher") - } -} diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift index 18f9860c..2d6cde0c 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubPresenceEngineContractTestSteps.swift @@ -83,7 +83,7 @@ extension Presence.Event: ContractTestIdentifiable { class PubNubPresenceEngineContractTestsSteps: PubNubEventEngineContractTestsSteps { // A decorator that records Invocations and forwards all calls to the original instance - private var dispatcherDecorator: DispatcherDecorator! + private var dispatcherDecorator: DispatcherDecorator! // A decorator that records Events and forwards all calls to the original instance private var transitionDecorator: TransitionDecorator! @@ -95,13 +95,7 @@ class PubNubPresenceEngineContractTestsSteps: PubNubEventEngineContractTestsStep override func createPubNubClient() -> PubNub { dispatcherDecorator = DispatcherDecorator(wrappedInstance: EffectDispatcher( - factory: PresenceEffectFactory( - session: HTTPSession( - configuration: .pubnub, - sessionQueue: DispatchQueue(label: "Subscribe Response Queue"), - sessionStream: SessionListener() - ) - ) + factory: PresenceEffectFactory.defaultFactory() )) transitionDecorator = TransitionDecorator( wrappedInstance: PresenceTransition(configuration: configuration) @@ -109,18 +103,11 @@ class PubNubPresenceEngineContractTestsSteps: PubNubEventEngineContractTestsStep let configuration = self.configuration let factory = EventEngineFactory() + let subscriptionSession = SubscriptionSession( strategy: EventEngineSubscriptionSessionStrategy( configuration: configuration, - subscribeEngine: factory.subscribeEngine( - with: configuration, - dispatcher: EffectDispatcher(factory: SubscribeEffectFactory(session: HTTPSession( - configuration: URLSessionConfiguration.subscription, - sessionQueue: DispatchQueue(label: "Subscribe Response Queue"), - sessionStream: SessionListener() - ))), - transition: SubscribeTransition() - ), + subscribeEngine: factory.subscribeEngine(with: configuration), presenceEngine: factory.presenceEngine( with: configuration, dispatcher: self.dispatcherDecorator, @@ -130,6 +117,8 @@ class PubNubPresenceEngineContractTestsSteps: PubNubEventEngineContractTestsStep ) return PubNub( configuration: self.configuration, + session: HTTPSession(session: URLSession.shared, delegate: HTTPSessionDelegate(), sessionQueue: .global(qos: .default)), + fileSession: URLSession(configuration: .pubnubBackground), subscriptionSession: subscriptionSession ) } diff --git a/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift b/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift index e6659bb8..a623076c 100644 --- a/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift +++ b/Tests/PubNubContractTest/Steps/EventEngine/PubNubSubscribeEngineContractTestsSteps.swift @@ -103,7 +103,7 @@ extension Subscribe.Event: ContractTestIdentifiable { class PubNubSubscribeEngineContractTestsSteps: PubNubEventEngineContractTestsSteps { // A decorator that records Invocations and forwards all calls to the original instance - private var dispatcherDecorator: DispatcherDecorator! + private var dispatcherDecorator: DispatcherDecorator! // A decorator that records Events and forwards all calls to the original instance private var transitionDecorator: TransitionDecorator! @@ -113,20 +113,6 @@ class PubNubSubscribeEngineContractTestsSteps: PubNubEventEngineContractTestsSte super.handleAfterHook() } - override func handleBeforeHook() { - dispatcherDecorator = DispatcherDecorator(wrappedInstance: EffectDispatcher( - factory: SubscribeEffectFactory(session: HTTPSession( - configuration: URLSessionConfiguration.subscription, - sessionQueue: DispatchQueue(label: "Subscribe Response Queue"), - sessionStream: SessionListener() - )) - )) - transitionDecorator = TransitionDecorator( - wrappedInstance: SubscribeTransition() - ) - super.handleBeforeHook() - } - override var expectSubscribeFailure: Bool { [ "Successfully restore subscribe with failures", @@ -137,24 +123,34 @@ class PubNubSubscribeEngineContractTestsSteps: PubNubEventEngineContractTestsSte } override func createPubNubClient() -> PubNub { + dispatcherDecorator = DispatcherDecorator(wrappedInstance: EffectDispatcher( + factory: SubscribeEffectFactory.defaultFactory() + )) + transitionDecorator = TransitionDecorator( + wrappedInstance: SubscribeTransition() + ) + let factory = EventEngineFactory() + let configuration = self.configuration + let subscriptionSession = SubscriptionSession( strategy: EventEngineSubscriptionSessionStrategy( - configuration: self.configuration, + configuration: configuration, subscribeEngine: factory.subscribeEngine( - with: self.configuration, + with: configuration, dispatcher: self.dispatcherDecorator, transition: self.transitionDecorator ), presenceEngine: factory.presenceEngine( - with: self.configuration, - dispatcher: EmptyDispatcher(), + with: configuration, transition: PresenceTransition(configuration: configuration) ) ) ) return PubNub( configuration: self.configuration, + session: HTTPSession(session: URLSession.shared, delegate: HTTPSessionDelegate(), sessionQueue: .global(qos: .default)), + fileSession: URLSession(configuration: .pubnubBackground), subscriptionSession: subscriptionSession ) } diff --git a/Tests/PubNubContractTest/Steps/PubNubEventEngineTestsHelpers.swift b/Tests/PubNubContractTest/Steps/PubNubEventEngineTestsHelpers.swift index 65b4fc60..ad82f9a4 100644 --- a/Tests/PubNubContractTest/Steps/PubNubEventEngineTestsHelpers.swift +++ b/Tests/PubNubContractTest/Steps/PubNubEventEngineTestsHelpers.swift @@ -57,11 +57,11 @@ class DispatcherDecorator: Dispat func dispatch( invocations: [EffectInvocation], - with customInput: EventEngineCustomInput, + with dependencies: EventEngineDependencies, notify listener: DispatcherListener ) { recordedInvocations += invocations - wrappedInstance.dispatch(invocations: invocations, with: customInput, notify: listener) + wrappedInstance.dispatch(invocations: invocations, with: dependencies, notify: listener) } } diff --git a/Tests/PubNubTests/EventEngine/DispatcherTests.swift b/Tests/PubNubTests/EventEngine/DispatcherTests.swift index 5a524232..369401d5 100644 --- a/Tests/PubNubTests/EventEngine/DispatcherTests.swift +++ b/Tests/PubNubTests/EventEngine/DispatcherTests.swift @@ -48,7 +48,7 @@ class DispatcherTests: XCTestCase { .managed(.third), .managed(.fourth) ], - with: EventEngineCustomInput(value: Void()), + with: EventEngineDependencies(value: Void()), notify: listener ) @@ -72,7 +72,7 @@ class DispatcherTests: XCTestCase { .cancel(.thirdCancellable), .managed(.fourth) ], - with: EventEngineCustomInput(value: Void()), + with: EventEngineDependencies(value: Void()), notify: listener ) @@ -87,7 +87,7 @@ class DispatcherTests: XCTestCase { dispatcher.dispatch( invocations: [.managed(.first)], - with: EventEngineCustomInput(value: Void()), + with: EventEngineDependencies(value: Void()), notify: listener ) } @@ -109,7 +109,7 @@ class DispatcherTests: XCTestCase { .cancel(.thirdCancellable), .managed(.fourth) ], - with: EventEngineCustomInput(value: Void()), + with: EventEngineDependencies(value: Void()), notify: listener ) @@ -160,7 +160,10 @@ fileprivate enum TestInvocation: String, AnyEffectInvocation { } fileprivate struct MockEffectHandlerFactory: EffectHandlerFactory { - func effect(for invocation: TestInvocation, with customInput: EventEngineCustomInput) -> any EffectHandler { + func effect( + for invocation: TestInvocation, + with dependencies: EventEngineDependencies + ) -> any EffectHandler { MockEffectHandler() } } @@ -175,7 +178,10 @@ fileprivate struct MockEffectHandler: EffectHandler { } fileprivate class StubEffectHandlerFactory: EffectHandlerFactory { - func effect(for invocation: TestInvocation, with customInput: EventEngineCustomInput) -> any EffectHandler { + func effect( + for invocation: TestInvocation, + with dependencies: EventEngineDependencies + ) -> any EffectHandler { StubEffectHandler() } } diff --git a/Tests/PubNubTests/EventEngine/Presence/DelayedHeartbeatEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/DelayedHeartbeatEffectTests.swift index 1c5a5c1c..ba556fc4 100644 --- a/Tests/PubNubTests/EventEngine/Presence/DelayedHeartbeatEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/DelayedHeartbeatEffectTests.swift @@ -162,7 +162,7 @@ fileprivate extension DelayedHeartbeatEffectTests { channels: ["channel-1", "channel-2"], groups: ["group-1", "group-2"], retryAttempt: attempt, error: error ), - with: EventEngineCustomInput(value: Presence.EngineInput( + with: EventEngineDependencies(value: Presence.Dependencies( configuration: PubNubConfiguration( publishKey: "pubKey", subscribeKey: "subKey", diff --git a/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift index 3db7f84b..323998cf 100644 --- a/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/HeartbeatEffectTests.swift @@ -68,7 +68,7 @@ class HeartbeatEffectTests: XCTestCase { let effect = factory.effect( for: .heartbeat(channels: ["channel-1", "channel-2"], groups: ["group-1", "group-2"]), - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in XCTAssertTrue(returnedEvents.elementsEqual([.heartbeatSuccess])) @@ -86,7 +86,7 @@ class HeartbeatEffectTests: XCTestCase { let effect = factory.effect( for: .heartbeat(channels: ["channel-1", "channel-2"], groups: ["group-1", "group-2"]), - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in XCTAssertTrue(returnedEvents.elementsEqual([.heartbeatFailed(error: PubNubError(.internalServiceError))])) diff --git a/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift index a914467f..a9a5b7c0 100644 --- a/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/LeaveEffectTests.swift @@ -67,7 +67,7 @@ class LeaveEffectTests: XCTestCase { ) let effect = factory.effect( for: .leave(channels: ["c1", "c2"], groups: ["g1", "g2"]), - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in XCTAssertTrue(returnedEvents.isEmpty) @@ -91,7 +91,7 @@ class LeaveEffectTests: XCTestCase { ) let effect = factory.effect( for: .leave(channels: ["c1", "c2"], groups: ["g1", "g2"]), - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in XCTAssertTrue(returnedEvents.isEmpty) diff --git a/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift b/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift index 3bbfc966..d9907e33 100644 --- a/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift +++ b/Tests/PubNubTests/EventEngine/Presence/WaitEffectTests.swift @@ -67,7 +67,7 @@ class WaitEffectTests: XCTestCase { let effect = factory.effect( for: .wait, - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) let startDate = Date() @@ -93,7 +93,7 @@ class WaitEffectTests: XCTestCase { ) let effect = factory.effect( for: .wait, - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in expectation.fulfill() @@ -116,7 +116,7 @@ class WaitEffectTests: XCTestCase { ) let effect = factory.effect( for: .wait, - with: EventEngineCustomInput(value: Presence.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Presence.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in XCTAssertTrue(returnedEvents.isEmpty) diff --git a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift index 7409d353..0f18170c 100644 --- a/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift +++ b/Tests/PubNubTests/EventEngine/Subscribe/SubscribeEffectsTests.swift @@ -86,7 +86,7 @@ class SubscribeEffectsTests: XCTestCase { channels: ["channel1", "channel1-pnpres", "channel2"], groups: ["g1", "g2", "g2-pnpres"] ), - with: EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + with: EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in let expectedCursor = SubscribeCursor(timetoken: 12345, region: 1) @@ -112,8 +112,8 @@ class SubscribeEffectsTests: XCTestCase { channels: ["channel1", "channel1-pnpres", "channel2"], groups: ["g1", "g2", "g2-pnpres"] ), - with: EventEngineCustomInput( - value: Subscribe.EngineInput(configuration: config) + with: EventEngineDependencies( + value: Subscribe.Dependencies(configuration: config) ) ) effect.performTask { returnedEvents in @@ -140,8 +140,8 @@ class SubscribeEffectsTests: XCTestCase { channels: ["channel1", "channel1-pnpres", "channel2"], groups: ["g1", "g2", "g2-pnpres"], cursor: SubscribeCursor(timetoken: 111, region: 1) - ), with: EventEngineCustomInput( - value: Subscribe.EngineInput(configuration: config) + ), with: EventEngineDependencies( + value: Subscribe.Dependencies(configuration: config) ) ) effect.performTask { returnedEvents in @@ -168,8 +168,8 @@ class SubscribeEffectsTests: XCTestCase { channels: ["channel1", "channel1-pnpres", "channel2"], groups: ["g1", "g2", "g2-pnpres"], cursor: SubscribeCursor(timetoken: 111, region: 1) - ), with: EventEngineCustomInput( - value: Subscribe.EngineInput(configuration: config) + ), with: EventEngineDependencies( + value: Subscribe.Dependencies(configuration: config) ) ) effect.performTask { returnedEvents in @@ -199,7 +199,7 @@ class SubscribeEffectsTests: XCTestCase { groups: ["g1", "g2", "g2-pnpres"], retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + ), with: EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) ) effect.performTask { returnedEvents in let expectedCursor = SubscribeCursor(timetoken: 12345, region: 1) @@ -215,7 +215,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.expectationDescription = "Effect Completion Expectation" expectation.assertForOverFulfill = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) let urlError = URLError(.badServerResponse) mockResponse( @@ -228,7 +228,7 @@ class SubscribeEffectsTests: XCTestCase { groups: ["g1", "g2", "g2-pnpres"], retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in let expectedUnderlyingErr = PubNubError(.nameResolutionFailure, underlying: URLError(.cannotFindHost)) @@ -244,7 +244,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.expectationDescription = "Effect Completion Expectation" expectation.assertForOverFulfill = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) let urlError = URLError(.badServerResponse) let effect = factory.effect( @@ -253,7 +253,7 @@ class SubscribeEffectsTests: XCTestCase { groups: ["g1", "g2", "g2-pnpres"], retryAttempt: 3, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in let expectedUnderlyingErr = PubNubError(.badServerResponse) @@ -270,7 +270,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.assertForOverFulfill = true let delay = 1.0 - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: configWithLinearPolicy(delay))) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: configWithLinearPolicy(delay))) let urlError = URLError(.badServerResponse) mockResponse(subscribeResponse: SubscribeResponse( @@ -285,7 +285,7 @@ class SubscribeEffectsTests: XCTestCase { groups: ["g1", "g2", "g2-pnpres"], retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { _ in XCTAssertTrue(Int(Date().timeIntervalSince(startDate)) == Int(delay)) @@ -299,7 +299,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.expectationDescription = "Effect Completion Expectation" expectation.assertForOverFulfill = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) let urlError = URLError(.badServerResponse) mockResponse(subscribeResponse: SubscribeResponse( @@ -314,7 +314,7 @@ class SubscribeEffectsTests: XCTestCase { cursor: SubscribeCursor(timetoken: 1111, region: 1), retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in let expectedCursor = SubscribeCursor(timetoken: 12345, region: 1) @@ -330,7 +330,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.expectationDescription = "Effect Completion Expectation" expectation.assertForOverFulfill = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) let urlError = URLError(.badServerResponse) mockResponse( @@ -345,7 +345,7 @@ class SubscribeEffectsTests: XCTestCase { cursor: SubscribeCursor(timetoken: 1111, region: 1), retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in let expectedUnderlyingErr = PubNubError(.nameResolutionFailure) @@ -361,7 +361,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.expectationDescription = "Effect Completion Expectation" expectation.assertForOverFulfill = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: config)) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: config)) let urlError = URLError(.badServerResponse) mockResponse( @@ -376,7 +376,7 @@ class SubscribeEffectsTests: XCTestCase { cursor: SubscribeCursor(timetoken: 1111, region: 1), retryAttempt: 3, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in let expectedUnderlyingErr = PubNubError(.badServerResponse) @@ -393,7 +393,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.assertForOverFulfill = true let delay = 1.0 - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: configWithLinearPolicy(delay))) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: configWithLinearPolicy(delay))) let urlError = URLError(.badServerResponse) mockResponse(subscribeResponse: SubscribeResponse( @@ -409,7 +409,7 @@ class SubscribeEffectsTests: XCTestCase { cursor: SubscribeCursor(timetoken: 1111, region: 1), retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { _ in XCTAssertTrue(Int(Date().timeIntervalSince(startDate)) == Int(delay)) @@ -424,7 +424,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.assertForOverFulfill = true expectation.isInverted = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: configWithLinearPolicy(1.0))) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: configWithLinearPolicy(1.0))) let urlError = URLError(.badServerResponse) mockResponse(subscribeResponse: SubscribeResponse( @@ -438,7 +438,7 @@ class SubscribeEffectsTests: XCTestCase { groups: ["g1", "g2", "g2-pnpres"], retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in expectation.fulfill() @@ -454,7 +454,7 @@ class SubscribeEffectsTests: XCTestCase { expectation.assertForOverFulfill = true expectation.isInverted = true - let customInput = EventEngineCustomInput(value: Subscribe.EngineInput(configuration: configWithLinearPolicy(1.0))) + let dependencies = EventEngineDependencies(value: Subscribe.Dependencies(configuration: configWithLinearPolicy(1.0))) let urlError = URLError(.badServerResponse) mockResponse(subscribeResponse: SubscribeResponse( @@ -469,7 +469,7 @@ class SubscribeEffectsTests: XCTestCase { cursor: SubscribeCursor(timetoken: 1111, region: 1), retryAttempt: 1, reason: SubscribeError(underlying: PubNubError(urlError.pubnubReason!, underlying: urlError)) - ), with: customInput + ), with: dependencies ) effect.performTask { returnedEvents in expectation.fulfill()