package com.limegroup.gnutella.version; import java.io.ByteArrayOutputStream; import java.io.File; import junit.framework.Test; import org.limewire.core.settings.UpdateSettings; import org.limewire.io.GGEP; import org.limewire.listener.EventListener; import org.limewire.util.Base32; import org.limewire.util.FileUtils; import org.limewire.util.PrivilegedAccessor; import org.limewire.util.TestUtils; import com.google.inject.AbstractModule; import com.google.inject.Module; import com.google.inject.Stage; import com.limegroup.gnutella.ActivityCallback; import com.limegroup.gnutella.BlockingConnectionUtils; import com.limegroup.gnutella.LimeTestUtils; import com.limegroup.gnutella.PeerTestCase; import com.limegroup.gnutella.connection.BlockingConnection; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.vendor.AbstractVendorMessage; import com.limegroup.gnutella.messages.vendor.CapabilitiesVM; import com.limegroup.gnutella.messages.vendor.CapabilitiesVMStubHelper; import com.limegroup.gnutella.messages.vendor.UpdateRequest; import com.limegroup.gnutella.messages.vendor.UpdateResponse; import com.limegroup.gnutella.messages.vendor.VendorMessage; import com.limegroup.gnutella.stubs.ActivityCallbackStub; import com.limegroup.gnutella.util.DataUtils; import com.limegroup.gnutella.util.LimeWireUtils; /** * Tests to make sure updates are sent, requested, etc... */ public class InterClientTest extends PeerTestCase { private BlockingConnection PEER; private MyActivityCallback myActivityCallback; public InterClientTest(String name) { super(name); } public static Test suite() { return buildTestSuite(InterClientTest.class); } public static void main(String[] args) throws Exception { junit.textui.TestRunner.run(suite()); } private UpdateRequest dummy = new UpdateRequest(); @Override public void setUp() throws Exception { myActivityCallback = new MyActivityCallback(); Module m = new AbstractModule() { @Override public void configure() { bind(ActivityCallback.class).toInstance(myActivityCallback); } }; super.setUp(LimeTestUtils.createInjector(Stage.PRODUCTION, m)); setEmpty(); PEER = connect(true); BlockingConnectionUtils.drain(PEER); doInitialExchange(); } @Override public void tearDown() throws Exception { super.tearDown(); if(PEER != null) PEER.close(); } private UpdateHandler getUpdateHandler() { return injector.getInstance(UpdateHandler.class); } /** * Simple test to make sure that if someone sends a CapabilitiesVM * with a greater version than we have, that we request the new version. * And that we don't request if someone doesn't send it. */ public void testRequestIsSent() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); PEER.send(getCVM(1)); PEER.flush(); // We should get an UpdateRequest. Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateRequest.class); assertNotNull(m); assertInstanceof(UpdateRequest.class, m); setCurrentId(10); assertEquals(10, getUpdateHandler().getLatestId()); PEER.send(getCVM(5)); PEER.flush(); // we shouldn't get a message, since they said they had 5 & we know of 10. m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateRequest.class); assertNull(m); // Now if they send with 11, we'll request. PEER.send(getCVM(11)); PEER.flush(); m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateRequest.class); assertNotNull(m); assertInstanceof(UpdateRequest.class, m); } /** * Tests that a response is sent in response to a rquest. */ public void testResponseIsSent() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); // We should get no response, since we have no data to give. PEER.send(new UpdateRequest()); PEER.flush(); Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNull(m); // Alright, set some current bytes so we can do some testing. byte[] data = setCurrent(-10); PEER.send(new UpdateRequest()); PEER.flush(); m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNotNull(m); assertInstanceof(UpdateResponse.class, m); byte[] payload = payload(m); GGEP ggep = new GGEP(payload, 0); assertEquals(data, ggep.get("U")); assertEquals(1, ggep.getHeaders().size()); } /** * Tests that valid versions are used. */ public void testValidVersion() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); // Get the -10 file. byte[] b = readFile(-10); PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. assertEquals(-10, getUpdateHandler().getLatestId()); // Make sure we got a new CapabilitiesVM. Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, CapabilitiesVM.class); assertNotNull(m); // TODO: is 65526 right? assertEquals(65526, ((CapabilitiesVM)m).supportsUpdate()); } /** * Tests that older versions are ignored. */ public void testOlderVersionsIgnored() throws Exception { setCurrentId(-9); assertEquals(-9, getUpdateHandler().getLatestId()); // Get the -10 file. byte[] b = readFile(-10); PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. assertEquals(-9, getUpdateHandler().getLatestId()); } /** * Test that invalid signatures are ignored. */ public void testInvalidSignaturesIgnored() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); // Get the -10 file. byte[] b = readFile(-10); b[0] = '0'; // break the sig. PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. assertEquals(-11, getUpdateHandler().getLatestId()); } /** * Tests that invalid bytes break verification. */ public void testInvalidBytesIgnored() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); // Get the -10 file. byte[] b = readFile(-10); b[b.length-1] = '0'; // break the data. PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. assertEquals(-11, getUpdateHandler().getLatestId()); } /** * Test invalid XML ignored. */ public void testInvalidXMLIgnored() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); // Get the -9 file. byte[] b = readFile(-9); PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. assertEquals(-11, getUpdateHandler().getLatestId()); } /** * Tests that no updates are sent to the UI because the * version is too old */ public void testUpdatesNotSentToGui() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); // add listener, should not receive any events HandleUpdate update = new HandleUpdate(); getUpdateHandler().addListener(update); // Get the -8 file. byte[] b = readFile(-8); PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. getUpdateHandler().removeListener(update);// remove listener assertEquals(-8, getUpdateHandler().getLatestId()); assertNull("Should not recieve update event", update.event); } /** * Tests that updates are sent out when versions come in. */ public void testUpdatesSentToGUI() throws Exception { setCurrentId(-11); assertEquals(-11, getUpdateHandler().getLatestId()); setVersion("3.0.0"); // Set the update style to zero to ensure the message is not ignored UpdateSettings.UPDATE_STYLE.setValue(0); //add listener, should receive an update event HandleUpdate update = new HandleUpdate(); getUpdateHandler().addListener(update); // Get the -8 file. byte[] b = readFile(-8); PEER.send(UpdateResponse.createUpdateResponse(b,dummy)); PEER.flush(); Thread.sleep(1000); // let it process. getUpdateHandler().removeListener(update); //remove listener assertEquals(-8, getUpdateHandler().getLatestId()); assertEquals(update.event.getType(), com.limegroup.gnutella.version.UpdateEvent.Type.UPDATE); } private class HandleUpdate implements EventListener<UpdateEvent> { UpdateEvent event = null; @Override public void handleEvent(UpdateEvent event) { this.event = event; } } public void testCompressedResponse() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); // We should get no response, since we have no data to give. UpdateRequestStub request = new UpdateRequestStub(2,true,true); PEER.send(new UpdateRequest()); PEER.flush(); Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNull(m); // Alright, set some current bytes so we can do some testing. byte[] data = setCurrent(-10); PEER.send(request); PEER.flush(); m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNotNull(m); assertInstanceof(UpdateResponse.class, m); byte [] payload = payload(m); GGEP g = new GGEP(payload,0,null); assertEquals(g.getBytes("C"),data); assertFalse(g.hasKey("U")); } public void testUncompressedGGEPResponse() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); // We should get no response, since we have no data to give. PEER.send(new UpdateRequest()); PEER.flush(); Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNull(m); UpdateRequestStub request = new UpdateRequestStub(2,true,false); byte[] data = setCurrent(-10); PEER.send(request); PEER.flush(); m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNotNull(m); assertInstanceof(UpdateResponse.class, m); byte [] payload = payload(m); GGEP g = new GGEP(payload,0,null); assertEquals(g.getBytes("U"),data); assertFalse(g.hasKey("C")); } /** * tests that a request with higher version w/o GGEP gets responded to properly */ public void testHigherVersionNoGGEP() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); // We should get no response, since we have no data to give. PEER.send(new UpdateRequest()); PEER.flush(); Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNull(m); UpdateRequestStub request = new UpdateRequestStub(UpdateRequest.VERSION+1,false,false); byte[] data = setCurrent(-10); PEER.send(request); PEER.flush(); m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNotNull(m); byte [] payload = payload(m); GGEP g = new GGEP(payload,0); assertEquals(g.getBytes("U"),data); assertFalse(g.hasKey("C")); } /** Tests that requests from older versions get responded to properly. */ public void testOldRequestGetsMaxVersionNoGGEP() throws Exception { assertEquals(0, getUpdateHandler().getLatestId()); // We should get no response, since we have no data to give. PEER.send(new UpdateRequestStub(1, false, false)); PEER.flush(); Message m = BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateResponse.class); assertNotNull(m); byte[] payload = payload(m); assertEquals("I5AVOQKFIZCE4Q2RKFATKVBWKBKVEWSOJRFU6WS2JVIUCR2QGRJESNBVIE3UESKDINIUCSSWIZKE2WCHGZKFMMS2GJAVSTKCGQ2TINSLIRFEQWCRG5KEITL4PQ6HK4DEMF2GKIDJMQ6SEMRRGQ3TIOBTGY2DOIRAORUW2ZLTORQW24B5EIYSEPQKEAQCAPDNONTSAZTSN5WT2IRXGYXDONZOG42SEIDGN5ZD2IRYGYXDQOBOHA2SEIDUN46SEOBWFY4DSLRYGURCA5LSNQ6SE2DUORYDULZPO53XOLTMNFWWK53JOJSS4Y3PNUXXK4DEMF2GKIRAON2HS3DFHURDAIRAN5ZT2ISXNFXGI33XOMRCA5LSNY6SE5LSNY5GE2LUOBZGS3TUHJIEYUCSKRIEET2BKJBE6U2BJNIECTKHKZJTEU2MGU3VGM2HIRGFCLRXIZIEGR2NG43VGSCPKFGVAUSQJU2UGNKMJ5NEKT2EG43EGRK2IQ2E2USBIVGESIRAOVRW63LNMFXGIPJHEISCKIRAF5JSOIDVNZQW2ZJ5EJGGS3LFK5UXEZKXNFXDILRRGYXDMLTFPBSSEIDTNF5GKPJCGQ2TANRSGU3CEPQKEAQCAIBAEA6GYYLOM4QGSZB5E5SW4JZ6BIQCAIBAEAQCAIB4EFNUGRCBKRAVWNBOGE3C4NRAKVJE4XK5HYFCAIBAEAQCAPBPNRQW4ZZ6BIQCAIB4F5WXGZZ6BIQCAIBAEAQDY3LTM4QGM4TPNU6SENBOHAXDCIRAMZXXEPJCGQXDCNROGYRCA5LSNQ6SE2DUORYDULZPO53XOLTMNFWWK53JOJSS4Y3PNUXXK4DEMF2GKIRAMZZGKZJ5EJ2HE5LFEIQG64Z5EJLWS3TEN53XGIRAON2HS3DFHURDIIRAOVZG4PJCOVZG4OTCNF2HA4TJNZ2DUUCMKBJFIUCCJ5AVEQSPKNAUWUCBJVDVMUZSKNGDKN2TGNDUITCRFY3UMUCDI5GTON2TJBHVCTKQKJIE2NKDGVGE6WSFJ5CDONSDIVNEINCNKJAUKTCJEIQHKY3PNVWWC3TEHUTSEJBFEIQC6UZHEB2W4YLNMU6SETDJNVSVO2LSMVLWS3RUFYYTMLRWFZSXQZJCEBZWS6TFHURDINJQGYZDKNRCHYFCAIBAEAQCAPDMMFXGOIDJMQ6SOZLOE47AUIBAEAQCAIB4EFNUGRCBKRAVWCRAEAQCAIBAHR2GCYTMMUQGC3DJM5XD2Y3FNZ2GK4RAOZQWY2LHNY6WGZLOORSXEPR4ORZD4PDUMQ7AUPDDMVXHIZLSHY6GEPSVOJTWK3TUEBGGS3LFK5UXEZJAKNSWG5LSNF2HSICVOBSGC5DFEBAXMYLJNRQWE3DFFY6GE4R6BJIGYZLBONSSAVLQMRQXIZJAJFWW2ZLENFQXIZLMPEXDYYTSHY6GE4R6HQXWEPQKJFTCA5DIMUQHK4DEMF2GKIDEN5SXGIDON52CA53POJVSYIDWNFZWS5B4MJZD4CTIOR2HAORPF53XO5ZONRUW2ZLXNFZGKLTDN5WS6ZDPO5XGY33BMQ6GE4R6EBTG64RAORUGKIDMMF2GK43UEB3GK4TTNFXW4IDPMYQEY2LNMVLWS4TFFY6C6YR6HQXWGZLOORSXEPR4F52GIPR4F52HEPR4F52GCYTMMU7AUIBAEAQCAIC5LU7AUIBAEAQCAIB4F5WGC3THHYFCAIBAEAQCAPBPNVZWOPQKEAQCAIBAEAFCAIBAEAQCAPDNONTSAZTSN5WT2IRUFY4C4MJCEBTG64R5EI2C4MJWFY3CEIDVOJWD2ITIOR2HAORPF53XO5ZONRUW2ZLXNFZGKLTDN5WS65LQMRQXIZJCEBZXI6LMMU6SENBCHYFCAIBAEAQCAPDMMFXGOIDJMQ6SOZLOE47AUIBAEAQCAIB4EFNUGRCBKRAVWCRAEAQCAIBAHR2GCYTMMUQGC3DJM5XD2Y3FNZ2GK4RAOZQWY2LHNY6WGZLOORSXEPR4ORZD4PDUMQ7AUPDDMVXHIZLSHY6GEPSVOJTWK3TUEBGGS3LFK5UXEZJAKNSWG5LSNF2HSICVOBSGC5DFEBAXMYLJNRQWE3DFFY6GE4R6BJIGYZLBONSSAVLQMRQXIZJAJFWW2ZLENFQXIZLMPEXDYYTSHY6GE4R6HQXWEPQKJFTCA5DIMUQHK4DEMF2GKIDEN5SXGIDON52CA53POJVSYIDWNFZWS5B4MJZD4CTIOR2HAORPF53XO5ZONRUW2ZLXNFZGKLTDN5WS6ZDPO5XGY33BMQ6GE4R6EBTG64RAORUGKIDMMF2GK43UEB3GK4TTNFXW4IDPMYQEY2LNMVLWS4TFFY6C6YR6HQXWGZLOORSXEPR4F52GIPR4F52HEPR4F52GCYTMMU7AUIBAEAQCAIC5LU7AUIBAEAQCAIB4F5WGC3THHYFCAIBAEAQCAPBPNVZWOPQKHQXXK4DEMF2GKPQK", Base32.encode(payload)); } private void setCurrentId(int i) throws Exception { PrivilegedAccessor.setValue(getUpdateHandler(), "_lastId", new Integer(i)); } private void setCurrentBytes(byte[] b) throws Exception { PrivilegedAccessor.setValue(getUpdateHandler(), "_lastBytes", b); } private void setCurrentInfo(UpdateInformation info) throws Exception { PrivilegedAccessor.setValue(getUpdateHandler(), "_updateInfo", null); } private byte[] setCurrent(int i) throws Exception { setCurrentId(i); byte[] b = readFile(i); assertNotNull(b); setCurrentBytes(b); return b; } private void setEmpty() throws Exception { setCurrentId(0); setCurrentBytes(null); setCurrentInfo(null); } private static byte[] readFile(int i) throws Exception { File f = TestUtils.getResourceFile("com/limegroup/gnutella/version/test_" + i + ".xml"); assertTrue(f.exists()); assertTrue(f.isFile()); return FileUtils.readFileFully(f); } private static void setVersion(String v) throws Exception { PrivilegedAccessor.setValue(LimeWireUtils.class, "testVersion", v); } private static byte[] payload(Message m) throws Exception { assertInstanceof("not a vendor message!", VendorMessage.class, m); return (byte[])PrivilegedAccessor.invokeMethod(m, "getPayload"); } private static Message getCVM(int i) throws Exception { return CapabilitiesVMStubHelper.makeCapabilitiesWithUpdate(i); } private void doInitialExchange() throws Exception { PEER.send(getCVM(0)); PEER.flush(); assertNotNull(BlockingConnectionUtils.getFirstInstanceOfMessageType(PEER, UpdateRequest.class)); } /* Required for PeerTestCase. */ @Override protected ActivityCallback getActivityCallback() { return new MyActivityCallback(); } /* Required for PeerTestCase. */ private static class MyActivityCallback extends ActivityCallbackStub { UpdateInformation lastUpdate; @Override public void updateAvailable(UpdateInformation info) { lastUpdate = info; } } private static byte[] derivePayload(boolean hasGGEP, boolean requestsCompressed) throws Exception { if (!hasGGEP) return DataUtils.EMPTY_BYTE_ARRAY; GGEP g = new GGEP(); if (requestsCompressed) g.put("C"); else g.put("X"); //put something else ByteArrayOutputStream baos = new ByteArrayOutputStream(); g.write(baos); return baos.toByteArray(); } private static class UpdateRequestStub extends AbstractVendorMessage { public int version; public boolean hasGGEP,requestsCompressed; public UpdateRequestStub(int version, boolean hasGGEP, boolean requestsCompressed) throws Exception { super(F_LIME_VENDOR_ID, F_UPDATE_REQ, version, derivePayload(hasGGEP, requestsCompressed)); this.version = version; this.hasGGEP = hasGGEP; this.requestsCompressed = requestsCompressed; } @Override public int getVersion() { return version; } public boolean hasGGEP() { return hasGGEP; } public boolean requestsCompressed() { return requestsCompressed; } } }