TestReconfig.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <cppunit/extensions/HelperMacros.h>
  19. #include <sys/types.h>
  20. #include <netinet/in.h>
  21. #include <errno.h>
  22. #include <iostream>
  23. #include <sstream>
  24. #include <arpa/inet.h>
  25. #include <exception>
  26. #include <stdlib.h>
  27. extern "C" {
  28. #include <src/addrvec.h>
  29. }
  30. #include "Util.h"
  31. #include "LibCMocks.h"
  32. #include "ZKMocks.h"
  33. using namespace std;
  34. static const int portOffset = 2000;
  35. class Client
  36. {
  37. private:
  38. // Member variables
  39. zhandle_t *zh;
  40. unsigned int seed;
  41. public:
  42. /**
  43. * Create a client with given connection host string and add to our internal
  44. * vector of clients. These are disconnected and cleaned up in tearDown().
  45. */
  46. Client(const string hosts, unsigned int seed) :
  47. seed((seed * seed) + 0xAFAFAFAF)
  48. {
  49. reSeed();
  50. zh = zookeeper_init(hosts.c_str(),0,1000,0,0,0);
  51. CPPUNIT_ASSERT(zh);
  52. // Set the flag to disable ZK from reconnecting to a different server.
  53. // Our reconfig test case will do explicit server shuffling through
  54. // zoo_cycle_next_server, and the reconnection attempts would interfere
  55. // with the server states the tests cases assume.
  56. zh->disable_reconnection_attempt = 1;
  57. reSeed();
  58. cycleNextServer();
  59. }
  60. void close()
  61. {
  62. zookeeper_close(zh);
  63. zh = NULL;
  64. }
  65. bool isReconfig()
  66. {
  67. return zh->reconfig != 0;
  68. }
  69. /**
  70. * re-seed this client with it's own previously generated seed so its
  71. * random choices are unique and separate from the other clients
  72. */
  73. void reSeed()
  74. {
  75. srandom(seed);
  76. srand48(seed);
  77. }
  78. /**
  79. * Get the server that this client is currently connected to.
  80. */
  81. string getServer()
  82. {
  83. const char* addrstring = zoo_get_current_server(zh);
  84. return string(addrstring);
  85. }
  86. /**
  87. * Get the server this client is currently connected to with no port
  88. * specification.
  89. */
  90. string getServerNoPort()
  91. {
  92. string addrstring = getServer();
  93. size_t found = addrstring.find_last_of(":");
  94. CPPUNIT_ASSERT(found != string::npos);
  95. // ipv6 address case (to remove leading and trailing bracket)
  96. if (addrstring.find("[") != string::npos)
  97. {
  98. return addrstring.substr(1, found-2);
  99. }
  100. else
  101. {
  102. return addrstring.substr(0, found);
  103. }
  104. }
  105. /**
  106. * Get the port of the server this client is currently connected to.
  107. */
  108. uint32_t getServerPort()
  109. {
  110. string addrstring = getServer();
  111. size_t found = addrstring.find_last_of(":");
  112. CPPUNIT_ASSERT(found != string::npos);
  113. string portStr = addrstring.substr(found+1);
  114. stringstream ss(portStr);
  115. uint32_t port;
  116. ss >> port;
  117. CPPUNIT_ASSERT(port >= portOffset);
  118. return port;
  119. }
  120. /**
  121. * Cycle to the next available server on the next connect attempt. It also
  122. * calls into getServer (above) to return the server connected to.
  123. */
  124. string cycleNextServer()
  125. {
  126. zoo_cycle_next_server(zh);
  127. return getServer();
  128. }
  129. void cycleUntilServer(const string requested)
  130. {
  131. // Call cycleNextServer until the one it's connected to is the one
  132. // specified (disregarding port).
  133. string first;
  134. while(true)
  135. {
  136. string next = cycleNextServer();
  137. if (first.empty())
  138. {
  139. first = next;
  140. }
  141. // Else we've looped around!
  142. else if (first == next)
  143. {
  144. CPPUNIT_ASSERT(false);
  145. }
  146. // Strip port off
  147. string server = getServerNoPort();
  148. // If it matches the requested host we're now 'connected' to the right host
  149. if (server == requested)
  150. {
  151. break;
  152. }
  153. }
  154. }
  155. /**
  156. * Set servers for this client.
  157. */
  158. void setServers(const string new_hosts)
  159. {
  160. int rc = zoo_set_servers(zh, new_hosts.c_str());
  161. CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
  162. }
  163. /**
  164. * Set servers for this client and validate reconfig value matches expected.
  165. */
  166. void setServersAndVerifyReconfig(const string new_hosts, bool is_reconfig)
  167. {
  168. setServers(new_hosts);
  169. CPPUNIT_ASSERT_EQUAL(is_reconfig, isReconfig());
  170. }
  171. /**
  172. * Sets the server list this client is connecting to AND if this requires
  173. * the client to be reconfigured (as dictated by internal client policy)
  174. * then it will trigger a call to cycleNextServer.
  175. */
  176. void setServersAndCycleIfNeeded(const string new_hosts)
  177. {
  178. setServers(new_hosts);
  179. if (isReconfig())
  180. {
  181. cycleNextServer();
  182. }
  183. }
  184. };
  185. class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture
  186. {
  187. CPPUNIT_TEST_SUITE(Zookeeper_reconfig);
  188. // Test cases
  189. CPPUNIT_TEST(testcycleNextServer);
  190. CPPUNIT_TEST(testMigrateOrNot);
  191. CPPUNIT_TEST(testMigrationCycle);
  192. CPPUNIT_TEST(testAddrVecContainsIPv4);
  193. #ifdef AF_INET6
  194. CPPUNIT_TEST(testAddrVecContainsIPv6);
  195. #endif
  196. // In threaded mode each 'create' is a thread -- it's not practical to create
  197. // 10,000 threads to test load balancing. The load balancing code can easily
  198. // be tested in single threaded mode as concurrency doesn't affect the algorithm.
  199. #ifndef THREADED
  200. CPPUNIT_TEST(testMigrateProbability);
  201. CPPUNIT_TEST(testLoadBalancing);
  202. #endif
  203. CPPUNIT_TEST_SUITE_END();
  204. FILE *logfile;
  205. double slackPercent;
  206. static const int numClients = 10000;
  207. static const int portOffset = 2000;
  208. vector<Client> clients;
  209. vector<uint32_t> numClientsPerHost;
  210. public:
  211. Zookeeper_reconfig() :
  212. slackPercent(10.0)
  213. {
  214. logfile = openlogfile("Zookeeper_reconfig");
  215. }
  216. ~Zookeeper_reconfig()
  217. {
  218. if (logfile)
  219. {
  220. fflush(logfile);
  221. fclose(logfile);
  222. logfile = 0;
  223. }
  224. }
  225. void setUp()
  226. {
  227. zoo_set_log_stream(logfile);
  228. zoo_deterministic_conn_order(1);
  229. numClientsPerHost.resize(numClients);
  230. }
  231. void tearDown()
  232. {
  233. for (unsigned int i = 0; i < clients.size(); i++)
  234. {
  235. clients.at(i).close();
  236. }
  237. }
  238. /**
  239. * Create a client with given connection host string and add to our internal
  240. * vector of clients. These are disconnected and cleaned up in tearDown().
  241. */
  242. Client& createClient(const string hosts)
  243. {
  244. Client client(hosts, clients.size());
  245. clients.push_back(client);
  246. return clients.back();
  247. }
  248. /**
  249. * Same as createClient(hosts) only it takes a specific host that this client
  250. * should simulate being connected to.
  251. */
  252. Client& createClient(const string hosts, const string host)
  253. {
  254. // Ensure requested host is in the list
  255. size_t found = hosts.find(host);
  256. CPPUNIT_ASSERT(found != hosts.npos);
  257. Client client(hosts, clients.size());
  258. client.cycleUntilServer(host);
  259. clients.push_back(client);
  260. return clients.back();
  261. }
  262. /**
  263. * Create a connection host list starting at 'start' and stopping at 'stop'
  264. * where start >= stop. This creates a connection string with host:port pairs
  265. * separated by commas. The given 'octet' is the starting octet that is used
  266. * as the last octet in the host's IP. This is decremented on each iteration.
  267. * Each port will be portOffset + octet.
  268. */
  269. string createHostList(uint32_t start, uint32_t stop = 1, uint32_t octet = 0)
  270. {
  271. if (octet == 0)
  272. {
  273. octet = start;
  274. }
  275. stringstream ss;
  276. for (uint32_t i = start; i >= stop; i--, octet--)
  277. {
  278. ss << "10.10.10." << octet << ":" << portOffset + octet;
  279. if (i > stop)
  280. {
  281. ss << ", ";
  282. }
  283. }
  284. return ss.str();
  285. }
  286. /**
  287. * Gets the lower bound of the number of clients per server that we expect
  288. * based on the probabilistic load balancing algorithm implemented by the
  289. * client code.
  290. */
  291. double lowerboundClientsPerServer(int numClients, int numServers)
  292. {
  293. return (1 - slackPercent/100.0) * numClients / numServers;
  294. }
  295. /**
  296. * Gets the upper bound of the number of clients per server that we expect
  297. * based on the probabilistic load balancing algorithm implemented by the
  298. * client code.
  299. */
  300. double upperboundClientsPerServer(int numClients, int numServers)
  301. {
  302. return (1 + slackPercent/100.0) * numClients / numServers;
  303. }
  304. /**
  305. * Update all the clients to use a new list of servers. This will also cause
  306. * the client to cycle to the next server as needed (e.g. due to a reconfig).
  307. * It then updates the number of clients connected to the server based on
  308. * this change.
  309. *
  310. * Afterwards it validates that all of the servers have the correct amount of
  311. * clients based on the probabilistic load balancing algorithm.
  312. */
  313. void updateAllClientsAndServers(int start, int stop = 1)
  314. {
  315. string newServers = createHostList(start, stop);
  316. int numServers = start - stop + 1;
  317. for (int i = 0; i < numClients; i++) {
  318. Client &client = clients.at(i);
  319. client.reSeed();
  320. client.setServersAndCycleIfNeeded(newServers);
  321. numClientsPerHost.at(client.getServerPort() - portOffset - 1)++;
  322. }
  323. int offset = stop - 1;
  324. for (int index = offset; index < numServers; index++) {
  325. if (numClientsPerHost.at(index) > upperboundClientsPerServer(numClients, numServers))
  326. {
  327. cout << "INDEX=" << index << " too many -- actual=" << numClientsPerHost.at(index)
  328. << " expected=" << upperboundClientsPerServer(numClients, numServers) << endl;
  329. }
  330. CPPUNIT_ASSERT(numClientsPerHost.at(index) <= upperboundClientsPerServer(numClients, numServers));
  331. if (numClientsPerHost.at(index) < lowerboundClientsPerServer(numClients, numServers))
  332. {
  333. cout << "INDEX=" << index << " too few -- actual=" << numClientsPerHost.at(index)
  334. << " expected=" << lowerboundClientsPerServer(numClients, numServers) << endl;
  335. }
  336. CPPUNIT_ASSERT(numClientsPerHost.at(index) >= lowerboundClientsPerServer(numClients, numServers));
  337. numClientsPerHost.at(index) = 0; // prepare for next test
  338. }
  339. }
  340. /*-------------------------------------------------------------------------*
  341. * TESTCASES
  342. *------------------------------------------------------------------------*/
  343. /**
  344. * Very basic sunny day test to ensure basic functionality of zoo_set_servers
  345. * and zoo_cycle_next_server.
  346. */
  347. void testcycleNextServer()
  348. {
  349. const string initial_hosts = createHostList(10); // 2010..2001
  350. const string new_hosts = createHostList(4); // 2004..2001
  351. Client &client = createClient(initial_hosts);
  352. client.setServersAndVerifyReconfig(new_hosts, true);
  353. for (int i = 0; i < 10; i++)
  354. {
  355. string next = client.cycleNextServer();
  356. }
  357. }
  358. /**
  359. * Test the migration policy implicit within the probabilistic load balancing
  360. * algorithm the Client implements. Tests all the corner cases whereby the
  361. * list of servers is decreased, increased, and stays the same. Also combines
  362. * various combinations of the currently connected server being in the new
  363. * configuration and not.
  364. */
  365. void testMigrateOrNot()
  366. {
  367. const string initial_hosts = createHostList(4); // 2004..2001
  368. Client &client = createClient(initial_hosts, "10.10.10.3");
  369. // Ensemble size decreasing, my server is in the new list
  370. client.setServersAndVerifyReconfig(createHostList(3), false);
  371. // Ensemble size decreasing, my server is NOT in the new list
  372. client.setServersAndVerifyReconfig(createHostList(2), true);
  373. // Ensemble size stayed the same, my server is NOT in the new list
  374. client.setServersAndVerifyReconfig(createHostList(2), true);
  375. // Ensemble size increased, my server is not in the new ensemble
  376. client.setServers(createHostList(4));
  377. client.cycleUntilServer("10.10.10.1");
  378. client.setServersAndVerifyReconfig(createHostList(7,2), true);
  379. }
  380. /**
  381. * This tests that as a client is in reconfig mode it will properly try to
  382. * connect to all the new servers first. Then it will try to connect to all
  383. * the 'old' servers that are staying in the new configuration. Finally it
  384. * will fallback to the normal behavior of trying servers in round-robin.
  385. */
  386. void testMigrationCycle()
  387. {
  388. int num_initial = 4;
  389. const string initial_hosts = createHostList(num_initial); // {2004..2001}
  390. int num_new = 10;
  391. string new_hosts = createHostList(12, 3); // {2012..2003}
  392. // servers from the old list that appear in the new list {2004..2003}
  393. int num_staying = 2;
  394. string oldStaying = createHostList(4, 3);
  395. // servers in the new list that are not in the old list {2012..2005}
  396. int num_coming = 8;
  397. string newComing = createHostList(12, 5);
  398. // Ensemble in increasing in size, my server is not in the new ensemble
  399. // load on the old servers must be decreased, so must connect to one of
  400. // new servers (pNew = 1)
  401. Client &client = createClient(initial_hosts, "10.10.10.1");
  402. client.setServersAndVerifyReconfig(new_hosts, true);
  403. // Since we're in reconfig mode, next connect should be from new list
  404. // We should try all the new servers *BEFORE* trying any old servers
  405. string seen;
  406. for (int i = 0; i < num_coming; i++) {
  407. client.cycleNextServer();
  408. // Assert next server is in the 'new' list
  409. stringstream next;
  410. next << client.getServerNoPort() << ":" << client.getServerPort();
  411. size_t found = newComing.find(next.str());
  412. CPPUNIT_ASSERT_MESSAGE(next.str() + " not in newComing list",
  413. found != string::npos);
  414. // Assert not in seen list then append
  415. found = seen.find(next.str());
  416. CPPUNIT_ASSERT_MESSAGE(next.str() + " in seen list",
  417. found == string::npos);
  418. seen += next.str() + ", ";
  419. }
  420. // Now it should start connecting to the old servers
  421. seen.clear();
  422. for (int i = 0; i < num_staying; i++) {
  423. client.cycleNextServer();
  424. // Assert it's in the old list
  425. stringstream next;
  426. next << client.getServerNoPort() << ":" << client.getServerPort();
  427. size_t found = oldStaying.find(next.str());
  428. CPPUNIT_ASSERT(found != string::npos);
  429. // Assert not in seen list then append
  430. found = seen.find(next.str());
  431. CPPUNIT_ASSERT(found == string::npos);
  432. seen += next.str() + ", ";
  433. }
  434. // NOW it goes back to normal as we've tried all the new and old
  435. string first = client.cycleNextServer();
  436. for (int i = 0; i < num_new - 1; i++) {
  437. client.cycleNextServer();
  438. }
  439. CPPUNIT_ASSERT_EQUAL(first, client.cycleNextServer());
  440. }
  441. /**
  442. * Test the migration probability to ensure that it conforms to our expected
  443. * lower and upper bounds of the number of clients per server as we are
  444. * reconfigured.
  445. *
  446. * In this case, the list of servers is increased and the client's server is
  447. * in the new list. Whether to move or not depends on the difference of
  448. * server sizes with probability 1 - |old|/|new| the client disconnects.
  449. *
  450. * In the test below 1-9/10 = 1/10 chance of disconnecting
  451. */
  452. void testMigrateProbability()
  453. {
  454. const string initial_hosts = createHostList(9); // 10.10.10.9:2009...10.10.10.1:2001
  455. string new_hosts = createHostList(10); // 10.10.10.10:2010...10.10.10.1:2001
  456. uint32_t numDisconnects = 0;
  457. for (int i = 0; i < numClients; i++) {
  458. Client &client = createClient(initial_hosts, "10.10.10.3");
  459. client.setServers(new_hosts);
  460. if (client.isReconfig())
  461. {
  462. numDisconnects++;
  463. }
  464. }
  465. // should be numClients/10 in expectation, we test that it's numClients/10 +- slackPercent
  466. CPPUNIT_ASSERT(numDisconnects < upperboundClientsPerServer(numClients, 10));
  467. }
  468. /**
  469. * Tests the probabilistic load balancing algorithm implemented by the Client
  470. * code.
  471. *
  472. * Test strategy:
  473. *
  474. * (1) Start with 9 servers and 10,000 clients. Remove a server, update
  475. * everything, and ensure that the clients are redistributed properly.
  476. *
  477. * (2) Remove two more nodes and repeat the same validations of proper client
  478. * redistribution. Ensure no clients are connected to the two removed
  479. * nodes.
  480. *
  481. * (3) Remove the first server in the list and simultaneously add the three
  482. * previously removed servers. Ensure everything is redistributed and
  483. * no clients are connected to the one missing node.
  484. *
  485. * (4) Add the one missing server back into the mix and validate.
  486. */
  487. void testLoadBalancing()
  488. {
  489. zoo_deterministic_conn_order(0);
  490. uint32_t numServers = 9;
  491. const string initial_hosts = createHostList(numServers); // 10.10.10.9:2009...10.10.10.1:2001
  492. // Create connections to servers
  493. for (int i = 0; i < numClients; i++) {
  494. Client &client = createClient(initial_hosts);
  495. numClientsPerHost.at(client.getServerPort() - portOffset - 1)++;
  496. }
  497. for (uint32_t i = 0; i < numServers; i++) {
  498. CPPUNIT_ASSERT(numClientsPerHost.at(i) <= upperboundClientsPerServer(numClients, numServers));
  499. CPPUNIT_ASSERT(numClientsPerHost.at(i) >= lowerboundClientsPerServer(numClients, numServers));
  500. numClientsPerHost.at(i) = 0; // prepare for next test
  501. }
  502. // remove last server
  503. numServers = 8;
  504. updateAllClientsAndServers(numServers);
  505. CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers));
  506. // Remove two more nodes
  507. numServers = 6;
  508. updateAllClientsAndServers(numServers);
  509. CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers));
  510. CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers+1));
  511. CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers+2));
  512. // remove host 0 (first one in list) and add back 6, 7, and 8
  513. numServers = 8;
  514. updateAllClientsAndServers(numServers, 1);
  515. CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(0));
  516. // add back host number 0
  517. numServers = 9;
  518. updateAllClientsAndServers(numServers);
  519. }
  520. /**
  521. * This tests that client can detect server's ipv4 address change.
  522. *
  523. * (1) We generate some address and put in addr, which saddr point to
  524. * (2) Add all addresses that differ by one bit from the source
  525. * (3) Add same address, but set ipv6 protocol
  526. * (4) Ensure, that our address is not equal to any of generated,
  527. * and that it equals to itself
  528. */
  529. void testAddrVecContainsIPv4() {
  530. addrvec_t vec;
  531. addrvec_init(&vec);
  532. sockaddr_storage addr;
  533. sockaddr_in* saddr = (sockaddr_in*)&addr;
  534. saddr->sin_family = AF_INET;
  535. saddr->sin_port = htons((u_short)1234);
  536. saddr->sin_addr.s_addr = INADDR_ANY;
  537. CPPUNIT_ASSERT(sizeof(saddr->sin_addr.s_addr) == 4);
  538. for (int i = 0; i < 32; i++) {
  539. saddr->sin_addr.s_addr ^= (1 << i);
  540. addrvec_append(&vec, &addr);
  541. saddr->sin_addr.s_addr ^= (1 << i);
  542. }
  543. saddr->sin_family = AF_INET6;
  544. addrvec_append(&vec, &addr);
  545. saddr->sin_family = AF_INET;
  546. CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr));
  547. addrvec_append(&vec, &addr);
  548. CPPUNIT_ASSERT(addrvec_contains(&vec, &addr));
  549. addrvec_free(&vec);
  550. }
  551. /**
  552. * This tests that client can detect server's ipv6 address change.
  553. *
  554. * Same logic as in previous testAddrVecContainsIPv4 method,
  555. * but we keep in mind, that ipv6 is 128-bit long.
  556. */
  557. #ifdef AF_INET6
  558. void testAddrVecContainsIPv6() {
  559. addrvec_t vec;
  560. addrvec_init(&vec);
  561. sockaddr_storage addr;
  562. sockaddr_in6* saddr = (sockaddr_in6*)&addr;
  563. saddr->sin6_family = AF_INET6;
  564. saddr->sin6_port = htons((u_short)1234);
  565. saddr->sin6_addr = in6addr_any;
  566. CPPUNIT_ASSERT(sizeof(saddr->sin6_addr.s6_addr) == 16);
  567. for (int i = 0; i < 16; i++) {
  568. for (int j = 0; j < 8; j++) {
  569. saddr->sin6_addr.s6_addr[i] ^= (1 << j);
  570. addrvec_append(&vec, &addr);
  571. saddr->sin6_addr.s6_addr[i] ^= (1 << j);
  572. }
  573. }
  574. saddr->sin6_family = AF_INET;
  575. addrvec_append(&vec, &addr);
  576. saddr->sin6_family = AF_INET6;
  577. CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr));
  578. addrvec_append(&vec, &addr);
  579. CPPUNIT_ASSERT(addrvec_contains(&vec, &addr));
  580. addrvec_free(&vec);
  581. }
  582. #endif
  583. };
  584. CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_reconfig);