package com.limegroup.gnutella.filters; import junit.framework.Test; import org.jmock.Expectations; import org.jmock.Mockery; import org.limewire.io.GUID; import org.limewire.util.BaseTestCase; import com.limegroup.gnutella.messages.PingRequest; import com.limegroup.gnutella.messages.QueryRequest; public class AnomalousQueryFilterTest extends BaseTestCase { public AnomalousQueryFilterTest(String name){ super(name); } public static Test suite() { return buildTestSuite(AnomalousQueryFilterTest.class); } QueryRequest query; PingRequest ping; AnomalousQueryFilter filter; Mockery context; @Override public void setUp() throws Exception { context = new Mockery(); query = context.mock(QueryRequest.class); ping = context.mock(PingRequest.class); filter = new AnomalousQueryFilter(); } /** * Returns a GUID that starts with 4 fixed bytes */ private static byte[] makeSuspiciousGuid() { byte[] guid = GUID.makeGuid(); guid[0] = 0; guid[1] = 1; guid[2] = 2; guid[3] = 3; return guid; } /** * Tests that suspicious queries are not blocked until sufficient * queries have been seen to identify suspicious queries reliably */ public void testAllowedUntilTotalIsSufficient() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; context.checking(new Expectations() {{ exactly(total - 1).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); }}); for(int i = 0; i < total - 1; i++) { assertTrue(filter.allow(query)); } context.assertIsSatisfied(); } /** * Tests that suspicious queries are blocked as soon as sufficient * queries have been seen to identify suspicious queries reliably */ public void testBlockedWhenTotalIsSufficient() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; context.checking(new Expectations() {{ exactly(total).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); // These methods will be called for the last query one(query).desiresOutOfBandReplies(); will(returnValue(false)); one(query).getMinSpeed(); will(returnValue(0)); }}); for(int i = 0; i < total - 1; i++) { assertTrue(filter.allow(query)); } // The last query should be blocked assertFalse(filter.allow(query)); context.assertIsSatisfied(); } /** * Tests that suspicious queries will not be blocked unless they * make up a sufficient fraction of observed queries */ public void testAllowedUntilFractionIsSufficient() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; final int suspiciousCount = (int)(AnomalousQueryFilter.PREFIXES_TO_COUNT * AnomalousQueryFilter.MAX_FRACTION_PER_PREFIX); final int innocentCount = total - suspiciousCount; context.checking(new Expectations() {{ exactly(innocentCount).of(query).getGUID(); will(returnValue(GUID.makeGuid())); exactly(suspiciousCount).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); }}); for(int i = 0; i < total; i++) { assertTrue(filter.allow(query)); } context.assertIsSatisfied(); } /** * Tests that suspicious queries will be blocked as soon as they * make up a sufficient fraction of observed queries */ public void testBlockedWhenFractionIsSufficient() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; final int suspiciousCount = (int)(AnomalousQueryFilter.PREFIXES_TO_COUNT * AnomalousQueryFilter.MAX_FRACTION_PER_PREFIX) + 1; final int innocentCount = total - suspiciousCount; context.checking(new Expectations() {{ exactly(innocentCount).of(query).getGUID(); will(returnValue(GUID.makeGuid())); exactly(suspiciousCount).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); // These methods will be called for the last query one(query).desiresOutOfBandReplies(); will(returnValue(false)); one(query).getMinSpeed(); will(returnValue(0)); }}); for(int i = 0; i < total - 1; i++) { assertTrue(filter.allow(query)); } // The last query should be blocked assertFalse(filter.allow(query)); context.assertIsSatisfied(); } /** * Tests that suspicious queries will only be blocked if they * don't ask for out of band results */ public void testAllowedIfAsksForOutOfBand() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; final int suspiciousCount = (int)(AnomalousQueryFilter.PREFIXES_TO_COUNT * AnomalousQueryFilter.MAX_FRACTION_PER_PREFIX) + 1; final int innocentCount = total - suspiciousCount; context.checking(new Expectations() {{ exactly(innocentCount).of(query).getGUID(); will(returnValue(GUID.makeGuid())); exactly(suspiciousCount).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); // This method will be called for the last query one(query).desiresOutOfBandReplies(); will(returnValue(true)); // Minimum speed will never be checked }}); for(int i = 0; i < total; i++) { assertTrue(filter.allow(query)); } context.assertIsSatisfied(); } /** * Tests that suspicious queries will only be blocked if they * have non-zero minimum speed */ public void testAllowedIfNonZeroMinimumSpeed() { final int total = AnomalousQueryFilter.PREFIXES_TO_COUNT; final int suspiciousCount = (int)(AnomalousQueryFilter.PREFIXES_TO_COUNT * AnomalousQueryFilter.MAX_FRACTION_PER_PREFIX) + 1; final int innocentCount = total - suspiciousCount; context.checking(new Expectations() {{ exactly(innocentCount).of(query).getGUID(); will(returnValue(GUID.makeGuid())); exactly(suspiciousCount).of(query).getGUID(); will(returnValue(makeSuspiciousGuid())); // These methods will be called for the last query one(query).desiresOutOfBandReplies(); will(returnValue(false)); one(query).getMinSpeed(); will(returnValue(QueryRequest.SPECIAL_MINSPEED_MASK)); }}); for(int i = 0; i < total; i++) { assertTrue(filter.allow(query)); } context.assertIsSatisfied(); } public void testOtherMessagesAreIgnored() throws Exception { context.checking(new Expectations() {{ never(ping); }}); assertTrue(filter.allow(ping)); context.assertIsSatisfied(); } }