diff --git a/Test/Tests/RealtimeClientPresenceTests.swift b/Test/Tests/RealtimeClientPresenceTests.swift index f29a037f4..9a3a4312d 100644 --- a/Test/Tests/RealtimeClientPresenceTests.swift +++ b/Test/Tests/RealtimeClientPresenceTests.swift @@ -745,86 +745,106 @@ class RealtimeClientPresenceTests: XCTestCase { } } - func test__023__Presence__Channel_state_change_side_effects__channel_enters_the_SUSPENDED_state__should_maintain_the_PresenceMap_and_any_members_present_before_and_after_the_sync_should_not_emit_presence_events() throws { + func test__023__Presence__Channel_state_change_side_effects__channel_enters_the_SUSPENDED_state__members_map_is_preserved_and_only_members_that_changed_between_ATTACHED_states_should_result_in_presence_events() throws { let test = Test() let options = try AblyTests.commonAppSetup(for: test) let channelName = test.uniqueChannelName() - var clientMembers: ARTRealtime? - clientMembers = AblyTests.addMembersSequentiallyToChannel(channelName, members: 3, options: options) - defer { clientMembers?.dispose(); clientMembers?.close() } - - options.clientId = "tester" + let clientMembers = AblyTests.addMembersSequentiallyToChannel(channelName, members: 2, options: options) + defer { clientMembers.dispose(); clientMembers.close() } + + options.clientId = "leaves" + let leavesClient = AblyTests.newRealtime(options).client + defer { leavesClient.dispose(); leavesClient.close() } + + options.clientId = "main" options.tokenDetails = try getTestTokenDetails(for: test, key: options.key!, clientId: options.clientId, ttl: 5.0) - let client = AblyTests.newRealtime(options).client - defer { client.dispose(); client.close() } - let channel = client.channels.get(channelName) - waitUntil(timeout: testTimeout) { done in - let partialDone = AblyTests.splitDone(3, done: done) - channel.presence.enter(nil) { error in + let mainClient = AblyTests.newRealtime(options).client + defer { mainClient.dispose(); mainClient.close() } + + let leavesChannel = leavesClient.channels.get(channelName) + let mainChannel = mainClient.channels.get(channelName) + + waitUntil(timeout: testTimeout.multiplied(by: 9)) { done in + let partialDone = AblyTests.splitDone(5, done: done) + mainChannel.presence.enter(nil) { error in XCTAssertNil(error) partialDone() } - channel.presence.get { members, error in + leavesChannel.presence.enter(nil) { error in + XCTAssertNil(error) + partialDone() + } + mainChannel.presence.get { members, error in XCTAssertNil(error) if let members { - XCTAssertEqual(members.count, 3) + XCTAssertEqual(members.count, 3) // "user1", "user2", "leaves" } else { XCTFail("Expected members to be non-nil") } partialDone() } - channel.presence.subscribe(.enter) { message in - if message.clientId == "tester" { - channel.presence.unsubscribe() + mainChannel.presence.subscribe { message in + if message.clientId == "main" { + XCTAssertEqual(message.action, ARTPresenceAction.enter) + mainChannel.presence.unsubscribe() + partialDone() + } + else if message.clientId == "leaves" { + XCTAssertEqual(message.action, ARTPresenceAction.present) // .enter was replaced by .present (RTP2d) partialDone() } } } + var presenceEvents = [ARTPresenceMessage]() waitUntil(timeout: testTimeout) { done in let partialDone = AblyTests.splitDone(4, done: done) - channel.presence.subscribe { presence in - XCTAssertEqual(presence.action, ARTPresenceAction.leave) - XCTAssertEqual(presence.clientId, "tester") - partialDone() - } - channel.once(.suspended) { _ in - channel.internalSync { _internal in - XCTAssertEqual(_internal.presenceMap.members.count, 4) - XCTAssertEqual(_internal.presenceMap.localMembers.count, 1) + mainChannel.presence.subscribe { presence in + presenceEvents += [presence] + delay(1) { + partialDone() // Wait a bit to make sure we don't receive any other presence messages } - partialDone() } - channel.once(.attached) { stateChange in - XCTAssertNil(stateChange.reason) - channel.presence.leave(nil) { error in + mainChannel.once(.suspended) { _ in + mainChannel.internalSync { _internal in + XCTAssertEqual(_internal.presenceMap.members.count, 4) // "main", "user1", "user2", "leaves" + XCTAssertEqual(_internal.presenceMap.localMembers.count, 1) // "main" + } + leavesChannel.presence.leave(nil) { error in XCTAssertNil(error) partialDone() } partialDone() } - channel.internalAsync { _internal in + mainChannel.once(.attached) { stateChange in + XCTAssertNil(stateChange.reason) + partialDone() + } + mainChannel.internalAsync { _internal in _internal.setSuspended(.init(state: .ok)) } } + XCTAssertEqual(presenceEvents.count, 1) + XCTAssertEqual(presenceEvents[0].action, ARTPresenceAction.leave) + XCTAssertEqual(presenceEvents[0].clientId, "leaves") - channel.presence.unsubscribe() + mainChannel.presence.unsubscribe() waitUntil(timeout: testTimeout) { done in - channel.presence.get { members, error in + mainChannel.presence.get { members, error in XCTAssertNil(error) guard let members = members else { fail("Members is nil"); done(); return } - XCTAssertEqual(members.count, 3) + XCTAssertEqual(members.count, 3) // "main", "user1", "user2" expect(members).to(allPass { (member: ARTPresenceMessage?) in member!.action != .absent }) done() } } - channel.internalSync { _internal in - XCTAssertEqual(_internal.presenceMap.members.count, 3) - expect(_internal.presenceMap.localMembers).to(beEmpty()) + mainChannel.internalSync { _internal in + XCTAssertEqual(_internal.presenceMap.members.count, 3) // "main", "user1", "user2" + XCTAssertEqual(_internal.presenceMap.localMembers.count, 1) // "main" } }