diff --git a/Source/ARTProtocolMessage+Private.h b/Source/ARTProtocolMessage+Private.h index f9464f860..d094a0aec 100644 --- a/Source/ARTProtocolMessage+Private.h +++ b/Source/ARTProtocolMessage+Private.h @@ -6,6 +6,8 @@ // Copyright (c) 2014 Ably. All rights reserved. // +NSString *__art_nonnull ARTProtocolMessageActionToStr(ARTProtocolMessageAction action); + ART_ASSUME_NONNULL_BEGIN @interface ARTProtocolMessage () diff --git a/Source/ARTProtocolMessage.h b/Source/ARTProtocolMessage.h index 72cf8c08a..4d6e532c0 100644 --- a/Source/ARTProtocolMessage.h +++ b/Source/ARTProtocolMessage.h @@ -38,6 +38,8 @@ typedef NS_ENUM(NSUInteger, ARTProtocolMessageAction) { ARTProtocolMessageAuth = 17, }; +NSString *__art_nonnull ARTProtocolMessageActionToStr(ARTProtocolMessageAction action); + ART_ASSUME_NONNULL_BEGIN /** diff --git a/Source/ARTProtocolMessage.m b/Source/ARTProtocolMessage.m index a3ee70325..f73fcb433 100644 --- a/Source/ARTProtocolMessage.m +++ b/Source/ARTProtocolMessage.m @@ -116,3 +116,44 @@ - (ARTConnectionDetails *)getConnectionDetails { } @end + +NSString* ARTProtocolMessageActionToStr(ARTProtocolMessageAction action) { + switch(action) { + case ARTProtocolMessageHeartbeat: + return @"Heartbeat"; //0 + case ARTProtocolMessageAck: + return @"Ack"; //1 + case ARTProtocolMessageNack: + return @"Nack"; //2 + case ARTProtocolMessageConnect: + return @"Connect"; //3 + case ARTProtocolMessageConnected: + return @"Connected"; //4 + case ARTProtocolMessageDisconnect: + return @"Disconnect"; //5 + case ARTProtocolMessageDisconnected: + return @"Disconnected"; //6 + case ARTProtocolMessageClose: + return @"Close"; //7 + case ARTProtocolMessageClosed: + return @"Closed"; //8 + case ARTProtocolMessageError: + return @"Error"; //9 + case ARTProtocolMessageAttach: + return @"Attach"; //10 + case ARTProtocolMessageAttached: + return @"Attached"; //11 + case ARTProtocolMessageDetach: + return @"Detach"; //12 + case ARTProtocolMessageDetached: + return @"Detached"; //13 + case ARTProtocolMessagePresence: + return @"Presence"; //14 + case ARTProtocolMessageMessage: + return @"Message"; //15 + case ARTProtocolMessageSync: + return @"Sync"; //16 + case ARTProtocolMessageAuth: + return @"Auth"; //17 + } +} diff --git a/Source/ARTRealtime+Private.h b/Source/ARTRealtime+Private.h index c5ee12235..fa907b64d 100644 --- a/Source/ARTRealtime+Private.h +++ b/Source/ARTRealtime+Private.h @@ -28,8 +28,6 @@ ART_ASSUME_NONNULL_BEGIN @property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTConnectionStateChange *) *internalEventEmitter; @property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNull *, NSNull *) *connectedEventEmitter; -+ (NSString *)protocolStr:(ARTProtocolMessageAction)action; - // State properties - (BOOL)shouldSendEvents; - (BOOL)shouldQueueEvents; @@ -48,6 +46,7 @@ ART_ASSUME_NONNULL_BEGIN @property (readonly, getter=getTransport, art_nullable) id transport; @property (readonly, strong, nonatomic, art_nonnull) id reachability; @property (readonly, getter=getLogger) ARTLog *logger; +@property (nonatomic) NSTimeInterval connectionStateTtl; /// Current protocol `msgSerial`. Starts at zero. @property (readwrite, assign, nonatomic) int64_t msgSerial; diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index b0839eb49..3f971cc98 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -48,7 +48,6 @@ @implementation ARTRealtime { BOOL _renewingToken; __GENERIC(ARTEventEmitter, NSNull *, ARTErrorInfo *) *_pingEventEmitter; NSDate *_startedReconnection; - NSTimeInterval _connectionStateTtl; Class _transportClass; Class _reachabilityClass; id _transport; @@ -312,7 +311,7 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange usingEvent if (!_transport) { NSString *resumeKey = nil; NSNumber *connectionSerial = nil; - if (stateChange.previous == ARTRealtimeFailed || stateChange.previous == ARTRealtimeDisconnected) { + if (stateChange.previous == ARTRealtimeFailed || stateChange.previous == ARTRealtimeDisconnected || stateChange.previous == ARTRealtimeSuspended) { resumeKey = self.connection.key; connectionSerial = [NSNumber numberWithLongLong:self.connection.serial]; _resuming = true; @@ -432,6 +431,12 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange usingEvent if ([self shouldSendEvents]) { [self sendQueuedMessages]; + // For every Channel + for (ARTRealtimeChannel* channel in self.channels) { + if (channel.state == ARTRealtimeChannelSuspended) { + [channel attach]; + } + } } else if (![self shouldQueueEvents]) { [self failQueuedMessages:status]; ARTStatus *channelStatus = status; @@ -440,22 +445,27 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange usingEvent } // For every Channel for (ARTRealtimeChannel* channel in self.channels) { - if (channel.state == ARTRealtimeChannelInitialized || channel.state == ARTRealtimeChannelAttaching || channel.state == ARTRealtimeChannelAttached || channel.state == ARTRealtimeChannelFailed) { - if(stateChange.current == ARTRealtimeClosing) { - //do nothing. Closed state is coming. - } - else if(stateChange.current == ARTRealtimeClosed) { - [channel detachChannel:[ARTStatus state:ARTStateOk]]; - } - else if(stateChange.current == ARTRealtimeSuspended) { - [channel detachChannel:channelStatus]; - } - else { - [channel setFailed:channelStatus]; - } - } - else { - [channel setSuspended:channelStatus]; + switch (channel.state) { + case ARTRealtimeChannelInitialized: + case ARTRealtimeChannelAttaching: + case ARTRealtimeChannelAttached: + case ARTRealtimeChannelFailed: + if (stateChange.current == ARTRealtimeClosing) { + //do nothing. Closed state is coming. + } + else if (stateChange.current == ARTRealtimeClosed) { + [channel detachChannel:[ARTStatus state:ARTStateOk]]; + } + else if (stateChange.current == ARTRealtimeSuspended) { + [channel setSuspended:channelStatus]; + } + else { + [channel setFailed:channelStatus]; + } + break; + default: + [channel setSuspended:channelStatus]; + break; } } } @@ -942,49 +952,6 @@ - (void)setReachabilityClass:(Class)reachabilityClass { _reachabilityClass = reachabilityClass; } -+ (NSString *)protocolStr:(ARTProtocolMessageAction) action { - switch(action) { - case ARTProtocolMessageHeartbeat: - return @"Heartbeat"; //0 - case ARTProtocolMessageAck: - return @"Ack"; //1 - case ARTProtocolMessageNack: - return @"Nack"; //2 - case ARTProtocolMessageConnect: - return @"Connect"; //3 - case ARTProtocolMessageConnected: - return @"Connected"; //4 - case ARTProtocolMessageDisconnect: - return @"Disconnect"; //5 - case ARTProtocolMessageDisconnected: - return @"Disconnected"; //6 - case ARTProtocolMessageClose: - return @"Close"; //7 - case ARTProtocolMessageClosed: - return @"Closed"; //8 - case ARTProtocolMessageError: - return @"Error"; //9 - case ARTProtocolMessageAttach: - return @"Attach"; //10 - case ARTProtocolMessageAttached: - return @"Attached"; //11 - case ARTProtocolMessageDetach: - return @"Detach"; //12 - case ARTProtocolMessageDetached: - return @"Detached"; //13 - case ARTProtocolMessagePresence: - return @"Presence"; //14 - case ARTProtocolMessageMessage: - return @"Message"; //15 - case ARTProtocolMessageSync: - return @"Sync"; //16 - case ARTProtocolMessageAuth: - return @"Auth"; //17 - default: - return [NSString stringWithFormat: @"unknown protocol state %d", (int)action]; - } -} - #pragma mark - ARTRealtimeTransportDelegate implementation - (void)realtimeTransport:(id)transport didReceiveMessage:(ARTProtocolMessage *)message { @@ -993,7 +960,7 @@ - (void)realtimeTransport:(id)transport didReceiveMessage:(ARTProtocolMessage *) return; } - [self.logger verbose:@"R:%p ARTRealtime didReceive Protocol Message %@ ", self, [ARTRealtime protocolStr:message.action]]; + [self.logger verbose:@"R:%p ARTRealtime didReceive Protocol Message %@ ", self, ARTProtocolMessageActionToStr(message.action)]; if (message.error) { [self.logger verbose:@"R:%p ARTRealtime Protocol Message with error %@ ", self, message.error]; diff --git a/Source/ARTRealtimeChannel+Private.h b/Source/ARTRealtimeChannel+Private.h index 36031f42f..c7199c210 100644 --- a/Source/ARTRealtimeChannel+Private.h +++ b/Source/ARTRealtimeChannel+Private.h @@ -23,7 +23,7 @@ ART_ASSUME_NONNULL_BEGIN @property (readwrite, strong, nonatomic) NSMutableArray *queuedMessages; @property (readwrite, strong, nonatomic, art_nullable) NSString *attachSerial; @property (readonly, getter=getClientId) NSString *clientId; -@property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTErrorInfo *) *statesEventEmitter; +@property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTChannelStateChange *) *statesEventEmitter; @property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSString *, ARTMessage *) *messagesEventEmitter; @property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTPresenceMessage *) *presenceEventEmitter; @property (readwrite, strong, nonatomic) ARTPresenceMap *presenceMap; diff --git a/Source/ARTRealtimeChannel.h b/Source/ARTRealtimeChannel.h index 2b20f4446..2d972d350 100644 --- a/Source/ARTRealtimeChannel.h +++ b/Source/ARTRealtimeChannel.h @@ -42,7 +42,7 @@ ART_ASSUME_NONNULL_BEGIN - (BOOL)history:(ARTRealtimeHistoryQuery *__art_nullable)query callback:(void(^)(__GENERIC(ARTPaginatedResult, ARTMessage *) *__art_nullable result, ARTErrorInfo *__art_nullable error))callback error:(NSError *__art_nullable *__art_nullable)errorPtr; -ART_EMBED_INTERFACE_EVENT_EMITTER(ARTChannelEvent, ARTErrorInfo *) +ART_EMBED_INTERFACE_EVENT_EMITTER(ARTChannelEvent, ARTChannelStateChange *) @end diff --git a/Source/ARTRealtimeChannel.m b/Source/ARTRealtimeChannel.m index 41daebef0..e81b46e7e 100644 --- a/Source/ARTRealtimeChannel.m +++ b/Source/ARTRealtimeChannel.m @@ -157,6 +157,7 @@ - (void)publishProtocolMessage:(ARTProtocolMessage *)pm callback:(void (^)(ARTSt [self addToQueue:pm callback:cb]; break; } + case ARTRealtimeChannelSuspended: case ARTRealtimeChannelDetaching: case ARTRealtimeChannelDetached: case ARTRealtimeChannelFailed: @@ -180,8 +181,6 @@ - (void)publishProtocolMessage:(ARTProtocolMessage *)pm callback:(void (^)(ARTSt } break; } - default: - NSAssert(NO, @"Invalid State"); } } @@ -272,19 +271,19 @@ - (void)unsubscribe:(NSString *)name listener:(ARTEventListener *) [self.messagesEventEmitter off:name listener:listener]; } -- (__GENERIC(ARTEventListener, ARTErrorInfo *) *)on:(ARTChannelEvent)event callback:(void (^)(ARTErrorInfo *))cb { +- (__GENERIC(ARTEventListener, ARTChannelStateChange *) *)on:(ARTChannelEvent)event callback:(void (^)(ARTChannelStateChange *))cb { return [self.statesEventEmitter on:[NSNumber numberWithInt:event] callback:cb]; } -- (__GENERIC(ARTEventListener, ARTErrorInfo *) *)on:(void (^)(ARTErrorInfo *))cb { +- (__GENERIC(ARTEventListener, ARTChannelStateChange *) *)on:(void (^)(ARTChannelStateChange *))cb { return [self.statesEventEmitter on:cb]; } -- (__GENERIC(ARTEventListener, ARTErrorInfo *) *)once:(ARTChannelEvent)event callback:(void (^)(ARTErrorInfo *))cb { +- (__GENERIC(ARTEventListener, ARTChannelStateChange *) *)once:(ARTChannelEvent)event callback:(void (^)(ARTChannelStateChange *))cb { return [self.statesEventEmitter once:[NSNumber numberWithInt:event] callback:cb]; } -- (__GENERIC(ARTEventListener, ARTErrorInfo *) *)once:(void (^)(ARTErrorInfo *))cb { +- (__GENERIC(ARTEventListener, ARTChannelStateChange *) *)once:(void (^)(ARTChannelStateChange *))cb { return [self.statesEventEmitter once:cb]; } @@ -295,11 +294,11 @@ - (void)off:(ARTChannelEvent)event listener:listener { [self.statesEventEmitter off:[NSNumber numberWithInt:event] listener:listener]; } -- (void)off:(__GENERIC(ARTEventListener, ARTErrorInfo *) *)listener { +- (void)off:(__GENERIC(ARTEventListener, ARTChannelStateChange *) *)listener { [self.statesEventEmitter off:listener]; } -- (void)emit:(ARTChannelEvent)event with:(ARTErrorInfo *)data { +- (void)emit:(ARTChannelEvent)event with:(ARTChannelStateChange *)data { [self.statesEventEmitter emit:[NSNumber numberWithInt:event] with:data]; } @@ -308,6 +307,7 @@ - (ARTEventListener *)timed:(ARTEventListener *)listener deadline:(NSTimeInterva } - (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status { + ARTChannelStateChange *stateChange = [[ARTChannelStateChange alloc] initWithCurrent:state previous:self.state reason:status.errorInfo]; self.state = state; _errorReason = status.errorInfo; @@ -321,7 +321,7 @@ - (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status { [_attachedEventEmitter emit:[NSNull null] with:[ARTErrorInfo createWithCode:90000 message:msg]]; } - [self emit:(ARTChannelEvent)state with:status.errorInfo]; + [self emit:(ARTChannelEvent)stateChange.current with:stateChange]; } - (void)dealloc { @@ -339,7 +339,8 @@ - (void)unlessStateChangesBefore:(NSTimeInterval)deadline do:(void(^)())callback // Already changed; do nothing. return; } - [self timed:[self once:^(ARTErrorInfo *errorInfo) { + // FIXME: should not use the global listener for internal purpose + [self timed:[self once:^(ARTChannelStateChange *stateChange) { // Any state change cancels the timeout. }] deadline:deadline onTimeout:callback]; }); @@ -397,8 +398,8 @@ - (void)setAttached:(ARTProtocolMessage *)message { if (self.state == ARTRealtimeChannelAttached) { if (message.error != nil) { _errorReason = message.error; - [self emit:ARTChannelEventUpdate with:message.error]; } + [self emit:ARTChannelEventUpdate with:[[ARTChannelStateChange alloc] initWithCurrent:self.state previous:self.state reason:message.error]]; return; } @@ -448,7 +449,7 @@ - (void)setFailed:(ARTStatus *)error { - (void)setSuspended:(ARTStatus *)error { [self failQueuedMessages:error]; - [self transition:ARTRealtimeChannelDetached status:error]; + [self transition:ARTRealtimeChannelSuspended status:error]; } - (void)onMessage:(ARTProtocolMessage *)message { @@ -463,7 +464,7 @@ - (void)onMessage:(ARTProtocolMessage *)message { ARTErrorInfo *errorInfo = [ARTErrorInfo wrap:(ARTErrorInfo *)error.userInfo[NSLocalizedFailureReasonErrorKey] prepend:@"Failed to decode data: "]; [self.logger error:@"R:%p C:%p %@", _realtime, self, errorInfo.message]; _errorReason = errorInfo; - [self emit:ARTChannelEventUpdate with:errorInfo]; + [self emit:ARTChannelEventUpdate with:[[ARTChannelStateChange alloc] initWithCurrent:self.state previous:self.state reason:errorInfo]]; } } diff --git a/Source/ARTTypes.h b/Source/ARTTypes.h index 4ab1eeadf..915e6cc6c 100644 --- a/Source/ARTTypes.h +++ b/Source/ARTTypes.h @@ -76,6 +76,7 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { ARTRealtimeChannelAttached, ARTRealtimeChannelDetaching, ARTRealtimeChannelDetached, + ARTRealtimeChannelSuspended, ARTRealtimeChannelFailed }; @@ -90,12 +91,14 @@ typedef NS_ENUM(NSUInteger, ARTChannelEvent) { ARTChannelEventAttached, ARTChannelEventDetaching, ARTChannelEventDetached, + ARTChannelEventSuspended, ARTChannelEventFailed, ARTChannelEventUpdate }; NSString *__art_nonnull ARTChannelEventToStr(ARTChannelEvent event); + typedef NS_ENUM(NSInteger, ARTDataQueryError) { ARTDataQueryErrorLimit = 1, ARTDataQueryErrorTimestampRange = 2, @@ -144,6 +147,28 @@ NSString *generateNonce(); @end +#pragma mark - ARTChannelStateChange + +@interface ARTChannelStateChange : NSObject + +- (instancetype)initWithCurrent:(ARTRealtimeChannelState)current + previous:(ARTRealtimeChannelState)previous + reason:(ARTErrorInfo *__art_nullable)reason; + +- (instancetype)initWithCurrent:(ARTRealtimeChannelState)current + previous:(ARTRealtimeChannelState)previous + reason:(ARTErrorInfo *__art_nullable)reason + resumed:(BOOL)resumed; + +@property (readonly, nonatomic) ARTRealtimeChannelState current; +@property (readonly, nonatomic) ARTRealtimeChannelState previous; +@property (readonly, nonatomic, art_nullable) ARTErrorInfo *reason; +@property (readonly, nonatomic) BOOL resumed; + +@end + +#pragma mark - ARTJsonCompatible + @protocol ARTJsonCompatible - (NSDictionary *__art_nullable)toJSON:(NSError *__art_nullable *__art_nullable)error; @end diff --git a/Source/ARTTypes.m b/Source/ARTTypes.m index a00c8ca25..9c5c56851 100644 --- a/Source/ARTTypes.m +++ b/Source/ARTTypes.m @@ -115,6 +115,33 @@ - (void)setRetryIn:(NSTimeInterval)retryIn { } } +#pragma mark - ARTChannelStateChange + +@implementation ARTChannelStateChange + +- (instancetype)initWithCurrent:(ARTRealtimeChannelState)current previous:(ARTRealtimeChannelState)previous reason:(ARTErrorInfo *)reason { + return [self initWithCurrent:current previous:previous reason:reason resumed:NO]; +} + +- (instancetype)initWithCurrent:(ARTRealtimeChannelState)current previous:(ARTRealtimeChannelState)previous reason:(ARTErrorInfo *)reason resumed:(BOOL)resumed { + self = [self init]; + if (self) { + _current = current; + _previous = previous; + _reason = reason; + _resumed = resumed; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ - \n\t current: %@; \n\t previous: %@; \n\t reason: %@; \n\t resumed: %d; \n", [super description], ARTRealtimeChannelStateToStr(_current), ARTRealtimeChannelStateToStr(_previous), _reason, _resumed]; +} + +@end + +#pragma mark - ARTJsonCompatible + @implementation NSString (ARTJsonCompatible) - (NSDictionary *)toJSON:(NSError *__art_nullable *__art_nullable)error { @@ -158,7 +185,7 @@ - (NSString *)description { @end NSString *ARTRealtimeChannelStateToStr(ARTRealtimeChannelState state) { - switch(state) { + switch (state) { case ARTRealtimeChannelInitialized: return @"Initialized"; //0 case ARTRealtimeChannelAttaching: @@ -169,13 +196,15 @@ - (NSString *)description { return @"Detaching"; //3 case ARTRealtimeChannelDetached: return @"Detached"; //4 + case ARTRealtimeChannelSuspended: + return @"Suspended"; //5 case ARTRealtimeChannelFailed: - return @"Failed"; //5 + return @"Failed"; //6 } } NSString *ARTChannelEventToStr(ARTChannelEvent event) { - switch(event) { + switch (event) { case ARTChannelEventInitialized: return @"Initialized"; //0 case ARTChannelEventAttaching: @@ -186,9 +215,11 @@ - (NSString *)description { return @"Detaching"; //3 case ARTChannelEventDetached: return @"Detached"; //4 + case ARTChannelEventSuspended: + return @"Suspended"; //5 case ARTChannelEventFailed: - return @"Failed"; //5 + return @"Failed"; //6 case ARTChannelEventUpdate: - return @"Update"; //6 + return @"Update"; //7 } } diff --git a/Spec/RealtimeClient.swift b/Spec/RealtimeClient.swift index fcf0e7bbf..c1215089b 100644 --- a/Spec/RealtimeClient.swift +++ b/Spec/RealtimeClient.swift @@ -491,8 +491,8 @@ class RealtimeClient: QuickSpec { let channel = client.channels.get("foo") waitUntil(timeout: testTimeout) { done in - channel.once(.Failed) { error in - guard let error = error else { + channel.once(.Failed) { stateChange in + guard let error = stateChange?.reason else { fail("Error is nil"); done(); return } expect(error.message).to(contain("Channel denied access based on given capability")) @@ -552,11 +552,11 @@ class RealtimeClient: QuickSpec { // Retry Channel attach waitUntil(timeout: testTimeout) { done in - channel.once(.Failed) { error in + channel.once(.Failed) { _ in fail("Should not reach Failed state"); done(); return } - channel.once(.Attached) { error in - expect(error).to(beNil()) + channel.once(.Attached) { stateChange in + expect(stateChange?.reason).to(beNil()) done() } channel.attach() @@ -587,8 +587,8 @@ class RealtimeClient: QuickSpec { waitUntil(timeout: testTimeout) { done in let partialDone = AblyTests.splitDone(2, done: done) - channel.once(.Failed) { error in - guard let error = error else { + channel.once(.Failed) { stateChange in + guard let error = stateChange?.reason else { fail("ErrorInfo is nil"); partialDone(); return } expect(error).to(beIdenticalTo(channel.errorReason)) diff --git a/Spec/RealtimeClientChannel.swift b/Spec/RealtimeClientChannel.swift index ca05891a9..6b88b0536 100644 --- a/Spec/RealtimeClientChannel.swift +++ b/Spec/RealtimeClientChannel.swift @@ -77,7 +77,7 @@ class RealtimeClientChannel: QuickSpec { } // RTL2 - context("EventEmitter and states") { + context("EventEmitter, channel states and events") { // RTL2a it("should implement the EventEmitter and emit events for state changes") { @@ -103,16 +103,25 @@ class RealtimeClientChannel: QuickSpec { emitCounter += 1 } - var states = [ARTRealtimeChannelState]() + var states = [channel.state] waitUntil(timeout: testTimeout) { done in - channel.on { errorInfo in - states += [channel.state] - switch channel.state { + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStateChange is nil"); done(); return + } + expect(stateChange.previous).to(equal(states.last)) + expect(channel.state).to(equal(stateChange.current)) + states += [stateChange.current] + + switch stateChange.current { case .Attached: + expect(stateChange.reason).to(beNil()) channel.detach() case .Detached: - channel.onError(AblyTests.newErrorProtocolMessage()) - case .Failed: + guard let error = stateChange.reason else { + fail("Detach state change reason is nil"); done(); return + } + expect(error.message).to(contain("channel has detached")) done() default: break @@ -120,22 +129,119 @@ class RealtimeClientChannel: QuickSpec { } channel.attach() } - channel.off() expect(channelOnMethodCalled).to(beTrue()) expect(statesEventEmitterOnMethodCalled).to(beTrue()) - expect(emitCounter).to(equal(5)) + expect(emitCounter).to(equal(4)) if states.count != 5 { fail("Expecting 5 states; got \(states)") return } - expect(states[0].rawValue).to(equal(ARTRealtimeChannelState.Attaching.rawValue), description: "Should be ATTACHING state") - expect(states[1].rawValue).to(equal(ARTRealtimeChannelState.Attached.rawValue), description: "Should be ATTACHED state") - expect(states[2].rawValue).to(equal(ARTRealtimeChannelState.Detaching.rawValue), description: "Should be DETACHING state") - expect(states[3].rawValue).to(equal(ARTRealtimeChannelState.Detached.rawValue), description: "Should be DETACHED state") - expect(states[4].rawValue).to(equal(ARTRealtimeChannelState.Failed.rawValue), description: "Should be FAILED state") + expect(states[0].rawValue).to(equal(ARTRealtimeChannelState.Initialized.rawValue), description: "Should be INITIALIZED state") + expect(states[1].rawValue).to(equal(ARTRealtimeChannelState.Attaching.rawValue), description: "Should be ATTACHING state") + expect(states[2].rawValue).to(equal(ARTRealtimeChannelState.Attached.rawValue), description: "Should be ATTACHED state") + expect(states[3].rawValue).to(equal(ARTRealtimeChannelState.Detaching.rawValue), description: "Should be DETACHING state") + expect(states[4].rawValue).to(equal(ARTRealtimeChannelState.Detached.rawValue), description: "Should be DETACHED state") + } + + // RTL2a + it("should implement the EventEmitter and emit events for FAILED state changes") { + let options = AblyTests.clientOptions() + options.token = getTestToken(capability: "{\"secret\":[\"subscribe\"]}") + let client = ARTRealtime(options: options) + defer { client.dispose(); client.close() } + let channel = client.channels.get("test") + + waitUntil(timeout: testTimeout) { done in + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStateChange is nil"); done(); return + } + expect(channel.state).to(equal(stateChange.current)) + switch stateChange.current { + case .Attaching: + expect(stateChange.reason).to(beNil()) + expect(stateChange.previous).to(equal(ARTRealtimeChannelState.Initialized)) + case .Failed: + guard let reason = stateChange.reason else { + fail("Reason is nil"); done(); return + } + expect(reason.code) == 40160 + expect(stateChange.previous).to(equal(ARTRealtimeChannelState.Attaching)) + done() + default: + break + } + } + channel.attach() + } + } + + // RTL2a + it("should implement the EventEmitter and emit events for SUSPENDED state changes") { + let client = ARTRealtime(options: AblyTests.commonAppSetup()) + defer { client.dispose(); client.close() } + let channel = client.channels.get("test") + + waitUntil(timeout: testTimeout) { done in + channel.attach() { error in + expect(error).to(beNil()) + done() + } + } + + client.simulateSuspended(beforeSuspension: { done in + channel.once(.Suspended) { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + expect(stateChange.reason).to(beNil()) + expect(stateChange.previous).to(equal(ARTRealtimeChannelState.Attached)) + expect(channel.state).to(equal(stateChange.current)) + done() + } + }) + } + + // RTL2g + it("can emit an UPDATE event") { + let client = ARTRealtime(options: AblyTests.commonAppSetup()) + defer { client.dispose(); client.close() } + let channel = client.channels.get("foo") + waitUntil(timeout: testTimeout) { done in + channel.attach() { error in + expect(error).to(beNil()) + done() + } + } + + channel.on(.Attached) { _ in + fail("Should not emit Attached again") + } + defer { + channel.off() + } + + waitUntil(timeout: testTimeout) { done in + channel.on(.Update) { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStateChange is nil"); done(); return + } + expect(channel.state).to(equal(ARTRealtimeChannelState.Attached)) + expect(stateChange.previous).to(equal(channel.state)) + expect(stateChange.current).to(equal(channel.state)) + expect(stateChange.resumed).to(beFalse()) + expect(stateChange.reason).to(beNil()) + done() + } + + let attachedMessage = ARTProtocolMessage() + attachedMessage.action = .Attached + attachedMessage.channel = "foo" + client.transport?.receive(attachedMessage) + } } // RTL2b @@ -157,15 +263,96 @@ class RealtimeClientChannel: QuickSpec { defer { client.dispose(); client.close() } let channel = client.channels.get("test") - let error = AblyTests.newErrorProtocolMessage() + let pmError = AblyTests.newErrorProtocolMessage() + waitUntil(timeout: testTimeout) { done in + channel.on(.Failed) { stateChange in + guard let error = stateChange?.reason else { + fail("Error is nil"); done(); return + } + expect(error).to(equal(pmError.error)) + expect(channel.errorReason).to(equal(pmError.error)) + done() + } + channel.onError(pmError) + } + } + + // RTL2d + it("a ChannelStateChange is emitted as the first argument for every channel state change") { + let client = ARTRealtime(options: AblyTests.commonAppSetup()) + defer { client.dispose(); client.close() } + let channel = client.channels.get("test") + + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); return + } + expect(stateChange.reason).to(beNil()) + expect(stateChange.current.rawValue).to(equal(channel.state.rawValue)) + expect(stateChange.previous.rawValue).toNot(equal(channel.state.rawValue)) + } + + channel.attach() + expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) + channel.off() + + waitUntil(timeout: testTimeout) { done in + channel.once(.Failed) { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + expect(stateChange.reason).toNot(beNil()) + expect(stateChange.current).to(equal(ARTRealtimeChannelState.Failed)) + expect(stateChange.previous).to(equal(ARTRealtimeChannelState.Attached)) + done() + } + channel.onError(AblyTests.newErrorProtocolMessage()) + } + } + + // RTL2f + pending("ChannelStateChange will contain a resumed boolean attribute with value @true@ if the bit flag RESUMED was included") { + let options = AblyTests.commonAppSetup() + options.disconnectedRetryTimeout = 1.0 + options.tokenDetails = getTestTokenDetails(ttl: 5.0) + let client = ARTRealtime(options: options) + defer { client.dispose(); client.close() } + let channel = client.channels.get("test") + + waitUntil(timeout: testTimeout) { done in + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + switch stateChange.current { + case .Attached: + expect(stateChange.resumed).to(beFalse()) + default: + expect(stateChange.resumed).to(beFalse()) + } + } + client.connection.once(.Disconnected) { stateChange in + channel.off() + guard let error = stateChange?.reason else { + fail("Error is nil"); done(); return + } + expect(error.code) == 40142 + done() + } + channel.attach() + } waitUntil(timeout: testTimeout) { done in - channel.on(.Failed) { errorInfo in - expect(errorInfo).to(equal(error.error)) - expect(channel.errorReason).to(equal(error.error)) + channel.once(.Attached) { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + expect(stateChange.resumed).to(beTrue()) + expect(stateChange.reason).to(beNil()) + expect(stateChange.current).to(equal(ARTRealtimeChannelState.Attached)) + expect(stateChange.previous).to(equal(ARTRealtimeChannelState.Attached)) done() } - channel.onError(error) } } @@ -194,28 +381,14 @@ class RealtimeClientChannel: QuickSpec { waitUntil(timeout: testTimeout) { done in let pmError = AblyTests.newErrorProtocolMessage() - - channel.on { stateChange in - let partialDone = AblyTests.splitDone(2, done: done) - - guard let stateChange = stateChange else { - fail("ChannelStateChange is nil"); partialDone(); return - } - guard let error = stateChange.reason else { - fail("Reason error is nil"); partialDone(); return - } - - if stateChange.current == .Failed { - expect(error).to(equal(pmError.error)) - expect(channel.errorReason).to(beIdenticalTo(error)) - partialDone() - } - else if stateChange.current == .Error { - expect(error).to(equal(pmError.error)) - partialDone() + channel.once(.Failed) { stateChange in + guard let error = stateChange?.reason else { + fail("Reason error is nil"); done(); return } + expect(error).to(equal(pmError.error)) + expect(channel.errorReason).to(beIdenticalTo(error)) + done() } - client.onError(pmError) } @@ -232,28 +405,14 @@ class RealtimeClientChannel: QuickSpec { waitUntil(timeout: testTimeout) { done in let pmError = AblyTests.newErrorProtocolMessage() - - channel.on { stateChange in - let partialDone = AblyTests.splitDone(2, done: done) - - guard let stateChange = stateChange else { - fail("ChannelStateChange is nil"); partialDone(); return - } - guard let error = stateChange.reason else { - fail("Reason error is nil"); partialDone(); return - } - - if stateChange.current == .Failed { - expect(error).to(equal(pmError.error)) - expect(channel.errorReason).to(beIdenticalTo(error)) - partialDone() - } - else if stateChange.current == .Error { - expect(error).to(equal(pmError.error)) - partialDone() + channel.once(.Failed) { stateChange in + guard let error = stateChange?.reason else { + fail("Reason error is nil"); done(); return } + expect(error).to(equal(pmError.error)) + expect(channel.errorReason).to(equal(error)) + done() } - client.onError(pmError) } @@ -340,7 +499,7 @@ class RealtimeClientChannel: QuickSpec { } // RTL3d - it("if the connection state enters the @CONNECTED@ state, then a @SUSPENDED@ channel will initiate an attach operation") { + it("if the connection state enters the CONNECTED state, then a SUSPENDED channel will initiate an attach operation") { let options = AblyTests.commonAppSetup() options.suspendedRetryTimeout = 1.0 let client = ARTRealtime(options: options) @@ -354,26 +513,20 @@ class RealtimeClientChannel: QuickSpec { } } - waitUntil(timeout: testTimeout) { done in + client.simulateSuspended(beforeSuspension: { done in channel.once(.Suspended) { stateChange in expect(stateChange?.reason).to(beNil()) done() } - client.simulateSuspended() - } + }) expect(client.connection.state).toEventually(equal(ARTRealtimeConnectionState.Connected), timeout: testTimeout) expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) } // RTL3d - it("if the attach operation for the channel times out and the channel returns to the SUSPENDED state, then an ERROR event with Ably error code 91200 should be emitted") { - let options = AblyTests.commonAppSetup() - options.autoConnect = false - options.suspendedRetryTimeout = 1.0 - let client = ARTRealtime(options: options) - client.setTransportClass(TestProxyTransport.self) - client.connect() + it("if the attach operation for the channel times out and the channel returns to the SUSPENDED state") { + let client = AblyTests.newRealtime(AblyTests.commonAppSetup()) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -384,31 +537,15 @@ class RealtimeClientChannel: QuickSpec { } } - defer { - TestProxyTransport.network = nil - } - waitUntil(timeout: testTimeout) { done in - let partialDone = AblyTests.splitDone(2, done: done) - channel.on { stateChange in + client.simulateSuspended(beforeSuspension: { done in + channel.once(.Suspended) { stateChange in guard let stateChange = stateChange else { - fail("ChannelStateChange is nil"); partialDone(); return - } - switch stateChange.current { - case .Suspended: - expect(stateChange.reason).to(beNil()) - partialDone() - case .Error: - guard let error = stateChange.reason else { - fail("Error is nil"); partialDone(); return - } - expect(error.code) == 91200 - partialDone() + fail("ChannelStateChange is nil"); done(); return } + expect(stateChange.reason).to(beNil()) + done() } - client.simulateSuspended() - // Force connection to timeout - TestProxyTransport.network = .RequestTimeout(timeout: options.suspendedRetryTimeout + 1) - } + }) } // RTL3e @@ -423,6 +560,9 @@ class RealtimeClientChannel: QuickSpec { channel.once(.Detached) { stateChange in fail("Should not reach the DETACHED state") } + defer { + channel.off() + } waitUntil(timeout: testTimeout) { done in channel.attach() { error in @@ -694,13 +834,17 @@ class RealtimeClientChannel: QuickSpec { let channel = client.channels.get("test") channel.attach() - channel.on { errorInfo in - if channel.state == .Failed { - expect(errorInfo!.code).to(equal(40160)) + waitUntil(timeout: testTimeout) { done in + channel.once(.Failed) { stateChange in + guard let error = stateChange?.reason else { + fail("Reason error is nil"); done(); return + } + expect(error.code).to(equal(40160)) + done() } } - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Failed), timeout: testTimeout) + expect(channel.state).to(equal(ARTRealtimeChannelState.Failed)) } // RTL4f @@ -1143,8 +1287,11 @@ class RealtimeClientChannel: QuickSpec { let error = stateChange.reason if state == .Connected { let channel = client.channels.get("test") - channel.on { errorInfo in - if channel.state == .Attached { + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + if stateChange.current == .Attached { channel.publish(nil, data: "message") { errorInfo in expect(errorInfo).to(beNil()) done() @@ -1170,8 +1317,11 @@ class RealtimeClientChannel: QuickSpec { let error = stateChange.reason if state == .Connected { let channel = client.channels.get("test") - channel.on { errorInfo in - if channel.state == .Attached { + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); done(); return + } + if stateChange.current == .Attached { channel.publish(nil, data: "message") { errorInfo in expect(errorInfo).toNot(beNil()) guard let errorInfo = errorInfo else { @@ -1206,8 +1356,11 @@ class RealtimeClientChannel: QuickSpec { TotalMessages.failed = 0 let channelToSucceed = client.channels.get("channelToSucceed") - channelToSucceed.on { errorInfo in - if channelToSucceed.state == .Attached { + channelToSucceed.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); return + } + if stateChange.current == .Attached { for index in 1...TotalMessages.expected { channelToSucceed.publish(nil, data: "message\(index)") { errorInfo in if errorInfo == nil { @@ -1221,8 +1374,11 @@ class RealtimeClientChannel: QuickSpec { channelToSucceed.attach() let channelToFail = client.channels.get("channelToFail") - channelToFail.on { errorInfo in - if channelToFail.state == .Attached { + channelToFail.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); return + } + if stateChange.current == .Attached { for index in 1...TotalMessages.expected { channelToFail.publish(nil, data: "message\(index)") { errorInfo in if errorInfo != nil { @@ -1989,6 +2145,7 @@ class RealtimeClientChannel: QuickSpec { waitUntil(timeout: testTimeout) { done in let partlyDone = AblyTests.splitDone(2, done: done) + channel.subscribe(testMessage.encoded.name) { message in expect(message.data as? NSObject).to(equal(AblyTests.base64ToData(testMessage.encrypted.data))) @@ -2000,13 +2157,12 @@ class RealtimeClientChannel: QuickSpec { partlyDone() } - channel.on(.Update) { errorInfo in - guard let errorInfo = errorInfo else { + channel.on(.Update) { stateChange in + guard let error = stateChange?.reason else { return } - expect(errorInfo.message).to(contain("Failed to decode data: unknown encoding: 'bad_encoding_type'")) - expect(errorInfo).to(beIdenticalTo(channel.errorReason)) - + expect(error.message).to(contain("Failed to decode data: unknown encoding: 'bad_encoding_type'")) + expect(error).to(beIdenticalTo(channel.errorReason)) partlyDone() } @@ -2414,6 +2570,9 @@ class RealtimeClientChannel: QuickSpec { channel.on(.Attached) { _ in fail("Should not be called") } + defer { + channel.off() + } var hook: AspectToken? waitUntil(timeout: testTimeout) { done in @@ -2425,9 +2584,8 @@ class RealtimeClientChannel: QuickSpec { done() } - let transport = client.transport as! TestProxyTransport // Inject additional ATTACHED action without an error - transport.receive(attachedMessage) + client.transport?.receive(attachedMessage) } hook!.remove() expect(channel.errorReason).to(beNil()) @@ -2438,15 +2596,17 @@ class RealtimeClientChannel: QuickSpec { attachedMessageWithError.action = .Attached attachedMessageWithError.channel = "test" - channel.once(.Update) { error in - expect(error).to(beIdenticalTo(attachedMessageWithError.error)) - expect(channel.errorReason).to(beIdenticalTo(error)) + channel.once(.Update) { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStateChange is nil"); done(); return + } + expect(stateChange.reason).to(beIdenticalTo(attachedMessageWithError.error)) + expect(channel.errorReason).to(beIdenticalTo(stateChange.reason)) done() } - let transport = client.transport as! TestProxyTransport // Inject additional ATTACHED action with an error - transport.receive(attachedMessageWithError) + client.transport?.receive(attachedMessageWithError) } expect(channel.state).to(equal(ARTRealtimeChannelState.Attached)) } @@ -2468,18 +2628,16 @@ class RealtimeClientChannel: QuickSpec { errorProtocolMessage.action = .Error errorProtocolMessage.channel = "foo" - let partialDone = AblyTests.splitDone(2, done: done) - channel.once(.Failed) { stateChange in guard let error = stateChange?.reason else { - fail("Reason error is nil"); partialDone(); return + fail("Reason error is nil"); done(); return } expect(error).to(beIdenticalTo(errorProtocolMessage.error)) expect(channel.errorReason).to(beIdenticalTo(error)) - partialDone() + done() } - client.transport.receive(errorProtocolMessage) + client.transport?.receive(errorProtocolMessage) } expect(channel.state).to(equal(ARTRealtimeChannelState.Failed)) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 6aabc89c4..244197243 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -335,7 +335,7 @@ class RealtimeClientConnection: QuickSpec { let authMessage = ARTProtocolMessage() authMessage.action = .Auth - client.transport.receive(authMessage) + client.transport?.receive(authMessage) } } @@ -524,7 +524,7 @@ class RealtimeClientConnection: QuickSpec { let connectedMessageWithError = originalConnectedMessage connectedMessageWithError.error = ARTErrorInfo.createWithCode(1234, message: "fabricated error") - client.transport.receive(connectedMessageWithError) + client.transport?.receive(connectedMessageWithError) } } } @@ -555,8 +555,11 @@ class RealtimeClientConnection: QuickSpec { disposable.append(client) let channel = client.channels.get(channelName) - channel.on { errorInfo in - if channel.state == .Attached { + channel.on { stateChange in + guard let stateChange = stateChange else { + fail("ChannelStageChange is nil"); return + } + if stateChange.current == .Attached { TotalReach.shared += 1 } } @@ -681,15 +684,13 @@ class RealtimeClientConnection: QuickSpec { let error = stateChange.reason if state == .Connected { let channel = client.channels.get("test") - channel.on { errorInfo in - if channel.state == .Attached { - channel.presence.enterClient("client_string", data: nil, callback: { errorInfo in - expect(errorInfo).to(beNil()) - done() - }) - } + channel.attach() { error in + expect(error).to(beNil()) + channel.presence.enterClient("client_string", data: nil, callback: { errorInfo in + expect(errorInfo).to(beNil()) + done() + }) } - channel.attach() } } } @@ -752,15 +753,13 @@ class RealtimeClientConnection: QuickSpec { let error = stateChange.reason if state == .Connected { let channel = client.channels.get("test") - channel.on { errorInfo in - if channel.state == .Attached { - channel.presence.enterClient("invalid", data: nil, callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - done() - }) - } + channel.attach() { error in + expect(error).to(beNil()) + channel.presence.enterClient("invalid", data: nil, callback: { errorInfo in + expect(errorInfo).toNot(beNil()) + done() + }) } - channel.attach() } } } @@ -868,19 +867,17 @@ class RealtimeClientConnection: QuickSpec { transport.actionsIgnored += [.Ack, .Nack] waitUntil(timeout: testTimeout) { done in - channel.on { errorInfo in - if channel.state == .Attached { - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - done() - }) - // Wait until the message is pushed to Ably first - delay(1.0) { - transport.simulateIncomingNormalClose() - } + channel.attach() { error in + expect(error).to(beNil()) + channel.publish(nil, data: "message", callback: { errorInfo in + expect(errorInfo).toNot(beNil()) + done() + }) + // Wait until the message is pushed to Ably first + delay(1.0) { + transport.simulateIncomingNormalClose() } } - channel.attach() } } @@ -898,19 +895,17 @@ class RealtimeClientConnection: QuickSpec { transport.actionsIgnored += [.Ack, .Nack] waitUntil(timeout: testTimeout) { done in - channel.on { errorInfo in - if channel.state == .Attached { - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - done() - }) - // Wait until the message is pushed to Ably first - delay(1.0) { - transport.simulateIncomingError() - } + channel.attach() { error in + expect(error).to(beNil()) + channel.publish(nil, data: "message", callback: { errorInfo in + expect(errorInfo).toNot(beNil()) + done() + }) + // Wait until the message is pushed to Ably first + delay(1.0) { + transport.simulateIncomingError() } } - channel.attach() } } @@ -2095,9 +2090,9 @@ class RealtimeClientConnection: QuickSpec { } waitUntil(timeout: testTimeout) { done in - channel.once(.Attached) { error in - guard let error = error else { - fail("Error is nil"); done(); return + channel.once(.Attached) { stateChange in + guard let error = stateChange?.reason else { + fail("Reason error is nil"); done(); return } expect(error.message).to(equal("Channel injected error")) expect(channel.errorReason).to(beIdenticalTo(error)) @@ -3094,140 +3089,6 @@ class RealtimeClientConnection: QuickSpec { } - // RTN18 - context("state change side effects") { - - // RTN18a - it("when a connection enters the DISCONNECTED state, it will have no effect on the the channel states") { - let options = AblyTests.commonAppSetup() - let client = ARTRealtime(options: options) - defer { - client.dispose() - client.close() - } - - let channel = client.channels.get("test") - channel.attach() - - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) - - client.onDisconnected() - - expect(client.connection.state).to(equal(ARTRealtimeConnectionState.Disconnected)) - expect(channel.state).to(equal(ARTRealtimeChannelState.Attached)) - - waitUntil(timeout: testTimeout + options.disconnectedRetryTimeout) { done in - channel.publish(nil, data: "queuedMessage", callback: { errorInfo in - expect(errorInfo).to(beNil()) - done() - }) - } - expect(client.connection.state).to(equal(ARTRealtimeConnectionState.Connected)) - } - - // RTN18b - context("all channels will move to DETACHED state") { - - it("when a connection enters SUSPENDED state") { - let options = AblyTests.commonAppSetup() - options.suspendedRetryTimeout = 0.1 - let client = ARTRealtime(options: options) - defer { - client.dispose() - client.close() - } - - let channel = client.channels.get("test") - channel.attach() - - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) - - client.simulateSuspended() - - expect(client.connection.state).to(equal(ARTRealtimeConnectionState.Suspended)) - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Detached), timeout: testTimeout) - - waitUntil(timeout: testTimeout) { done in - // Reject publishing of messages - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - expect(errorInfo!.code).to(equal(90001)) - done() - }) - } - - expect(client.connection.state).toEventually(equal(ARTRealtimeConnectionState.Connecting), timeout: options.suspendedRetryTimeout + 1.0) - channel.attach() - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) - - waitUntil(timeout: testTimeout) { done in - // Accept publishing of messages - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).to(beNil()) - done() - }) - } - } - - it("when a connection enters CLOSED state") { - let options = AblyTests.commonAppSetup() - let client = ARTRealtime(options: options) - defer { - client.dispose() - client.close() - } - let channel = client.channels.get("test") - channel.attach() - - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) - - client.close() - - expect(client.connection.state).toEventually(equal(ARTRealtimeConnectionState.Closed), timeout: testTimeout) - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Detached), timeout: testTimeout) - - waitUntil(timeout: testTimeout) { done in - // Reject publishing of messages - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - expect(errorInfo!.code).to(equal(90001)) - done() - }) - } - } - - } - - // RTN18c - it("when a connection enters FAILED state, all channels will move to FAILED state") { - let options = AblyTests.commonAppSetup() - let client = ARTRealtime(options: options) - defer { - client.dispose() - client.close() - } - let channel = client.channels.get("test") - channel.attach() - - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Attached), timeout: testTimeout) - - client.onError(AblyTests.newErrorProtocolMessage()) - - expect(client.connection.state).to(equal(ARTRealtimeConnectionState.Failed)) - expect(channel.state).toEventually(equal(ARTRealtimeChannelState.Failed), timeout: testTimeout) - - waitUntil(timeout: testTimeout) { done in - // Reject publishing of messages - channel.publish(nil, data: "message", callback: { errorInfo in - expect(errorInfo).toNot(beNil()) - expect(errorInfo!.code).to(equal(90001)) - done() - }) - } - } - - } - // RTN19 it("attributes within ConnectionDetails should be used as defaults") { let options = AblyTests.commonAppSetup() @@ -3496,7 +3357,7 @@ class RealtimeClientConnection: QuickSpec { } waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connected) { stateChange in + client.connection.once(.Update) { stateChange in expect(stateChange?.reason).to(beNil()) expect(initialToken).toNot(equal(client.auth.tokenDetails?.token)) done() @@ -3689,7 +3550,7 @@ class RealtimeClientConnection: QuickSpec { let connectedMessageWithError = originalConnectedMessage connectedMessageWithError.error = ARTErrorInfo.createWithCode(1234, message: "fabricated error") - client.transport.receive(connectedMessageWithError) + client.transport?.receive(connectedMessageWithError) } expect(client.connection.errorReason).to(beNil()) diff --git a/Spec/TestUtilities.swift b/Spec/TestUtilities.swift index 67e737fcd..d95fa26cc 100644 --- a/Spec/TestUtilities.swift +++ b/Spec/TestUtilities.swift @@ -356,14 +356,14 @@ class PublishTestMessage { let state = stateChange.current if state == .Connected { let channel = client.channels.get("test") - channel.on { errorInfo in - switch channel.state { + channel.on { stateChange in + switch stateChange!.current { case .Attached: channel.publish(nil, data: "message") { errorInfo in complete(errorInfo) } case .Failed: - complete(errorInfo) + complete(stateChange!.reason) default: break } @@ -992,13 +992,13 @@ extension ARTRealtime { self.onDisconnected() } - func simulateSuspended() { + func simulateSuspended(beforeSuspension beforeSuspensionCallback: (done: () -> ()) -> Void) { waitUntil(timeout: testTimeout) { done in - self.connection.on(.Closed) { _ in + self.connection.once(.Disconnected) { _ in + beforeSuspensionCallback(done: done) self.onSuspended() - done() } - self.close() + self.onDisconnected() } } @@ -1067,7 +1067,7 @@ extension ARTRealtimeConnectionEvent : CustomStringConvertible { extension ARTProtocolMessageAction : CustomStringConvertible { public var description : String { - return ARTRealtime.protocolStr(self) + return ARTProtocolMessageActionToStr(self) } } diff --git a/Tests/ARTRealtimeAttachTest.m b/Tests/ARTRealtimeAttachTest.m index f1e755a8f..c08ec288b 100644 --- a/Tests/ARTRealtimeAttachTest.m +++ b/Tests/ARTRealtimeAttachTest.m @@ -43,13 +43,13 @@ - (void)testAttachOnce { ARTRealtimeChannel *channel = [realtime.channels get:@"attach"]; __block bool hasAttached = false; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttaching) { - XCTAssertNil(errorInfo); + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttaching) { + XCTAssertNil(stateChange.reason); [channel attach]; } - if (channel.state == ARTRealtimeChannelAttached) { - XCTAssertNil(errorInfo); + if (stateChange.current == ARTRealtimeChannelAttached) { + XCTAssertNil(stateChange.reason); [channel attach]; if(!hasAttached) { @@ -60,7 +60,7 @@ - (void)testAttachOnce { XCTFail(@"duplicate call to attach shouldnt happen"); } } - if (channel.state == ARTRealtimeChannelDetached) { + if (stateChange.current == ARTRealtimeChannelDetached) { [expectation fulfill]; } }]; @@ -80,13 +80,13 @@ - (void)testAttachMultipleChannels { [channel1 attach]; ARTRealtimeChannel *channel2 = [realtime.channels get:@"test_attach_multiple2"]; [channel2 attach]; - [channel1 on:^(ARTErrorInfo *errorInfo) { - if (channel1.state == ARTRealtimeChannelAttached) { + [channel1 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation1 fulfill]; } }]; - [channel2 on:^(ARTErrorInfo *errorInfo) { - if (channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation2 fulfill]; } }]; @@ -102,11 +102,11 @@ - (void)testDetach { ARTRealtimeConnectionState state = stateChange.current; if (state == ARTRealtimeConnected) { ARTRealtimeChannel *channel = [realtime.channels get:@"detach"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel detach]; } - else if(channel.state == ARTRealtimeChannelDetached) { + else if (stateChange.current == ARTRealtimeChannelDetached) { [expectation fulfill]; } }]; @@ -126,14 +126,14 @@ - (void)testDetaching { ARTRealtimeConnectionState state = stateChange.current; if (state == ARTRealtimeConnected) { ARTRealtimeChannel *channel = [realtime.channels get:@"detach"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel detach]; } - else if(channel.state == ARTRealtimeChannelDetaching) { + else if (stateChange.current == ARTRealtimeChannelDetaching) { detachingHit = YES; } - else if(channel.state == ARTRealtimeChannelDetached) { + else if (stateChange.current == ARTRealtimeChannelDetached) { if(detachingHit) { [expectation fulfill]; } @@ -154,18 +154,18 @@ - (void)testSkipsFromAttachingToDetaching { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"attaching_to_detaching"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { XCTFail(@"Should not have made it to attached"); } - else if( channel.state == ARTRealtimeChannelAttaching) { + else if (stateChange.current == ARTRealtimeChannelAttaching) { [channel detach]; } - else if(channel.state == ARTRealtimeChannelDetaching) { + else if (stateChange.current == ARTRealtimeChannelDetaching) { [channel off]; [expectation fulfill]; } - else if(channel.state == ARTRealtimeChannelDetached) { + else if (stateChange.current == ARTRealtimeChannelDetached) { XCTFail(@"Should not have made it to detached"); } @@ -184,15 +184,14 @@ -(void)testDetachingIgnoresDetach { if (state == ARTRealtimeConnected) { ARTRealtimeChannel *channel = [realtime.channels get:@"testDetachingIgnoresDetach"]; - [channel on:^(ARTErrorInfo *errorInfo) { - - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel detach]; } - if( channel.state == ARTRealtimeChannelDetaching) { + if (stateChange.current == ARTRealtimeChannelDetaching) { [channel detach]; } - if(channel.state == ARTRealtimeChannelDetached) { + if (stateChange.current == ARTRealtimeChannelDetached) { [expectation fulfill]; } }]; @@ -212,15 +211,15 @@ - (void)testAttachFailsOnFailedConnection { if (state == ARTRealtimeConnected) { ARTRealtimeChannel *channel = [realtime.channels get:@"attach"]; __block bool hasFailed = false; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { if(!hasFailed) { - XCTAssertNil(errorInfo); + XCTAssertNil(stateChange.reason); [realtime onError:[ARTTestUtil newErrorProtocolMessage]]; } } - else if(channel.state == ARTRealtimeChannelFailed) { - XCTAssertNotNil(errorInfo); + else if (stateChange.current == ARTRealtimeChannelFailed) { + XCTAssertNotNil(stateChange.reason); [channel attach:^(ARTErrorInfo *errorInfo) { XCTAssertNotNil(errorInfo); [expectation fulfill]; @@ -253,8 +252,8 @@ - (void)testAttachRestricted { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"some_unpermitted_channel"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state != ARTRealtimeChannelAttaching) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current != ARTRealtimeChannelAttaching) { XCTAssertEqual(channel.state, ARTRealtimeChannelFailed); [expectation fulfill]; [channel off]; @@ -270,8 +269,8 @@ - (void)testAttachingChannelFails { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel1 = [realtime.channels get:@"channel"]; - [channel1 on:^(ARTErrorInfo *errorInfo) { - if (channel1.state == ARTRealtimeChannelAttaching) { + [channel1 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttaching) { [realtime onError:[ARTTestUtil newErrorProtocolMessage]]; } else { @@ -289,11 +288,11 @@ - (void)testAttachedChannelFails { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel1 = [realtime.channels get:@"channel"]; - [channel1 on:^(ARTErrorInfo *errorInfo) { - if (channel1.state == ARTRealtimeChannelAttached) { + [channel1 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [realtime onError:[ARTTestUtil newErrorProtocolMessage]]; } - else if(channel1.state != ARTRealtimeChannelAttaching) { + else if (stateChange.current != ARTRealtimeChannelAttaching) { XCTAssertEqual(ARTRealtimeChannelFailed, channel1.state); [expectation fulfill]; } @@ -308,11 +307,11 @@ - (void)testChannelClosesOnClose { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel1 = [realtime.channels get:@"channel"]; - [channel1 on:^(ARTErrorInfo *errorInfo) { - if (channel1.state == ARTRealtimeChannelAttached) { + [channel1 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [realtime close]; } - else if(channel1.state != ARTRealtimeChannelAttaching) { + else if (stateChange.current != ARTRealtimeChannelAttaching) { XCTAssertEqual(ARTRealtimeChannelDetached, channel1.state); [expectation fulfill]; } diff --git a/Tests/ARTRealtimeChannelTest.m b/Tests/ARTRealtimeChannelTest.m index d094e687a..0b0668deb 100644 --- a/Tests/ARTRealtimeChannelTest.m +++ b/Tests/ARTRealtimeChannelTest.m @@ -45,8 +45,8 @@ - (void)testAttach { ARTRealtimeConnectionState state = stateChange.current; if (state == ARTRealtimeConnected) { ARTRealtimeChannel *channel = [realtime.channels get:@"attach"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation fulfill]; } }]; @@ -62,8 +62,8 @@ - (void)testAttachBeforeConnect { ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"attach_before_connect"]; [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation fulfill]; } }]; @@ -78,12 +78,12 @@ - (void)testAttachDetach { [channel attach]; __block BOOL attached = NO; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached = YES; [channel detach]; } - if (attached && channel.state == ARTRealtimeChannelDetached) { + if (attached && stateChange.current == ARTRealtimeChannelDetached) { [expectation fulfill]; } }]; @@ -98,8 +98,8 @@ - (void)testAttachDetachAttach { [channel attach]; __block BOOL attached = false; __block int attachCount = 0; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attachCount++; attached = true; if (attachCount == 1) { @@ -109,7 +109,7 @@ - (void)testAttachDetachAttach { [expectation fulfill]; } } - if (attached && channel.state == ARTRealtimeChannelDetached) { + if (attached && stateChange.current == ARTRealtimeChannelDetached) { [channel attach]; } }]; @@ -149,42 +149,16 @@ - (void)testSubscribeUnsubscribe { [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; } -- (void)testSuspendingDetachesChannel { - ARTClientOptions *options = [ARTTestUtil newSandboxApp:self withDescription:__FUNCTION__]; - __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; - ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; - ARTRealtimeChannel *channel = [realtime.channels get:@"channel"]; - __block bool gotCb=false; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { - [realtime onSuspended]; - } - else if(channel.state == ARTRealtimeChannelDetached) { - if(!gotCb) { - [channel publish:nil data:@"will_fail" callback:^(ARTErrorInfo *errorInfo) { - XCTAssertNotNil(errorInfo); - XCTAssertEqual(90001, errorInfo.code); - gotCb = true; - [realtime close]; - [expectation fulfill]; - }]; - } - } - }]; - [channel attach]; - [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; -} - - (void)testFailingFailsChannel { ARTClientOptions *options = [ARTTestUtil newSandboxApp:self withDescription:__FUNCTION__]; __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"channel"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [realtime onError:[ARTTestUtil newErrorProtocolMessage]]; } - else if(channel.state == ARTRealtimeChannelFailed) { + else if (stateChange.current == ARTRealtimeChannelFailed) { [channel publish:nil data:@"will_fail" callback:^(ARTErrorInfo *errorInfo) { XCTAssertNotNil(errorInfo); [expectation fulfill]; @@ -272,8 +246,8 @@ - (void)testAttachFails { [realtime.connection on:^(ARTConnectionStateChange *stateChange) { ARTRealtimeConnectionState state = stateChange.current; if (state == ARTRealtimeConnected) { - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [realtime onError:[ARTTestUtil newErrorProtocolMessage]]; } }]; @@ -311,15 +285,15 @@ - (void)testClientIdPreserved { __block NSUInteger attached = 0; // Channel 1 - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; // Channel 2 - [channel2 on:^(ARTErrorInfo *errorInfo) { - if (channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; diff --git a/Tests/ARTRealtimeMessageTest.m b/Tests/ARTRealtimeMessageTest.m index f063c0b52..28c41baba 100644 --- a/Tests/ARTRealtimeMessageTest.m +++ b/Tests/ARTRealtimeMessageTest.m @@ -43,8 +43,8 @@ - (void)multipleSendName:(NSString *)name count:(int)count delay:(int)delay { [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel subscribe:^(ARTMessage *message) { ++numReceived; if (numReceived == count) { @@ -94,15 +94,15 @@ - (void)testSingleSendEchoText { __block NSUInteger attached = 0; // Channel 1 - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; // Channel 2 - [channel2 on:^(ARTErrorInfo *errorInfo) { - if (channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; @@ -208,9 +208,9 @@ - (void)testSubscribeAttaches { ARTRealtimeChannel *channel = [realtime.channels get:@"testSubscribeAttaches"]; [channel subscribe:^(ARTMessage *message) { }]; - [channel on:^(ARTErrorInfo *errorInfo) { - XCTAssert(!errorInfo); - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + XCTAssert(!stateChange.reason); + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation fulfill]; } }]; @@ -310,8 +310,8 @@ - (void)testPublishImmediate { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel publish:nil data:@"testString" callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; diff --git a/Tests/ARTRealtimePresenceHistoryTest.m b/Tests/ARTRealtimePresenceHistoryTest.m index 2857a34ca..c912d0897 100644 --- a/Tests/ARTRealtimePresenceHistoryTest.m +++ b/Tests/ARTRealtimePresenceHistoryTest.m @@ -61,7 +61,7 @@ - (void)runTestLimit:(int)limit forwards:(bool)forwards callback:(void (^)(ARTPa ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:[self channelName]]; [channel attach]; - [channel once:ARTChannelEventAttached callback:^(ARTErrorInfo *errorInfo) { + [channel once:ARTChannelEventAttached callback:^(ARTChannelStateChange *stateChange) { [channel.presence enter:[self enter1Str] callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); //second enter gets treated as an update. @@ -94,8 +94,8 @@ - (void)testPresenceHistory { ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"testSimpleText"]; [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); @@ -124,8 +124,8 @@ - (void)testForward { ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"persisted:testSimpleText"]; [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter1 callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); [channel.presence enter:presenceEnter2 callback:^(ARTErrorInfo *errorInfo) { @@ -174,8 +174,8 @@ - (void)testSecondChannel { ARTRealtime *realtime2 = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime1.channels get:channelName]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { ARTRealtimeChannel *channel2 = [realtime2.channels get:channelName]; [channel2.presence enter:presenceEnter1 callback:^(ARTErrorInfo *errorInfo) { @@ -232,8 +232,8 @@ - (void)testWaitTextBackward { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter1 callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); @@ -359,8 +359,8 @@ - (void)runTestTimeForwards:(bool) forwards limit:(int) limit callback:(void (^) int secondBatchTotal = [self secondBatchSize]; int thirdBatchTotal = [self thirdBatchSize]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:[self enter1Str] callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); @@ -475,8 +475,8 @@ - (void)testFromAttach { ARTRealtime *realtime2 = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:[self channelName]]; [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:[self enter1Str] callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); [channel.presence enter:[self enter2Str] callback:^(ARTErrorInfo *errorInfo) { @@ -484,8 +484,8 @@ - (void)testFromAttach { [channel.presence update:[self updateStr] callback:^(ARTErrorInfo *errorInfo2) { XCTAssertNil(errorInfo2); ARTRealtimeChannel *channel2 = [realtime2.channels get:[self channelName]]; - [channel2 on:^(ARTErrorInfo *errorInfo) { - if(channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { ARTRealtimeHistoryQuery *query = [[ARTRealtimeHistoryQuery alloc] init]; query.direction = ARTQueryDirectionForwards; [channel2.presence history:query callback:^(ARTPaginatedResult *c2Result, ARTErrorInfo *error2) { diff --git a/Tests/ARTRealtimePresenceTest.m b/Tests/ARTRealtimePresenceTest.m index db2435951..dd762565f 100644 --- a/Tests/ARTRealtimePresenceTest.m +++ b/Tests/ARTRealtimePresenceTest.m @@ -59,14 +59,14 @@ - (void)testTwoConnections { __block NSUInteger attached = 0; // Channel 1 - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; // Channel 2 - [channel2 on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; @@ -115,8 +115,8 @@ - (void)testEnterSimple { [channel attach]; __weak XCTestExpectation *expectChannel2Connected = [self expectationWithDescription:@"presence message"]; - [channel2 on:^(ARTErrorInfo *errorInfo) { - if(channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectChannel2Connected fulfill]; } }]; @@ -161,8 +161,8 @@ - (void)testSubscribeConnects { [channel.presence subscribe:^(ARTPresenceMessage *message) { }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation fulfill]; } }]; @@ -180,8 +180,8 @@ - (void)testUpdateConnects { [channel.presence update:@"update" callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [expectation fulfill]; } }]; @@ -209,9 +209,8 @@ - (void)testEnterBeforeConnect { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) - { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -252,8 +251,8 @@ - (void)testEnterLeaveSimple { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -293,8 +292,8 @@ - (void)testEnterEnter { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -334,8 +333,8 @@ - (void)testEnterUpdateSimple { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -374,8 +373,8 @@ - (void)testUpdateNull { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -418,8 +417,8 @@ - (void)testEnterLeaveWithoutData { [channel attach]; } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence enter:presenceEnter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -452,8 +451,8 @@ - (void)testUpdateNoEnter { } }]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence update:update callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -515,8 +514,8 @@ - (void)testEnterOnDetached { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"testEnterNoClientId"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel detach]; } else if(channel.state == ARTRealtimeChannelDetached) { @@ -537,11 +536,11 @@ - (void)testEnterOnFailed { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"testEnterNoClientId"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel setFailed:[ARTStatus state:ARTStateError]]; } - else if(channel.state == ARTRealtimeChannelFailed) { + else if (stateChange.current == ARTRealtimeChannelFailed) { [channel.presence enter:@"thisWillFail" callback:^(ARTErrorInfo *errorInfo) { XCTAssertNotNil(errorInfo); [expectation fulfill]; @@ -605,8 +604,8 @@ - (void)testLeaveNoData { } }]; [channel attach]; - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel.presence update:enter callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo); }]; @@ -677,11 +676,11 @@ - (void)testLeaveOnDetached { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"testEnterNoClientId"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel detach]; } - else if(channel.state == ARTRealtimeChannelDetached) { + else if (stateChange.current == ARTRealtimeChannelDetached) { XCTAssertThrows([channel.presence leave:@"thisWillFail" callback:^(ARTErrorInfo *errorInfo) {}]); [expectation fulfill]; } @@ -697,11 +696,11 @@ - (void)testLeaveOnFailed { __weak XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"%s", __FUNCTION__]]; ARTRealtime *realtime = [[ARTRealtime alloc] initWithOptions:options]; ARTRealtimeChannel *channel = [realtime.channels get:@"testEnterNoClientId"]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel setFailed:[ARTStatus state:ARTStateError]]; } - else if(channel.state == ARTRealtimeChannelFailed) { + else if (stateChange.current == ARTRealtimeChannelFailed) { XCTAssertThrows([channel.presence leave:@"thisWillFail" callback:^(ARTErrorInfo *errorInfo) {}]); [expectation fulfill]; } @@ -1000,15 +999,15 @@ - (void)testPresenceWithDataOnLeave { __block NSUInteger attached = 0; // Channel 1 - [channel on:^(ARTErrorInfo *errorInfo) { - if (channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; // Channel 2 - [channel2 on:^(ARTErrorInfo *errorInfo) { - if (channel2.state == ARTRealtimeChannelAttached) { + [channel2 on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { attached++; } }]; diff --git a/Tests/ARTRealtimeResumeTest.m b/Tests/ARTRealtimeResumeTest.m index aa35f4a8c..3ce393627 100644 --- a/Tests/ARTRealtimeResumeTest.m +++ b/Tests/ARTRealtimeResumeTest.m @@ -45,14 +45,14 @@ - (void)testSimpleDisconnected { ARTRealtimeChannel *channel = [realtime.channels get:channelName]; ARTRealtimeChannel *channel2 = [realtime2.channels get:channelName]; - [channel on:^(ARTErrorInfo *errorInfo) { - if(channel.state == ARTRealtimeChannelAttached) { + [channel on:^(ARTChannelStateChange *stateChange) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel2 attach]; } }]; - [channel2 on:^(ARTErrorInfo *errorInfo) { + [channel2 on:^(ARTChannelStateChange *stateChange) { //both channels are attached. lets get to work. - if(channel2.state == ARTRealtimeChannelAttached) { + if (stateChange.current == ARTRealtimeChannelAttached) { [channel2 publish:nil data:message1 callback:^(ARTErrorInfo *errorInfo) { [channel2 publish:nil data:message2 callback:^(ARTErrorInfo *errorInfo) { XCTAssertNil(errorInfo);