package org.limewire.mojito.routing.impl; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Arrays; import junit.framework.TestSuite; import org.limewire.mojito.KUID; import org.limewire.mojito.MojitoTestCase; import org.limewire.mojito.routing.Bucket; import org.limewire.mojito.routing.ClassfulNetworkCounter; import org.limewire.mojito.routing.Contact; import org.limewire.mojito.routing.ContactFactory; import org.limewire.mojito.routing.RouteTable; import org.limewire.mojito.routing.Vendor; import org.limewire.mojito.routing.Version; import org.limewire.mojito.routing.Contact.State; import org.limewire.mojito.settings.ContextSettings; import org.limewire.mojito.settings.KademliaSettings; import org.limewire.mojito.settings.RouteTableSettings; public class BucketNodeTest extends MojitoTestCase { private Contact localNode; private RouteTableImpl routeTable; private Vendor vendor; private Version version; public BucketNodeTest(String name) { super(name); } public static TestSuite suite() { return buildTestSuite(BucketNodeTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } @Override protected void setUp() throws Exception { vendor = ContextSettings.getVendor(); version = ContextSettings.getVersion(); routeTable = new RouteTableImpl(); localNode = routeTable.getLocalNode(); } @Override protected void tearDown() throws Exception { super.tearDown(); } public void testPurge() { Bucket bucket = routeTable.getBucket(localNode.getNodeID()); //try purging bucket with only local node bucket.purge(); assertTrue("should contain local node", bucket.getActiveContacts().contains(localNode)); SocketAddress address = new InetSocketAddress("localhost", 2000); //now add dead and unknown nodes Contact node = ContactFactory.createUnknownContact( vendor, version, KUID.createRandomID(), address); bucket.addActiveContact(node); Contact node2 = new RemoteContact(address, vendor, version, KUID.createRandomID(), address, 0, Contact.DEFAULT_FLAG, State.DEAD); bucket.addActiveContact(node2); assertEquals(bucket.getActiveContacts().size(), 3); bucket.purge(); assertTrue("should contain local node", bucket.getActiveContacts().contains(localNode)); assertEquals(bucket.getActiveContacts().size(), 1); //now add cached node node = ContactFactory.createUnknownContact( vendor, version, KUID.createRandomID(), address); bucket.addActiveContact(node); node2 = new RemoteContact(address, vendor, version, KUID.createRandomID(), address, 0, Contact.DEFAULT_FLAG, State.DEAD); bucket.addActiveContact(node2); Contact node3 = new RemoteContact(address, vendor, version, KUID.createRandomID(), address, 0, Contact.DEFAULT_FLAG, State.ALIVE); bucket.addCachedContact(node3); assertEquals(bucket.getActiveContacts().size(), 3); bucket.purge(); assertTrue(bucket.getActiveContacts().contains(node3)); assertFalse(bucket.getCachedContacts().contains(node3)); } public void testTouchBucket() throws Exception{ Bucket bucket = routeTable.getBucket(localNode.getNodeID()); long now = System.currentTimeMillis(); assertEquals(0, bucket.getTimeStamp()); assertLessThan(now, bucket.getTimeStamp()); Thread.sleep(200); bucket.touch(); Thread.sleep(200); assertGreaterThan(now, bucket.getTimeStamp()); now = System.currentTimeMillis(); assertLessThan(now, bucket.getTimeStamp()); } public void testUpdateClassfulNetworkCounter() { RouteTable routeTable = new RouteTableImpl(); Bucket bucket = routeTable.getBucket(KUID.MINIMUM); ClassfulNetworkCounter counter = bucket.getClassfulNetworkCounter(); assertEquals(0, counter.size()); KUID nodeId1 = KUID.createRandomID(); Contact node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, nodeId1, new InetSocketAddress("localhost", 5000)); bucket.addActiveContact(node); assertEquals(1, counter.size()); node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, nodeId1, new InetSocketAddress("www.apple.com", 5000)); bucket.updateContact(node); assertEquals(1, counter.size()); KUID nodeId2 = KUID.createRandomID(); node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, nodeId2, new InetSocketAddress("www.apple.com", 5000)); bucket.addActiveContact(node); assertEquals(3, bucket.size()); // local Node, first Node and this Node assertEquals(1, counter.size()); KUID nodeId3 = KUID.createRandomID(); node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, nodeId3, new InetSocketAddress("www.google.com", 5000)); bucket.addActiveContact(node); assertEquals(4, bucket.size()); assertEquals(2, counter.size()); } public void testLeastAndMostRecentlySeenActiveContact() { RouteTableImpl routeTable = new RouteTableImpl(); Bucket bucket = routeTable.getBucket(KUID.MINIMUM); bucket.clear(); assertEquals(0, bucket.getActiveSize()); Contact leastRecentlySeen = null; Contact mostRecentlySeen = null; int k = KademliaSettings.REPLICATION_PARAMETER.getValue(); assertGreaterThan(0, k); for (int i = 0; i < k; i++) { Contact node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(), new InetSocketAddress("localhost", 6000 + i)); node.setTimeStamp(/* fake time */ i); if (leastRecentlySeen == null) { leastRecentlySeen = node; } mostRecentlySeen = node; bucket.addActiveContact(node); } assertEquals(k, bucket.getActiveSize()); // Test initial State assertSame(leastRecentlySeen, bucket.getLeastRecentlySeenActiveContact()); assertSame(mostRecentlySeen, bucket.getMostRecentlySeenActiveContact()); // Touch the least recently seen Node which makes it the // most recently seen Node assert leastRecentlySeen != null; // avoid NPE warning leastRecentlySeen.setTimeStamp(/* fake time */ Integer.MAX_VALUE); assertSame(leastRecentlySeen, bucket.getMostRecentlySeenActiveContact()); assertNotSame(mostRecentlySeen, bucket.getMostRecentlySeenActiveContact()); } public void testLeastAndMostRecentlySeenCachedContact() { RouteTableImpl routeTable = new RouteTableImpl(); Bucket bucket = routeTable.getBucket(KUID.MINIMUM); Contact leastRecentlySeen = null; Contact mostRecentlySeen = null; int maxCacheSize = RouteTableSettings.MAX_CACHE_SIZE.getValue(); for (int i = 0; i < maxCacheSize; i++) { Contact node = ContactFactory.createUnknownContact( Vendor.UNKNOWN, Version.ZERO, KUID.createRandomID(), new InetSocketAddress("localhost", 6000 + i)); if (leastRecentlySeen == null) { leastRecentlySeen = node; } mostRecentlySeen = node; bucket.addCachedContact(node); } assertEquals(maxCacheSize, bucket.getCacheSize()); // Test initial State assertSame(leastRecentlySeen, bucket.getLeastRecentlySeenCachedContact()); assertSame(mostRecentlySeen, bucket.getMostRecentlySeenCachedContact()); // Touch the least recently seen Node which makes it the // most recently seen Node bucket.addCachedContact(leastRecentlySeen); assertSame(leastRecentlySeen, bucket.getMostRecentlySeenCachedContact()); assertNotSame(mostRecentlySeen, bucket.getMostRecentlySeenCachedContact()); } public void testIsTooDeep() { try { RouteTableSettings.DEPTH_LIMIT.setValue(4); RouteTableImpl routeTable = new RouteTableImpl(); KUID localNodeId = routeTable.getLocalNode().getNodeID(); byte firstByte = 0; byte bytes[] = new byte[20]; for(int i = 0; i < 256; i++){ SocketAddress src = new InetSocketAddress("localhost", 3000+i); bytes[0] = firstByte; KUID nodeId = KUID.createWithBytes(bytes); firstByte++; Vendor vendor = Vendor.UNKNOWN; Version version = Version.ZERO; SocketAddress con = new InetSocketAddress("localhost", 30000+i); int instanceId = 0; int flags = Contact.DEFAULT_FLAG; Contact node = ContactFactory.createLiveContact( src, vendor, version, nodeId, con, instanceId, flags); routeTable.add(node); } // + 1 for local node assertEquals("All contacts should have been added", 256 + 1, routeTable.getContacts().size()); // all buckets except for local one should split, since depth is one for (Bucket bucket : routeTable.getBuckets()) { int commonPrefixLength = bucket.getBucketID().getCommonPrefixLength(localNodeId); if (bucket.getDepth() - commonPrefixLength >= RouteTableSettings.DEPTH_LIMIT.getValue()) { assertTrue(bucket.isTooDeep()); } else { assertFalse(bucket.isTooDeep()); } } } finally { RouteTableSettings.DEPTH_LIMIT.revertToDefault(); } } /** * Tests that all buckets are too deep except for local node. */ public void testBucketsAreTooDeep() { try { RouteTableSettings.DEPTH_LIMIT.setValue(1); byte[] localNodeId = new byte[KUID.LENGTH]; Arrays.fill(localNodeId, (byte)0xFF); RouteTableImpl routeTable = new RouteTableImpl(localNodeId); byte firstByte = (byte)255; byte bytes[] = new byte[20]; for(int i = 0; i < 256; i++){ SocketAddress src = new InetSocketAddress("localhost", 3000+i); bytes[0] = firstByte; KUID nodeId = KUID.createWithBytes(bytes); firstByte--; Vendor vendor = Vendor.UNKNOWN; Version version = Version.ZERO; SocketAddress con = new InetSocketAddress("localhost", 30000+i); int instanceId = 0; int flags = Contact.DEFAULT_FLAG; Contact node = ContactFactory.createLiveContact( src, vendor, version, nodeId, con, instanceId, flags); routeTable.add(node); } // all buckets except for local one should split, since depth is one for (Bucket bucket : routeTable.getBuckets()) { if (bucket.contains(KUID.createWithBytes(localNodeId))) { assertFalse(bucket.isTooDeep()); } else { assertTrue(bucket.isTooDeep()); } } } finally { RouteTableSettings.DEPTH_LIMIT.revertToDefault(); } } public void testInSmallestSubtree() { RouteTableImpl routeTable = new RouteTableImpl(); Contact localNode = routeTable.getLocalNode(); int firstBit; if (localNode.getNodeID().isBitSet(0)) { firstBit = 0; } else { firstBit = 128; } byte bytes[] = new byte[20]; for (int i = firstBit; i < 128 + firstBit; i++) { SocketAddress src = new InetSocketAddress("localhost", 3000 + i); bytes[0] = (byte) i; KUID nodeId = KUID.createWithBytes(bytes); Vendor vendor = Vendor.UNKNOWN; Version version = Version.ZERO; SocketAddress con = new InetSocketAddress("localhost", 30000 + i); int instanceId = 0; int flags = Contact.DEFAULT_FLAG; Contact node = ContactFactory.createLiveContact(src, vendor, version, nodeId, con, instanceId, flags); routeTable.add(node); System.out.println(node); } for (Bucket bucket : routeTable.getBuckets()) { if (bucket.contains(localNode.getNodeID())) { assertFalse(bucket.isInSmallestSubtree()); } else { assertTrue(bucket.isInSmallestSubtree()); } } } }