package com.limegroup.gnutella.downloader;
import junit.framework.Test;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.limewire.core.settings.DHTSettings;
import org.limewire.io.GUID;
import org.limewire.mojito.settings.LookupSettings;
import org.limewire.nio.observer.Shutdownable;
import org.limewire.util.PrivilegedAccessor;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.limegroup.gnutella.DownloadManager;
import com.limegroup.gnutella.LimeTestUtils;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.altlocs.AlternateLocation;
import com.limegroup.gnutella.dht.DHTEvent;
import com.limegroup.gnutella.dht.DHTEventListener;
import com.limegroup.gnutella.dht.DHTManager;
import com.limegroup.gnutella.dht.DHTManagerStub;
import com.limegroup.gnutella.dht.NullDHTController;
import com.limegroup.gnutella.dht.db.AltLocFinder;
import com.limegroup.gnutella.dht.db.SearchListener;
import com.limegroup.gnutella.downloader.RequeryManager.QueryType;
import com.limegroup.gnutella.messages.QueryRequest;
import com.limegroup.gnutella.util.LimeTestCase;
import com.limegroup.gnutella.util.LimeWireUtils;
public class RequeryManagerTest extends LimeTestCase {
public RequeryManagerTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(RequeryManagerTest.class);
}
private RequeryManagerFactory requeryManagerFactory;
private MyDHTManager dhtManager;
private RequeryListener requeryListener;
private DownloadManager downloadManager;
private MyAltLocFinder altLocFinder;
private Mockery mockery;
private Sequence sequence;
private URN sha1Urn;
@Override
public void setUp() throws Exception {
DHTSettings.ENABLE_DHT_ALT_LOC_QUERIES.setValue(true);
RequeryManager.NO_DELAY = true;
mockery = new Mockery();
requeryListener = mockery.mock(RequeryListener.class);
downloadManager = mockery.mock(DownloadManager.class);
sequence = mockery.sequence("Sequence");
Injector injector = LimeTestUtils.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(DHTManager.class).to(MyDHTManager.class);
bind(DownloadManager.class).toInstance(downloadManager);
bind(AltLocFinder.class).to(MyAltLocFinder.class);
}
});
dhtManager = (MyDHTManager)injector.getInstance(DHTManager.class);
altLocFinder = (MyAltLocFinder)injector.getInstance(AltLocFinder.class);
requeryManagerFactory = injector.getInstance(RequeryManagerFactory.class);
sha1Urn = URN.createSHA1Urn("urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
setPro(false);
}
@Override
protected void tearDown() throws Exception {
mockery.assertIsSatisfied();
}
private void setPro(boolean pro) throws Exception {
PrivilegedAccessor.setValue(LimeWireUtils.class, "_isPro", pro);
assertEquals(pro, LimeWireUtils.isPro());
}
public void testRegistersWithDHTManager() throws Exception {
assertNull(dhtManager.listener);
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
assertSame(requeryManager, dhtManager.listener);
requeryManager.cleanUp();
assertNull(dhtManager.listener);
}
public void testCancelsQueries() throws Exception {
dhtManager.on = true;
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.activate();
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
assertTrue(requeryManager.isWaitingForResults());
assertFalse(altLocFinder.cancelled);
// We're stubbing out the Shutdownable, so it doesn't actually notify us it finished..
// but we do check to make sure it was cancelled.
// mockery.checking(new Expectations() {{
// one(requeryListener).lookupFinished(QueryType.DHT);
// }});
requeryManager.cleanUp();
assertTrue(altLocFinder.cancelled);
}
public void testNotInitedDoesNothingBasic() throws Exception {
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
// shouldn't trigger any queries
dhtManager.on = true;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
requeryManager.sendQuery();
assertNull(altLocFinder.listener);
}
public void testNotInitedAutoDHTPro() throws Exception {
DHTSettings.MAX_DHT_ALT_LOC_QUERY_ATTEMPTS.setValue(2);
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
setPro(true);
// if dht is off do nothing
dhtManager.on = false;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
requeryManager.sendQuery();
assertNull(altLocFinder.listener);
// if dht is on start querying
dhtManager.on = true;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
// But immediately after, requires an activate (for gnet query)
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// but if some time passes, a dht query will work again.
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleAltLocSearchDone(false);
PrivilegedAccessor.setValue(requeryManager, "lastQuerySent", 1);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
// and we should start a lookup
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// make sure after that lookup finishes, can still do gnet
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleAltLocSearchDone(false);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// some time passes, but we've hit our dht queries limit
// can still send gnet though
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleAltLocSearchDone(false);
PrivilegedAccessor.setValue(requeryManager, "lastQuerySent", 1);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
/**
* tests that only a single gnet query is sent if dht is off.
*/
public void testOnlyGnetIfNoDHT() throws Exception {
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
dhtManager.on = false;
assertNull(altLocFinder.listener);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
requeryManager.activate();
assertTrue(requeryManager.canSendQueryNow());
final QueryRequest queryRequest = mockery.mock(QueryRequest.class);
// first try a requery that will not work
mockery.checking(new Expectations() {{
one(requeryListener).createQuery(); inSequence(sequence);
will(returnValue(queryRequest));
one(downloadManager).sendQuery(with(same(queryRequest))); inSequence(sequence);
one(requeryListener).lookupStarted(QueryType.GNUTELLA, RequeryManager.TIME_BETWEEN_REQUERIES); inSequence(sequence);
}});
requeryManager.sendQuery();
assertNull(altLocFinder.listener); // should not try dht
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// but if we try again, nothing happens.
requeryManager.sendQuery();
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
public void testWaitsForStableConns() throws Exception {
// no DHT nor connections
dhtManager.on = false;
RequeryManager.NO_DELAY = false;
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
requeryManager.activate();
mockery.checking(new Expectations() {{
one(requeryListener).lookupPending(QueryType.GNUTELLA, 750); inSequence(sequence);
}});
requeryManager.sendQuery();
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
// now we get connected
RequeryManager.NO_DELAY = true;
final QueryRequest queryRequest = mockery.mock(QueryRequest.class);
mockery.checking(new Expectations() {{
one(requeryListener).createQuery(); inSequence(sequence);
will(returnValue(queryRequest));
one(downloadManager).sendQuery(with(same(queryRequest))); inSequence(sequence);
one(requeryListener).lookupStarted(QueryType.GNUTELLA, RequeryManager.TIME_BETWEEN_REQUERIES); inSequence(sequence);
}});
requeryManager.sendQuery();
// should be sent.
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
public void testDHTTurnsOnStartsAutoIfInited() throws Exception {
DHTSettings.MAX_DHT_ALT_LOC_QUERY_ATTEMPTS.setValue(2);
// with dht off, send a query
dhtManager.on = false;
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
final QueryRequest queryRequest = mockery.mock(QueryRequest.class);
// first try a requery that will not work
mockery.checking(new Expectations() {{
one(requeryListener).createQuery(); inSequence(sequence);
will(returnValue(queryRequest));
one(downloadManager).sendQuery(with(same(queryRequest))); inSequence(sequence);
one(requeryListener).lookupStarted(QueryType.GNUTELLA, RequeryManager.TIME_BETWEEN_REQUERIES); inSequence(sequence);
}});
requeryManager.activate();
requeryManager.sendQuery();
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// query fails, dht still off
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
requeryManager.sendQuery();
assertNull(altLocFinder.listener);
// turn the dht on should immediately query
// even though the gnet query happened recently
dhtManager.on = true;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
// and we should start a lookup
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// make sure after that lookup finishes, we still can't do gnet
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleAltLocSearchDone(false);
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// some time passes, can send one more dht query
altLocFinder.listener = null;
PrivilegedAccessor.setValue(requeryManager, "lastQuerySent", 1);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// more time passes, but we hit our dht query limit so we can't do anything.
altLocFinder.listener = null;
PrivilegedAccessor.setValue(requeryManager, "lastQuerySent", 1);
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
public void testGnetFollowsDHT() throws Exception {
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
dhtManager.on = true;
requeryManager.activate();
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
// with dht on, start a requery
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener);
assertTrue(requeryManager.isWaitingForResults());
// pretend the dht lookup fails
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT);
}});
requeryManager.handleAltLocSearchDone(false);
assertFalse(requeryManager.isWaitingForResults());
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
// the next requery should be gnet
altLocFinder.listener = null;
final QueryRequest queryRequest = mockery.mock(QueryRequest.class);
// first try a requery that will not work
mockery.checking(new Expectations() {{
one(requeryListener).createQuery(); inSequence(sequence);
will(returnValue(queryRequest));
one(downloadManager).sendQuery(with(same(queryRequest))); inSequence(sequence);
one(requeryListener).lookupStarted(QueryType.GNUTELLA, RequeryManager.TIME_BETWEEN_REQUERIES); inSequence(sequence);
}});
requeryManager.sendQuery();
assertTrue(requeryManager.isWaitingForResults());
assertNull(altLocFinder.listener);
// from now on we should give up & no more requeries
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
public void testGnetFollowsDHTPro() throws Exception {
setPro(true);
testGnetFollowsDHT();
}
public void testOnlyGnetPro() throws Exception {
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
dhtManager.on = true;
setPro(true);
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener); // sent a DHT query
assertTrue(requeryManager.isWaitingForResults());
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleAltLocSearchDone(false); // finish it
assertFalse(requeryManager.isWaitingForResults());
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
requeryManager.activate(); // now activate it.
altLocFinder.listener = null;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
final QueryRequest queryRequest = mockery.mock(QueryRequest.class);
// first try a requery that will not work
mockery.checking(new Expectations() {{
one(requeryListener).createQuery(); inSequence(sequence);
will(returnValue(queryRequest));
one(downloadManager).sendQuery(with(same(queryRequest))); inSequence(sequence);
one(requeryListener).lookupStarted(QueryType.GNUTELLA, RequeryManager.TIME_BETWEEN_REQUERIES); inSequence(sequence);
}});
requeryManager.sendQuery();
assertTrue(requeryManager.isWaitingForResults());
assertNull(altLocFinder.listener);
assertFalse(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
}
public void testDHTTurnsOff() throws Exception {
RequeryManager requeryManager = requeryManagerFactory.createRequeryManager(requeryListener);
dhtManager.on = true;
setPro(true); // so we immediately launch a query
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener); // sent a DHT query
assertTrue(requeryManager.isWaitingForResults());
// now turn the dht off
dhtManager.on = false;
mockery.checking(new Expectations() {{
one(requeryListener).lookupFinished(QueryType.DHT); inSequence(sequence);
}});
requeryManager.handleDHTEvent(new DHTEvent(new NullDHTController(), DHTEvent.Type.STOPPED));
assertFalse(requeryManager.isWaitingForResults());
assertTrue(requeryManager.canSendQueryAfterActivate());
assertFalse(requeryManager.canSendQueryNow());
// turn the dht on again, and even though no time has passed
// since the last query we can still do one
dhtManager.on = true;
altLocFinder.listener = null;
assertTrue(requeryManager.canSendQueryAfterActivate());
assertTrue(requeryManager.canSendQueryNow());
mockery.checking(new Expectations() {{
one(requeryListener).lookupStarted(QueryType.DHT, dhtQueryLength()); inSequence(sequence);
one(requeryListener).getSHA1Urn();
will(returnValue(sha1Urn));
inSequence(sequence);
}});
requeryManager.sendQuery();
assertSame(requeryManager.searchHandler, altLocFinder.listener); // sent a DHT query
assertTrue(requeryManager.isWaitingForResults());
}
private long dhtQueryLength() {
return Math.max(RequeryManager.TIME_BETWEEN_REQUERIES, LookupSettings.FIND_VALUE_LOOKUP_TIMEOUT.getValue());
}
@Singleton
private static class MyDHTManager extends DHTManagerStub {
private volatile DHTEventListener listener;
private volatile boolean on;
@Override
public void addEventListener(DHTEventListener listener) {
this.listener = listener;
}
@Override
public boolean isMemberOfDHT() {
return on;
}
@Override
public void removeEventListener(DHTEventListener listener) {
if (this.listener == listener)
this.listener = null;
}
}
@Singleton
private static class MyAltLocFinder implements AltLocFinder {
private volatile SearchListener<AlternateLocation> listener;
volatile boolean cancelled;
public Shutdownable findAltLocs(URN urn, SearchListener<AlternateLocation> listener) {
this.listener = listener;
return new Shutdownable() {
public void shutdown() {
cancelled = true;
}
};
}
public boolean findPushAltLocs(GUID guid, URN urn, SearchListener<AlternateLocation> listener) {
return false;
}
public AlternateLocation getAlternateLocation(GUID guid, URN urn) {
return null;
}
}
}