package com.limegroup.gnutella.dht.db;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Invocation;
import org.jmock.lib.action.CustomAction;
import org.limewire.core.settings.DHTSettings;
import org.limewire.io.Connectable;
import org.limewire.io.ConnectableImpl;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.io.IpPortSet;
import org.limewire.io.NetworkUtils;
import org.limewire.mojito.KUID;
import org.limewire.mojito.MojitoDHT;
import org.limewire.mojito.db.DHTValue;
import org.limewire.mojito.settings.DatabaseSettings;
import org.limewire.util.PrivilegedAccessor;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.limegroup.gnutella.ConnectionManager;
import com.limegroup.gnutella.LimeTestUtils;
import com.limegroup.gnutella.NetworkManager;
import com.limegroup.gnutella.dht.DHTControllerStub;
import com.limegroup.gnutella.dht.DHTEvent;
import com.limegroup.gnutella.dht.DHTManager;
import com.limegroup.gnutella.dht.NullDHTController;
import com.limegroup.gnutella.dht.DHTEvent.Type;
import com.limegroup.gnutella.dht.DHTManager.DHTMode;
import com.limegroup.gnutella.stubs.NetworkManagerStub;
import com.limegroup.gnutella.util.LimeTestCase;
public class PushProxiesPublisherTest extends LimeTestCase {
private NetworkManagerStub networkManagerStub;
private PushProxiesPublisher pushProxiesPublisher;
private Mockery context;
private Runnable publishingRunnable;
private ConnectionManager connectionManager;
public PushProxiesPublisherTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(PushProxiesPublisherTest.class);
}
@Override
protected void setUp() throws Exception {
context = new Mockery();
connectionManager = context.mock(ConnectionManager.class);
networkManagerStub = new NetworkManagerStub();
Injector injector = LimeTestUtils.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(NetworkManager.class).toInstance(networkManagerStub);
bind(ConnectionManager.class).toInstance(connectionManager);
}
});
pushProxiesPublisher = injector.getInstance(PushProxiesPublisher.class);
}
/**
* An integration test that makes sure we create a storable for a
* non-firewalled peer.
*/
public void testValueToPublishForNonFirewalledPeer() {
networkManagerStub.setAcceptedIncomingConnection(true);
assertTrue(NetworkUtils.isValidIpPort(new IpPortImpl(networkManagerStub.getAddress(), networkManagerStub.getPort())));
PushProxiesValue value = pushProxiesPublisher.getValueToPublish();
assertNull("First value should be null since not stable", value);
value = pushProxiesPublisher.getValueToPublish();
Connectable expected = new ConnectableImpl(new IpPortImpl(networkManagerStub.getAddress(), networkManagerStub.getPort()), networkManagerStub.isOutgoingTLSEnabled());
assertEquals(0, IpPort.IP_COMPARATOR.compare(expected, value.getPushProxies().iterator().next()));
}
/**
* When the set of push proxies or the fwt capability of this peer change it
* should publish its updated info into the DHT the next time the StorableModel
* is asked for them.
*/
public void testValueToPublishWhenProxiesValueHasChanged() throws Exception {
networkManagerStub.setAcceptedIncomingConnection(true);
networkManagerStub.setSupportsFWTVersion(1);
PushProxiesValue value = pushProxiesPublisher.getValueToPublish();
assertNull("First value should be null, since not considered stable", value);
value = pushProxiesPublisher.getValueToPublish();
Connectable expected = new ConnectableImpl(new IpPortImpl(networkManagerStub.getAddress(), networkManagerStub.getPort()), networkManagerStub.isOutgoingTLSEnabled());
assertEquals(0, IpPort.IP_COMPARATOR.compare(expected, value.getPushProxies().iterator().next()));
assertEquals(1, value.getFwtVersion());
// change fwt support status so we should get a different pushproxy value
// for ourselves that needs to be republished
networkManagerStub.setSupportsFWTVersion(2);
value = pushProxiesPublisher.getValueToPublish();
assertNull("first value after change should be null, since not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertEquals(2, value.getFwtVersion());
}
public void testValueToPublishDoesNotChangeWhenPushProxiesChange() throws Exception {
final IpPort proxy1 = new IpPortImpl("199.49.4.4", 4545);
final IpPort proxy2 = new IpPortImpl("205.2.1.1", 1000);
final IpPort proxy3 = new IpPortImpl("111.34.4.4", 1010);
final IpPort proxy4 = new IpPortImpl("111.34.4.4", 2020);
final IpPortSet proxies = new IpPortSet(proxy1, proxy2, proxy3);
context.checking(new Expectations() {{
allowing(connectionManager).getPushProxies();
will(returnValue(proxies));
}});
assertFalse(networkManagerStub.acceptedIncomingConnection());
PushProxiesValue value = pushProxiesPublisher.getValueToPublish();
assertNull("First value should be null, since not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertEquals(new IpPortSet(proxy1, proxy2, proxy3), value.getPushProxies());
// one new proxy, two old proxies
proxies.remove(proxy3);
proxies.add(proxy4);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should be null, since changed and not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should still be null, since mostly the same proxies", value);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should still be null, since mostly the same proxies", value);
// only one old proxy
proxies.remove(proxy2);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should be null, since changed and not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertEquals("new proxies should be published", new IpPortSet(proxy1, proxy4), value.getPushProxies());
value = pushProxiesPublisher.getValueToPublish();
assertNull("no changes, should be null", value);
}
public void testValueIsPublishedAfterValuesExpire() throws Exception {
final IpPort proxy1 = new IpPortImpl("199.49.4.4", 4545);
final IpPort proxy2 = new IpPortImpl("205.2.1.1", 1000);
final IpPort proxy3 = new IpPortImpl("111.34.4.4", 1010);
final IpPortSet proxies = new IpPortSet(proxy1, proxy2, proxy3);
context.checking(new Expectations() {{
allowing(connectionManager).getPushProxies();
will(returnValue(proxies));
}});
PrivilegedAccessor.setValue(DatabaseSettings.VALUE_REPUBLISH_INTERVAL, "value", 100);
PushProxiesValue value = pushProxiesPublisher.getValueToPublish();
assertNull("First value should be null, since not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertEquals(new IpPortSet(proxy1, proxy2, proxy3), value.getPushProxies());
value = pushProxiesPublisher.getValueToPublish();
assertNull("Value should be null, since just published and not changed", value);
Thread.sleep(200);
value = pushProxiesPublisher.getValueToPublish();
assertEquals("should not be null, should have been republished due to timeout",
new IpPortSet(proxy1, proxy2, proxy3), value.getPushProxies());
value = pushProxiesPublisher.getValueToPublish();
assertNull("Value should be null, since just published and not changed", value);
}
public void testValueChangingInBetweenConsecutiveCallsToGetValueToPublish() {
networkManagerStub.setAcceptedIncomingConnection(true);
PushProxiesValue value = pushProxiesPublisher.getValueToPublish();
assertNull("value should be null, since not stable as first value", value);
networkManagerStub.setPort(54545);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should be null, since push proxies should have changed and not stable", value);
networkManagerStub.setCanDoFWT(true);
networkManagerStub.setSupportsFWTVersion(2);
value = pushProxiesPublisher.getValueToPublish();
assertNull("value should be null, since push proxies should have changed and not stable", value);
value = pushProxiesPublisher.getValueToPublish();
assertNotNull("value should not be null, since stable now", value);
}
public void testDHTEventHandlerIsInstalledAndTriggersRunnable() {
networkManagerStub.setAcceptedIncomingConnection(true);
networkManagerStub.setSupportsFWTVersion(1);
final MojitoDHT mojitoDHT = context.mock(MojitoDHT.class);
final ScheduledExecutorService executorService = context.mock(ScheduledExecutorService.class);
final ScheduledFuture future = context.mock(ScheduledFuture.class);
final DHTManager dhtManager = context.mock(DHTManager.class);
context.checking(new Expectations() {{
one(executorService).scheduleAtFixedRate(with(any(Runnable.class)),
with(any(Long.class)),
with(equal(DHTSettings.PUSH_PROXY_STABLE_PUBLISHING_INTERVAL.getValue())),
with(equal(TimeUnit.MILLISECONDS)));
will(new CustomAction("save runnable and return future action") {
public Object invoke(Invocation invocation) throws Throwable {
publishingRunnable = (Runnable)invocation.getParameter(0);
return future;
}
});
}});
Injector injector = LimeTestUtils.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(NetworkManager.class).toInstance(networkManagerStub);
// not binding mocked executor, since it is used in all kinds of places
}
});
DHTSettings.PUBLISH_PUSH_PROXIES.setValue(true);
pushProxiesPublisher = new PushProxiesPublisher(injector.getInstance(PushProxiesValueFactory.class), executorService, dhtManager);
pushProxiesPublisher.handleDHTEvent(new DHTEvent(new DHTControllerStub(mojitoDHT, DHTMode.PASSIVE), Type.CONNECTED));
assertNotNull(publishingRunnable);
context.assertIsSatisfied();
// test publishing
context.checking(new Expectations() {{
one(dhtManager).put(with(any(KUID.class)), with(any(DHTValue.class)));
}});
publishingRunnable.run();
// publishing only takes place the second time around
publishingRunnable.run();
context.assertIsSatisfied();
context.checking(new Expectations() {{
one(future).cancel(false);
}});
pushProxiesPublisher.handleDHTEvent(new DHTEvent(new NullDHTController(), Type.STOPPED));
context.assertIsSatisfied();
}
}