package com.limegroup.gnutella.search;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import junit.framework.Test;
import com.limegroup.gnutella.Connection;
import com.limegroup.gnutella.ManagedConnection;
import com.limegroup.gnutella.ReplyHandler;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.messages.QueryRequest;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.util.BaseTestCase;
import com.limegroup.gnutella.util.LeafConnection;
import com.limegroup.gnutella.util.NewConnection;
import com.limegroup.gnutella.util.PrivilegedAccessor;
import com.limegroup.gnutella.util.TestConnection;
import com.limegroup.gnutella.util.TestConnectionManager;
import com.limegroup.gnutella.util.TestResultCounter;
import com.limegroup.gnutella.util.UltrapeerConnection;
/**
* Tests the functionality of the <tt>QueryHandlerTest</tt> class.
*/
public final class QueryHandlerTest extends BaseTestCase {
public QueryHandlerTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(QueryHandlerTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
/**
* Tests the method for sending an individual query to a
* single connections.
*/
public void testSendQueryToHost() throws Exception {
new RouterService(new ActivityCallbackStub());
ReplyHandler rh = new UltrapeerConnection();
QueryRequest query = QueryRequest.createQuery("test", (byte)1);
QueryHandler qh =
QueryHandler.createHandler(query, rh, new TestResultCounter());
Class[] paramTypes = new Class[] {
QueryRequest.class,
ManagedConnection.class,
QueryHandler.class,
};
Method m =
PrivilegedAccessor.getMethod(QueryHandler.class,
"sendQueryToHost",
paramTypes);
ManagedConnection mc = new UltrapeerConnection();
Object[] params = new Object[] {
query, mc, qh,
};
m.invoke(null, params);
List hostsQueried =
(List)PrivilegedAccessor.getValue(qh, "QUERIED_CONNECTIONS");
assertEquals("should not have added host", 0, hostsQueried.size());
// make sure we add the connection to the queries handlers
// if it doesn't support probe queries and the TTL is 1
params[1] = LeafConnection.createLeafConnection(false);
m.invoke(null, params);
hostsQueried =
(List)PrivilegedAccessor.getValue(qh, "QUERIED_CONNECTIONS");
assertEquals("should have added host", 1, hostsQueried.size());
// make sure it adds the connection when the ttl is higher
// than one and the connection supports probe queries
params[0] = QueryRequest.createQuery("test");
params[1] = new UltrapeerConnection();
m.invoke(null, params);
hostsQueried =
(List)PrivilegedAccessor.getValue(qh, "QUERIED_CONNECTIONS");
assertEquals("should have added host", 2, hostsQueried.size());
}
/**
* Tests the method for calculating the next TTL.
*/
public void testCalculateNewTTL() throws Exception {
Class[] params = new Class[]{Integer.TYPE, Integer.TYPE, Byte.TYPE};
Method m =
PrivilegedAccessor.getMethod(QueryHandler.class,
"calculateNewTTL",
params);
byte ttl = getTTL(m, 2000, 15, (byte)5);
assertEquals("unexpected ttl", 3, ttl);
ttl = getTTL(m, 200, 15, (byte)5);
assertEquals("unexpected ttl", 2, ttl);
ttl = getTTL(m, 200000, 15, (byte)4);
assertEquals("unexpected ttl", 4, ttl);
ttl = getTTL(m, 20000, 5, (byte)2);
assertEquals("unexpected ttl", 2, ttl);
}
/**
* Convenience method for getting the TTL from QueryHandler.
*/
private byte getTTL(Method m, int hosts, int degree, byte maxTTL)
throws Exception {
byte ttl =
((Byte)m.invoke(null, new Object[] {
new Integer(hosts), new Integer(degree),
new Byte(maxTTL)})).byteValue();
return ttl;
}
/**
* Tests the public sendQuery method to make sure it's working as
* expected.
*/
public void testPublicSendQuery() throws Exception {
new RouterService(new ActivityCallbackStub());
assertNotNull("should have a message router", RouterService.getMessageRouter());
List connections = new LinkedList();
for(int i=0; i<15; i++) {
connections.add(NewConnection.createConnection());
}
QueryHandler handler =
QueryHandler.createHandler(QueryRequest.createQuery("test"),
NewConnection.createConnection(),
new TestResultCounter(0));
TestConnectionManager tcm = new TestConnectionManager();
assertTrue(tcm.getInitializedConnections().size() > 0);
PrivilegedAccessor.setValue(QueryHandler.class, "_connectionManager", tcm);
handler.sendQuery();
int numQueries = tcm.getNumUltrapeerQueries();
assertEquals("unexpected number of probe queries sent", 3, numQueries);
// these calls should not go through, as it's too soon after the probe
// was sent!
handler.sendQuery();
handler.sendQuery();
handler.sendQuery();
assertEquals("unexpected number of probe queries sent", 3,
tcm.getNumUltrapeerQueries());
Thread.sleep(8000);
handler.sendQuery();
Thread.sleep(1000);
// after the sleep, it should go through!
assertEquals("unexpected number of queries sent", 4, tcm.getNumUltrapeerQueries());
// these calls should not go through, as it's too soon after the last query
// was sent!
handler.sendQuery();
handler.sendQuery();
handler.sendQuery();
assertEquals("unexpected number of queries sent", 4, tcm.getNumUltrapeerQueries());
Thread.sleep(8000);
handler.sendQuery();
// after the sleep, it should go through!
assertEquals("unexpected number of queries sent", 5, tcm.getNumUltrapeerQueries());
// now, send out a bunch of queries to make sure that, eventually,
// new queries don't go out because we've reached too high a theoretical
// horizon. These queries aren't returning any results, so the TTLs
// should be relatively high, causing us to hit the theoretical
// horizon limit!
for(int i=0; i<tcm.getInitializedConnections().size()-2; i++) {
Thread.sleep(2000);
handler.sendQuery();
}
assertTrue("too many hosts queried! -- theoretical horizon too high",
(tcm.getNumUltrapeerQueries() <= 12));
}
/**
* Test to make sure that the theoretical horizon we've reached
* is being calculated correctly.
*/
public void testCalculateNewHosts() throws Exception {
Method m =
PrivilegedAccessor.getMethod(QueryHandler.class,
"calculateNewHosts",
new Class[]{Connection.class,
Byte.TYPE});
// test for a degree 19, ttl 4 network
ManagedConnection mc = NewConnection.createConnection(19);
int horizon = 0;
for(int i=0; i<19; i++) {
horizon +=
((Integer)m.invoke(null,
new Object[]{mc, new Byte((byte)4)})).intValue();
}
assertEquals("incorrect horizon", 117325, horizon);
// test for a degree 30, ttl 3 network
mc = NewConnection.createConnection(30);
horizon = 0;
for(int i=0; i<30; i++) {
horizon +=
((Integer)m.invoke(null,
new Object[]{mc, new Byte((byte)3)})).intValue();
}
assertEquals("incorrect horizon", 26130, horizon);
}
/**
* Tests the private method for sending queries to make sure the
* basics of query dispatching are working correctly.
*/
public void testPrivateSendQuery() throws Exception {
new RouterService(new ActivityCallbackStub());
assertNotNull("should have a message router", RouterService.getMessageRouter());
Method m =
PrivilegedAccessor.getMethod(QueryHandler.class,
"sendQuery",
new Class[]{List.class});
int numConnections = 15;
List connections = new ArrayList();
for(int i=0; i<numConnections; i++) {
connections.add(NewConnection.createConnection(10));
}
QueryHandler handler =
QueryHandler.createHandler(QueryRequest.createQuery("test"),
NewConnection.createConnection(8),
new TestResultCounter(0));
Object[] params = new Object[] {connections};
// just send queries to all connections
for(int i=0; i<numConnections; i++) {
m.invoke(handler, params);
}
// just make sure all of the connections received the query
Iterator iter = connections.iterator();
while(iter.hasNext()) {
TestConnection tc = (TestConnection)iter.next();
assertTrue("should have received the query", tc.receivedQuery());
}
}
}