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()); } } }