DHTBucketTest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. #include "DHTBucket.h"
  2. #include "DHTNode.h"
  3. #include "Exception.h"
  4. #include "Util.h"
  5. #include <cppunit/extensions/HelperMacros.h>
  6. namespace aria2 {
  7. class DHTBucketTest:public CppUnit::TestFixture {
  8. CPPUNIT_TEST_SUITE(DHTBucketTest);
  9. CPPUNIT_TEST(testGetRandomNodeID);
  10. CPPUNIT_TEST(testIsInRange);
  11. CPPUNIT_TEST(testSplitAllowed);
  12. CPPUNIT_TEST(testSplit);
  13. CPPUNIT_TEST(testAddNode);
  14. CPPUNIT_TEST(testMoveToHead);
  15. CPPUNIT_TEST(testMoveToTail);
  16. CPPUNIT_TEST(testGetGoodNodes);
  17. CPPUNIT_TEST(testCacheNode);
  18. CPPUNIT_TEST(testDropNode);
  19. CPPUNIT_TEST_SUITE_END();
  20. public:
  21. void setUp() {}
  22. void tearDown() {}
  23. void testGetRandomNodeID();
  24. void testIsInRange();
  25. void testSplitAllowed();
  26. void testSplit();
  27. void testAddNode();
  28. void testMoveToHead();
  29. void testMoveToTail();
  30. void testGetGoodNodes();
  31. void testCacheNode();
  32. void testDropNode();
  33. };
  34. CPPUNIT_TEST_SUITE_REGISTRATION(DHTBucketTest);
  35. void DHTBucketTest::testGetRandomNodeID()
  36. {
  37. unsigned char localNodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  38. 0x00, 0x00, 0x00, 0x00, 0x00,
  39. 0x00, 0x00, 0x00, 0x00, 0x00,
  40. 0x00, 0x00, 0x00, 0x00, 0x00 };
  41. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  42. {
  43. DHTBucket bucket(localNode);
  44. unsigned char nodeID[DHT_ID_LENGTH];
  45. bucket.getRandomNodeID(nodeID);
  46. }
  47. {
  48. unsigned char max[] = { 0x01, 0x01, 0xff, 0xff, 0xff,
  49. 0xff, 0xff, 0xff, 0xff, 0xff,
  50. 0xff, 0xff, 0xff, 0xff, 0xff,
  51. 0xff, 0xff, 0xff, 0xff, 0xff };
  52. unsigned char min[] = { 0x01, 0x01, 0x00, 0x00, 0x00,
  53. 0x00, 0x00, 0x00, 0x00, 0x00,
  54. 0x00, 0x00, 0x00, 0x00, 0x00,
  55. 0x00, 0x00, 0x00, 0x00, 0x00 };
  56. DHTBucket bucket(16, max, min, localNode);
  57. unsigned char nodeID[DHT_ID_LENGTH];
  58. bucket.getRandomNodeID(nodeID);
  59. CPPUNIT_ASSERT_EQUAL(std::string("0101"),
  60. Util::toHex(nodeID, sizeof(nodeID)).substr(0, 4));
  61. }
  62. }
  63. void DHTBucketTest::testIsInRange()
  64. {
  65. unsigned char localNodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  66. 0x00, 0x00, 0x00, 0x00, 0x00,
  67. 0x00, 0x00, 0x00, 0x00, 0x00,
  68. 0x00, 0x00, 0x00, 0x00, 0x00 };
  69. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  70. {
  71. unsigned char nodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  72. 0x00, 0x00, 0x00, 0x00, 0x00,
  73. 0x00, 0x00, 0x00, 0x00, 0x00,
  74. 0x00, 0x00, 0x00, 0x00, 0x00 };
  75. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  76. DHTBucket bucket(localNode);
  77. CPPUNIT_ASSERT(bucket.isInRange(node));
  78. memset(nodeID, 0xff, sizeof(nodeID));
  79. CPPUNIT_ASSERT(bucket.isInRange(node));
  80. }
  81. {
  82. unsigned char max[] = { 0x01, 0x01, 0xff, 0xff, 0xff,
  83. 0xff, 0xff, 0xff, 0xff, 0xff,
  84. 0xff, 0xff, 0xff, 0xff, 0xff,
  85. 0xff, 0xff, 0xff, 0xff, 0xff };
  86. unsigned char min[] = { 0x01, 0x01, 0x00, 0x00, 0x00,
  87. 0x00, 0x00, 0x00, 0x00, 0x00,
  88. 0x00, 0x00, 0x00, 0x00, 0x00,
  89. 0x00, 0x00, 0x00, 0x00, 0x00 };
  90. {
  91. //min
  92. unsigned char nodeID[] = { 0x01, 0x01, 0x00, 0x00, 0x00,
  93. 0x00, 0x00, 0x00, 0x00, 0x00,
  94. 0x00, 0x00, 0x00, 0x00, 0x00,
  95. 0x00, 0x00, 0x00, 0x00, 0x00 };
  96. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  97. DHTBucket bucket(16, max, min, localNode);
  98. CPPUNIT_ASSERT(bucket.isInRange(node));
  99. }
  100. {
  101. //max
  102. unsigned char nodeID[] = { 0x01, 0x01, 0xff, 0xff, 0xff,
  103. 0xff, 0xff, 0xff, 0xff, 0xff,
  104. 0xff, 0xff, 0xff, 0xff, 0xff,
  105. 0xff, 0xff, 0xff, 0xff, 0xff };
  106. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  107. DHTBucket bucket(16, max, min, localNode);
  108. CPPUNIT_ASSERT(bucket.isInRange(node));
  109. }
  110. {
  111. unsigned char nodeID[] = { 0x01, 0x01, 0x00, 0x00, 0x00,
  112. 0x00, 0x00, 0x00, 0x00, 0x00,
  113. 0xff, 0xff, 0xff, 0xff, 0xff,
  114. 0xff, 0xff, 0xff, 0xff, 0xff };
  115. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  116. DHTBucket bucket(16, max, min, localNode);
  117. CPPUNIT_ASSERT(bucket.isInRange(node));
  118. }
  119. {
  120. // nodeID is out of range: smaller than this range
  121. unsigned char nodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  122. 0x00, 0x00, 0x00, 0x00, 0x00,
  123. 0x00, 0x00, 0x00, 0x00, 0x00,
  124. 0x00, 0x00, 0x00, 0x00, 0x00 };
  125. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  126. DHTBucket bucket(16, max, min, localNode);
  127. CPPUNIT_ASSERT(!bucket.isInRange(node));
  128. }
  129. {
  130. // nodeID is out of range: larger than this range
  131. unsigned char nodeID[] = { 0x10, 0x00, 0x00, 0x00, 0x00,
  132. 0x00, 0x00, 0x00, 0x00, 0x00,
  133. 0x00, 0x00, 0x00, 0x00, 0x00,
  134. 0x00, 0x00, 0x00, 0x00, 0x00 };
  135. SharedHandle<DHTNode> node = new DHTNode(nodeID);
  136. DHTBucket bucket(16, max, min, localNode);
  137. CPPUNIT_ASSERT(!bucket.isInRange(node));
  138. }
  139. }
  140. }
  141. void DHTBucketTest::testSplitAllowed()
  142. {
  143. {
  144. unsigned char localNodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  145. 0x00, 0x00, 0x00, 0x00, 0x00,
  146. 0x00, 0x00, 0x00, 0x00, 0x00,
  147. 0x00, 0x00, 0x00, 0x00, 0x00 };
  148. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  149. DHTBucket bucket(localNode);
  150. CPPUNIT_ASSERT(bucket.splitAllowed());
  151. }
  152. {
  153. unsigned char max[] = { 0xff, 0xff, 0xff, 0xff, 0xff,
  154. 0xff, 0xff, 0xff, 0xff, 0xff,
  155. 0xff, 0xff, 0xff, 0xff, 0xff,
  156. 0xff, 0xff, 0xff, 0xff, 0xff };
  157. unsigned char min[] = { 0xe0, 0x00, 0x00, 0x00, 0x00,
  158. 0x00, 0x00, 0x00, 0x00, 0x00,
  159. 0x00, 0x00, 0x00, 0x00, 0x00,
  160. 0x00, 0x00, 0x00, 0x00, 0x00 };
  161. {
  162. unsigned char localNodeID[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  163. 0x00, 0x00, 0x00, 0x00, 0x00,
  164. 0x00, 0x00, 0x00, 0x00, 0x00,
  165. 0x00, 0x00, 0x00, 0x00, 0x00 };
  166. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  167. DHTBucket bucket(3, max, min, localNode);
  168. CPPUNIT_ASSERT(!bucket.splitAllowed());
  169. }
  170. {
  171. unsigned char localNodeID[] = { 0xe0, 0x00, 0x00, 0x00, 0x00,
  172. 0x00, 0x00, 0x00, 0x00, 0x00,
  173. 0x00, 0x00, 0x00, 0x00, 0x00,
  174. 0x00, 0x00, 0x00, 0x00, 0x01 };
  175. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  176. DHTBucket bucket(3, max, min, localNode);
  177. CPPUNIT_ASSERT(bucket.splitAllowed());
  178. }
  179. }
  180. }
  181. void DHTBucketTest::testSplit()
  182. {
  183. unsigned char localNodeID[DHT_ID_LENGTH];
  184. memset(localNodeID, 0, DHT_ID_LENGTH);
  185. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  186. {
  187. DHTBucket bucket(localNode);
  188. SharedHandle<DHTBucket> r = bucket.split();
  189. {
  190. unsigned char expectedRMax[] = { 0x7f, 0xff, 0xff, 0xff, 0xff,
  191. 0xff, 0xff, 0xff, 0xff, 0xff,
  192. 0xff, 0xff, 0xff, 0xff, 0xff,
  193. 0xff, 0xff, 0xff, 0xff, 0xff };
  194. unsigned char expectedRMin[DHT_ID_LENGTH];
  195. memset(expectedRMin, 0, DHT_ID_LENGTH);
  196. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedRMax, DHT_ID_LENGTH),
  197. Util::toHex(r->getMaxID(), DHT_ID_LENGTH));
  198. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedRMin, DHT_ID_LENGTH),
  199. Util::toHex(r->getMinID(), DHT_ID_LENGTH));
  200. CPPUNIT_ASSERT_EQUAL((size_t)1, r->getPrefixLength());
  201. }
  202. {
  203. unsigned char expectedLMax[DHT_ID_LENGTH];
  204. memset(expectedLMax, 0xff, DHT_ID_LENGTH);
  205. unsigned char expectedLMin[] = { 0x80, 0x00, 0x00, 0x00, 0x00,
  206. 0x00, 0x00, 0x00, 0x00, 0x00,
  207. 0x00, 0x00, 0x00, 0x00, 0x00,
  208. 0x00, 0x00, 0x00, 0x00, 0x00 };
  209. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedLMax, DHT_ID_LENGTH),
  210. Util::toHex(bucket.getMaxID(), DHT_ID_LENGTH));
  211. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedLMin, DHT_ID_LENGTH),
  212. Util::toHex(bucket.getMinID(), DHT_ID_LENGTH));
  213. CPPUNIT_ASSERT_EQUAL((size_t)1, bucket.getPrefixLength());
  214. }
  215. }
  216. {
  217. SharedHandle<DHTBucket> bucket = new DHTBucket(localNode);
  218. for(int i = 0; i < 159; ++i) {
  219. CPPUNIT_ASSERT(bucket->splitAllowed());
  220. SharedHandle<DHTBucket> t = bucket;
  221. bucket = bucket->split();
  222. CPPUNIT_ASSERT(!t->splitAllowed());
  223. }
  224. CPPUNIT_ASSERT(!bucket->splitAllowed());
  225. unsigned char expectedMax[] = { 0x00, 0x00, 0x00, 0x00, 0x00,
  226. 0x00, 0x00, 0x00, 0x00, 0x00,
  227. 0x00, 0x00, 0x00, 0x00, 0x00,
  228. 0x00, 0x00, 0x00, 0x00, 0x01 };
  229. unsigned char expectedMin[DHT_ID_LENGTH];
  230. memset(expectedMin, 0, DHT_ID_LENGTH);
  231. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedMax, DHT_ID_LENGTH),
  232. Util::toHex(bucket->getMaxID(), DHT_ID_LENGTH));
  233. CPPUNIT_ASSERT_EQUAL(Util::toHex(expectedMin, DHT_ID_LENGTH),
  234. Util::toHex(bucket->getMinID(), DHT_ID_LENGTH));
  235. CPPUNIT_ASSERT_EQUAL((size_t)159, bucket->getPrefixLength());
  236. }
  237. }
  238. static void createID(unsigned char* id, unsigned char firstChar, unsigned char lastChar)
  239. {
  240. memset(id, 0, DHT_ID_LENGTH);
  241. id[0] = firstChar;
  242. id[DHT_ID_LENGTH-1] = lastChar;
  243. }
  244. void DHTBucketTest::testAddNode()
  245. {
  246. unsigned char localNodeID[DHT_ID_LENGTH];
  247. memset(localNodeID, 0, DHT_ID_LENGTH);
  248. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  249. DHTBucket bucket(localNode);
  250. unsigned char id[DHT_ID_LENGTH];
  251. SharedHandle<DHTNode> nodes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  252. for(size_t i = 0; i < DHTBucket::K; ++i) {
  253. createID(id, 0xf0, i);
  254. nodes[i] = new DHTNode(id);
  255. CPPUNIT_ASSERT(bucket.addNode(nodes[i]));
  256. }
  257. createID(id, 0xf0, 0xff);
  258. SharedHandle<DHTNode> newNode = new DHTNode(id);
  259. CPPUNIT_ASSERT(!bucket.addNode(newNode));
  260. // nodes[0] is located at the tail of the bucket(least recent seen)
  261. nodes[0]->markBad();
  262. CPPUNIT_ASSERT(bucket.addNode(newNode));
  263. CPPUNIT_ASSERT(bucket.getNodes().back() == newNode);
  264. }
  265. void DHTBucketTest::testMoveToHead()
  266. {
  267. unsigned char localNodeID[DHT_ID_LENGTH];
  268. memset(localNodeID, 0, DHT_ID_LENGTH);
  269. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  270. DHTBucket bucket(localNode);
  271. unsigned char id[DHT_ID_LENGTH];
  272. SharedHandle<DHTNode> nodes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  273. for(size_t i = 0; i < DHTBucket::K; ++i) {
  274. createID(id, 0xf0, i);
  275. nodes[i] = new DHTNode(id);
  276. CPPUNIT_ASSERT(bucket.addNode(nodes[i]));
  277. }
  278. bucket.moveToHead(nodes[DHTBucket::K-1]);
  279. CPPUNIT_ASSERT(bucket.getNodes().front() == nodes[DHTBucket::K-1]);
  280. }
  281. void DHTBucketTest::testMoveToTail()
  282. {
  283. unsigned char localNodeID[DHT_ID_LENGTH];
  284. memset(localNodeID, 0, DHT_ID_LENGTH);
  285. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  286. DHTBucket bucket(localNode);
  287. unsigned char id[DHT_ID_LENGTH];
  288. SharedHandle<DHTNode> nodes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  289. for(size_t i = 0; i < DHTBucket::K; ++i) {
  290. createID(id, 0xf0, i);
  291. nodes[i] = new DHTNode(id);
  292. CPPUNIT_ASSERT(bucket.addNode(nodes[i]));
  293. }
  294. bucket.moveToTail(nodes[0]);
  295. CPPUNIT_ASSERT(bucket.getNodes().back() == nodes[0]);
  296. }
  297. void DHTBucketTest::testGetGoodNodes()
  298. {
  299. unsigned char localNodeID[DHT_ID_LENGTH];
  300. memset(localNodeID, 0, DHT_ID_LENGTH);
  301. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  302. DHTBucket bucket(localNode);
  303. unsigned char id[DHT_ID_LENGTH];
  304. SharedHandle<DHTNode> nodes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  305. for(size_t i = 0; i < DHTBucket::K; ++i) {
  306. createID(id, 0xf0, i);
  307. nodes[i] = new DHTNode(id);
  308. nodes[i]->setPort(6881+i);
  309. CPPUNIT_ASSERT(bucket.addNode(nodes[i]));
  310. }
  311. nodes[3]->markBad();
  312. nodes[5]->markBad();
  313. std::deque<SharedHandle<DHTNode> > goodNodes = bucket.getGoodNodes();
  314. CPPUNIT_ASSERT_EQUAL((size_t)6, goodNodes.size());
  315. CPPUNIT_ASSERT_EQUAL((uint16_t)6881, goodNodes[0]->getPort());
  316. CPPUNIT_ASSERT_EQUAL((uint16_t)6882, goodNodes[1]->getPort());
  317. CPPUNIT_ASSERT_EQUAL((uint16_t)6883, goodNodes[2]->getPort());
  318. CPPUNIT_ASSERT_EQUAL((uint16_t)6885, goodNodes[3]->getPort());
  319. CPPUNIT_ASSERT_EQUAL((uint16_t)6887, goodNodes[4]->getPort());
  320. CPPUNIT_ASSERT_EQUAL((uint16_t)6888, goodNodes[5]->getPort());
  321. }
  322. void DHTBucketTest::testCacheNode()
  323. {
  324. unsigned char localNodeID[DHT_ID_LENGTH];
  325. memset(localNodeID, 0, DHT_ID_LENGTH);
  326. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  327. DHTBucket bucket(localNode);
  328. SharedHandle<DHTNode> n1 = new DHTNode();
  329. SharedHandle<DHTNode> n2 = new DHTNode();
  330. SharedHandle<DHTNode> n3 = new DHTNode();
  331. bucket.cacheNode(n1);
  332. bucket.cacheNode(n2);
  333. CPPUNIT_ASSERT_EQUAL((size_t)2, bucket.getCachedNodes().size());
  334. CPPUNIT_ASSERT(n2 == bucket.getCachedNodes()[0]);
  335. bucket.cacheNode(n3);
  336. CPPUNIT_ASSERT_EQUAL((size_t)2, bucket.getCachedNodes().size());
  337. CPPUNIT_ASSERT(n3 == bucket.getCachedNodes()[0]);
  338. CPPUNIT_ASSERT(n2 == bucket.getCachedNodes()[1]);
  339. }
  340. void DHTBucketTest::testDropNode()
  341. {
  342. unsigned char localNodeID[DHT_ID_LENGTH];
  343. memset(localNodeID, 0, DHT_ID_LENGTH);
  344. SharedHandle<DHTNode> localNode = new DHTNode(localNodeID);
  345. DHTBucket bucket(localNode);
  346. unsigned char id[DHT_ID_LENGTH];
  347. SharedHandle<DHTNode> nodes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  348. for(size_t i = 0; i < DHTBucket::K; ++i) {
  349. createID(id, 0xf0, i);
  350. nodes[i] = new DHTNode(id);
  351. nodes[i]->setPort(6881+i);
  352. CPPUNIT_ASSERT(bucket.addNode(nodes[i]));
  353. }
  354. SharedHandle<DHTNode> cachedNode1 = new DHTNode();
  355. SharedHandle<DHTNode> cachedNode2 = new DHTNode();
  356. bucket.dropNode(nodes[3]);
  357. // nothing happens because the replacement cache is empty.
  358. {
  359. std::deque<SharedHandle<DHTNode> > tnodes = bucket.getNodes();
  360. CPPUNIT_ASSERT_EQUAL((size_t)8, tnodes.size());
  361. CPPUNIT_ASSERT(nodes[3] == tnodes[3]);
  362. }
  363. bucket.cacheNode(cachedNode1);
  364. bucket.cacheNode(cachedNode2);
  365. bucket.dropNode(nodes[3]);
  366. {
  367. std::deque<SharedHandle<DHTNode> > tnodes = bucket.getNodes();
  368. CPPUNIT_ASSERT_EQUAL((size_t)8, tnodes.size());
  369. CPPUNIT_ASSERT(tnodes.end() == std::find(tnodes.begin(), tnodes.end(), nodes[3]));
  370. CPPUNIT_ASSERT(cachedNode2 == tnodes[7]);
  371. }
  372. CPPUNIT_ASSERT_EQUAL((size_t)1, bucket.getCachedNodes().size());
  373. CPPUNIT_ASSERT(cachedNode1 == bucket.getCachedNodes()[0]);
  374. }
  375. } // namespace aria2