Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTP2 #311

Merged
merged 8 commits into from
Apr 5, 2016
Merged

RTP2 #311

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/ARTBaseMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ART_ASSUME_NONNULL_BEGIN
@property (strong, nonatomic) NSString *id;

/// The timestamp for this message
@property (strong, nonatomic) NSDate *timestamp;
@property (strong, nonatomic, art_nullable) NSDate *timestamp;

/// The id of the publisher of this message
@property (strong, nonatomic, art_nullable) NSString *clientId;
Expand Down
2 changes: 1 addition & 1 deletion Source/ARTPresenceMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ - (id)init {

- (void)put:(ARTPresenceMessage *)message {
ARTPresenceMessage *latest = [self.recentMembers objectForKey:message.clientId];
if (!latest || !message.timestamp || latest.timestamp < message.timestamp) {
if (!latest || !message.timestamp || [latest.timestamp timeIntervalSince1970] <= [message.timestamp timeIntervalSince1970]) {
[self.recentMembers setObject:message forKey:message.clientId];
}
}
Expand Down
11 changes: 7 additions & 4 deletions Source/ARTRealtimeChannel.m
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,13 @@ - (void)onPresence:(ARTProtocolMessage *)message {
presence.id = [NSString stringWithFormat:@"%@:%d", message.id, i];
}

[self.presenceMap put:[message.presence objectAtIndex:i]];
[self.presenceMap clean];
[self broadcastPresence:presence];

[self.presenceMap onceSyncEnds:^(__GENERIC(NSArray, ARTPresenceMessage *) *msgs) {
[self.presenceMap put:presence];
[self.presenceMap clean];

[self broadcastPresence:presence];
}];

++i;
}
}
Expand Down
3 changes: 3 additions & 0 deletions Spec/NSObject+TestSuite.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ ART_ASSUME_NONNULL_BEGIN
/// Get each return value generated by the identified instance method.
- (void)testSuite_getReturnValueFrom:(SEL)selector callback:(void (^)(id))callback;

/// Get argument at index from the identified instance method.
- (void)testSuite_getArgumentFrom:(SEL)selector atIndex:(NSInteger)index callback:(void (^)(id))callback;

/// Inject a block of code to the identified instance method.
- (void)testSuite_injectIntoMethod:(SEL)selector code:(void (^)(void))block;

Expand Down
8 changes: 8 additions & 0 deletions Spec/NSObject+TestSuite.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ - (void)testSuite_getReturnValueFrom:(SEL)selector callback:(void (^)(id))callba
} error:nil];
}

- (void)testSuite_getArgumentFrom:(SEL)selector atIndex:(NSInteger)index callback:(void (^)(id))callback {
[self aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info) {
__autoreleasing id arg;
[[info originalInvocation] getArgument:&arg atIndex:2+index];
callback([arg copy]);
} error:nil];
}

- (void)testSuite_injectIntoMethod:(SEL)selector code:(void (^)(void))block {
[self aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info) {
block();
Expand Down
58 changes: 58 additions & 0 deletions Spec/RealtimeClientPresence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,64 @@ class RealtimeClientPresence: QuickSpec {

}

// RTP2
pending("should be used a PresenceMap to maintain a list of members") {
let options = AblyTests.commonAppSetup()
var clientSecondary: ARTRealtime!
defer { clientSecondary.close() }

waitUntil(timeout: testTimeout) { done in
clientSecondary = AblyTests.addMembersSequentiallyToChannel("test", members: 100, options: options) {
done()
}.first
}

let client = AblyTests.newRealtime(options)
defer { client.close() }
let channel = client.channels.get("test")

var user50LeaveTimestamp: NSDate?
channel.presence.subscribe(.Leave) { member in
expect(member.clientId).to(equal("user50"))
user50LeaveTimestamp = member.timestamp
}

var user50PresentTimestamp: NSDate?
channel.presenceMap.testSuite_getArgumentFrom("put:", atIndex: 0) { arg0 in
let member = arg0 as! ARTPresenceMessage
if member.clientId == "user50" && member.action == .Present {
user50PresentTimestamp = member.timestamp
}
}

waitUntil(timeout: testTimeout) { done in
channel.attach() { _ in
let transport = client.transport as! TestProxyTransport
transport.beforeProcessingReceivedMessage = { protocolMessage in
// A leave event for a member can arrive before that member is later registered as present as part of the initial SYNC operation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get how is this being enforced in the test. How do you know that the leave event for user50 is arriving to client before its enter event? The only thing I see here is that clientSecondary is leaving user50, but that event can reach client after the SYNC is complete, as far as I can tell.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I think the only viable way to test this is to send a mock protocol message to the library mid sync before user50 has been received simulating that message being received.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL 9d80ec2.

if protocolMessage.action == .Sync {
let msg = AblyTests.newPresenceProtocolMessage("test", action: .Leave, clientId: "user50")
// Ensure it happens "later" than the PRESENT message.
msg.timestamp = NSDate().dateByAddingTimeInterval(1.0)
client.onChannelMessage(msg)
done()
}
}
}
}

waitUntil(timeout: testTimeout) { done in
channel.presence.get { members, error in
expect(error).to(beNil())
expect(members).to(haveCount(99))
expect(members!.filter{ $0.clientId == "user50" }).to(haveCount(0))
done()
}
}

expect(user50LeaveTimestamp).to(beGreaterThan(user50PresentTimestamp))
}

// RTP15e
let cases: [String:(ARTRealtimePresence, Optional<(ARTErrorInfo?)->Void>)->()] = [
"enterClient": { $0.enterClient("john", data: nil, callback: $1) },
Expand Down
36 changes: 36 additions & 0 deletions Spec/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ class AblyTests {
return protocolMessage
}

class func newPresenceProtocolMessage(channel: String, action: ARTPresenceAction, clientId: String) -> ARTProtocolMessage {
let protocolMessage = ARTProtocolMessage()
protocolMessage.action = .Presence
protocolMessage.channel = channel
protocolMessage.timestamp = NSDate()
let presenceMessage = ARTPresenceMessage()
presenceMessage.action = action
presenceMessage.clientId = clientId
presenceMessage.timestamp = NSDate()
protocolMessage.presence = [presenceMessage]
return protocolMessage
}

class func newRealtime(options: ARTClientOptions) -> ARTRealtime {
options.autoConnect = false
let realtime = ARTRealtime(options: options)
Expand Down Expand Up @@ -582,6 +595,29 @@ extension JSON {

}


extension NSDate: Comparable { }

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return (lhs.compare(rhs) == .OrderedSame)
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return (lhs.compare(rhs) == .OrderedAscending)
}

public func >(lhs: NSDate, rhs: NSDate) -> Bool {
return (lhs.compare(rhs) == .OrderedDescending)
}

public func <=(lhs: NSDate, rhs: NSDate) -> Bool {
return (lhs < rhs || lhs == rhs)
}

public func >=(lhs: NSDate, rhs: NSDate) -> Bool {
return (lhs > rhs || lhs == rhs)
}

extension NSRegularExpression {

class func match(value: String?, pattern: String) -> Bool {
Expand Down