1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 10:19:41 +02:00

Add support for "Caps Optimizations"

Smack's previous entity caps implementation assumed that an entity lost
its entity caps feature as soon as a presence without caps from that
entity was received. But according to XEP-0115 § 8.4, this is a
perfectly normal optimization technique. We now reset the caps state
after an available presence becomes unavailable.

Also introduce PresenceEventListener, which is required for this
feature.

Also make Roster.preApprove() take a BareJid as argument.

Fixes SMACK-723.
This commit is contained in:
Florian Schmaus 2016-06-30 17:01:46 +02:00
parent 4248fbbb89
commit d07ed60737
8 changed files with 280 additions and 22 deletions

View file

@ -35,10 +35,10 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
*/
protected final XMPPConnection connection;
protected final long defaultTimeout;
protected final String testRunId;
protected final long defaultTimeout;
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;

View file

@ -0,0 +1,73 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.roster;
import java.util.concurrent.TimeoutException;
import org.igniterealtime.smack.inttest.AbstractSmackLowLevelIntegrationTest;
import org.igniterealtime.smack.inttest.Configuration;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jxmpp.jid.FullJid;
public class LowLevelRosterIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
public LowLevelRosterIntegrationTest(Configuration configuration, String testRunId) {
super(configuration, testRunId);
}
@SmackIntegrationTest
public void testPresenceEventListenersOffline(final XMPPTCPConnection conOne, final XMPPTCPConnection conTwo) throws TimeoutException, Exception {
RosterIntegrationTest.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
final Roster rosterOne = Roster.getInstanceFor(conOne);
final Roster rosterTwo = Roster.getInstanceFor(conTwo);
// TODO create Roster.createEntry() with boolean flag for subscribe or not.
rosterOne.createEntry(conTwo.getUser().asBareJid(), "Con Two", null);
rosterTwo.createEntry(conOne.getUser().asBareJid(), "Con One", null);
// TODO Change timeout form '5000' to something configurable.
final long timeout = 5000;
RosterIntegrationTest.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
final SimpleResultSyncPoint offlineTriggered = new SimpleResultSyncPoint();
rosterOne.addPresenceEventListener(new AbstractPresenceEventListener() {
@Override
public void presenceUnavailable(FullJid jid, Presence presence) {
if (!jid.equals(conTwo.getUser())) {
return;
}
offlineTriggered.signal();
}
});
// Disconnect conTwo, this should cause an 'unavilable' presence to be send from conTwo to
// conOne.
conTwo.disconnect();
Boolean result = offlineTriggered.waitForResult(timeout);
if (!result) {
throw new Exception("presenceUnavailable() was not called");
}
}
}

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2015 Florian Schmaus
* Copyright 2015-2016 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +32,7 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.packet.RosterPacket.ItemType;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.Jid;
public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
@ -47,7 +48,7 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
@SmackIntegrationTest
public void subscribeRequestListenerTest() throws TimeoutException, Exception {
ensureBothAccountsAreNotInEachOthersRoster();
ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
rosterTwo.setSubscribeListener(new SubscribeListener() {
@Override
@ -92,14 +93,14 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
assertTrue(addedAndSubscribed.waitForResult(2 * connection.getPacketReplyTimeout()));
}
private void ensureBothAccountsAreNotInEachOthersRoster() throws NotLoggedInException,
public static void ensureBothAccountsAreNotInEachOthersRoster(XMPPConnection conOne, XMPPConnection conTwo) throws NotLoggedInException,
NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException {
notInRoster(conOne, conTwo);
notInRoster(conTwo, conOne);
}
private void notInRoster(XMPPConnection c1, XMPPConnection c2) throws NotLoggedInException,
private static void notInRoster(XMPPConnection c1, XMPPConnection c2) throws NotLoggedInException,
NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException {
Roster roster = Roster.getInstanceFor(c1);
@ -109,4 +110,46 @@ public class RosterIntegrationTest extends AbstractSmackIntegrationTest {
}
roster.removeEntry(c2Entry);
}
public static void ensureBothAccountsAreSubscribedToEachOther(XMPPConnection conOne, XMPPConnection conTwo, long timeout) throws TimeoutException, Exception {
ensureSubscribedTo(conOne, conTwo, timeout);
ensureSubscribedTo(conTwo, conOne, timeout);
}
private static void ensureSubscribedTo(final XMPPConnection conOne, final XMPPConnection conTwo, long timeout) throws TimeoutException, Exception {
Roster rosterOne = Roster.getInstanceFor(conOne);
Roster rosterTwo = Roster.getInstanceFor(conTwo);
if (rosterOne.isSubscribedToMyPresence(conTwo.getUser())) {
return;
}
rosterOne.setSubscribeListener(new SubscribeListener() {
@Override
public SubscribeAnswer processSubscribe(Jid from, Presence subscribeRequest) {
if (from.equals(conTwo.getUser().asBareJid())) {
return SubscribeAnswer.Approve;
}
return SubscribeAnswer.Deny;
}
});
final SimpleResultSyncPoint syncPoint = new SimpleResultSyncPoint();
rosterTwo.addPresenceEventListener(new AbstractPresenceEventListener() {
@Override
public void presenceSubscribed(BareJid address, Presence subscribedPresence) {
if (!address.equals(conOne.getUser().asBareJid())) {
return;
}
syncPoint.signal();
}
});
rosterTwo.sendSubscriptionRequest(conOne.getUser().asBareJid());
try {
syncPoint.waitForResult(timeout);
} finally {
rosterOne.setSubscribeListener(null);
}
}
}