dhcp_parsers_unittest.cc 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259
  1. // Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <config/ccsession.h>
  16. #include <dhcp/option.h>
  17. #include <dhcp/option_custom.h>
  18. #include <dhcp/option_int.h>
  19. #include <dhcpsrv/cfgmgr.h>
  20. #include <dhcpsrv/subnet.h>
  21. #include <dhcpsrv/dhcp_parsers.h>
  22. #include <dhcpsrv/tests/test_libraries.h>
  23. #include <exceptions/exceptions.h>
  24. #include <hooks/hooks_manager.h>
  25. #include <gtest/gtest.h>
  26. #include <boost/foreach.hpp>
  27. #include <boost/pointer_cast.hpp>
  28. #include <map>
  29. #include <string>
  30. using namespace std;
  31. using namespace isc;
  32. using namespace isc::config;
  33. using namespace isc::data;
  34. using namespace isc::dhcp;
  35. using namespace isc::hooks;
  36. namespace {
  37. /// @brief DHCP Parser test fixture class
  38. class DhcpParserTest : public ::testing::Test {
  39. public:
  40. /// @brief Constructor
  41. ///
  42. DhcpParserTest() {
  43. CfgMgr::instance().deleteActiveIfaces();
  44. }
  45. };
  46. /// @brief Check BooleanParser basic functionality.
  47. ///
  48. /// Verifies that the parser:
  49. /// 1. Does not allow empty for storage.
  50. /// 2. Rejects a non-boolean element.
  51. /// 3. Builds with a valid true value.
  52. /// 4. Bbuils with a valid false value.
  53. /// 5. Updates storage upon commit.
  54. TEST_F(DhcpParserTest, booleanParserTest) {
  55. const std::string name = "boolParm";
  56. // Verify that parser does not allow empty for storage.
  57. BooleanStoragePtr bs;
  58. EXPECT_THROW(BooleanParser(name, bs), isc::dhcp::DhcpConfigError);
  59. // Construct parser for testing.
  60. BooleanStoragePtr storage(new BooleanStorage());
  61. BooleanParser parser(name, storage);
  62. // Verify that parser with rejects a non-boolean element.
  63. ElementPtr wrong_element = Element::create("I am a string");
  64. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  65. // Verify that parser will build with a valid true value.
  66. bool test_value = true;
  67. ElementPtr element = Element::create(test_value);
  68. ASSERT_NO_THROW(parser.build(element));
  69. // Verify that commit updates storage.
  70. bool actual_value = !test_value;
  71. parser.commit();
  72. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  73. EXPECT_EQ(test_value, actual_value);
  74. // Verify that parser will build with a valid false value.
  75. test_value = false;
  76. element->setValue(test_value);
  77. EXPECT_NO_THROW(parser.build(element));
  78. // Verify that commit updates storage.
  79. actual_value = ~test_value;
  80. parser.commit();
  81. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  82. EXPECT_EQ(test_value, actual_value);
  83. }
  84. /// @brief Check StringParser basic functionality
  85. ///
  86. /// Verifies that the parser:
  87. /// 1. Does not allow empty for storage.
  88. /// 2. Builds with a nont string value.
  89. /// 3. Builds with a string value.
  90. /// 4. Updates storage upon commit.
  91. TEST_F(DhcpParserTest, stringParserTest) {
  92. const std::string name = "strParm";
  93. // Verify that parser does not allow empty for storage.
  94. StringStoragePtr bs;
  95. EXPECT_THROW(StringParser(name, bs), isc::dhcp::DhcpConfigError);
  96. // Construct parser for testing.
  97. StringStoragePtr storage(new StringStorage());
  98. StringParser parser(name, storage);
  99. // Verify that parser with accepts a non-string element.
  100. ElementPtr element = Element::create(9999);
  101. EXPECT_NO_THROW(parser.build(element));
  102. // Verify that commit updates storage.
  103. parser.commit();
  104. std::string actual_value;
  105. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  106. EXPECT_EQ("9999", actual_value);
  107. // Verify that parser will build with a string value.
  108. const std::string test_value = "test value";
  109. element = Element::create(test_value);
  110. ASSERT_NO_THROW(parser.build(element));
  111. // Verify that commit updates storage.
  112. parser.commit();
  113. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  114. EXPECT_EQ(test_value, actual_value);
  115. }
  116. /// @brief Check Uint32Parser basic functionality
  117. ///
  118. /// Verifies that the parser:
  119. /// 1. Does not allow empty for storage.
  120. /// 2. Rejects a non-integer element.
  121. /// 3. Rejects a negative value.
  122. /// 4. Rejects too large a value.
  123. /// 5. Builds with value of zero.
  124. /// 6. Builds with a value greater than zero.
  125. /// 7. Updates storage upon commit.
  126. TEST_F(DhcpParserTest, uint32ParserTest) {
  127. const std::string name = "intParm";
  128. // Verify that parser does not allow empty for storage.
  129. Uint32StoragePtr bs;
  130. EXPECT_THROW(Uint32Parser(name, bs), isc::dhcp::DhcpConfigError);
  131. // Construct parser for testing.
  132. Uint32StoragePtr storage(new Uint32Storage());
  133. Uint32Parser parser(name, storage);
  134. // Verify that parser with rejects a non-interger element.
  135. ElementPtr wrong_element = Element::create("I am a string");
  136. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  137. // Verify that parser with rejects a negative value.
  138. ElementPtr int_element = Element::create(-1);
  139. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  140. // Verify that parser with rejects too large a value provided we are on
  141. // 64-bit platform.
  142. if (sizeof(long) > sizeof(uint32_t)) {
  143. long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
  144. int_element->setValue(max);
  145. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  146. }
  147. // Verify that parser will build with value of zero.
  148. int test_value = 0;
  149. int_element->setValue((long)test_value);
  150. ASSERT_NO_THROW(parser.build(int_element));
  151. // Verify that commit updates storage.
  152. parser.commit();
  153. uint32_t actual_value = 0;
  154. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  155. EXPECT_EQ(test_value, actual_value);
  156. // Verify that parser will build with a valid positive value.
  157. test_value = 77;
  158. int_element->setValue((long)test_value);
  159. ASSERT_NO_THROW(parser.build(int_element));
  160. // Verify that commit updates storage.
  161. parser.commit();
  162. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  163. EXPECT_EQ(test_value, actual_value);
  164. }
  165. /// @brief Check InterfaceListParser basic functionality
  166. ///
  167. /// Verifies that the parser:
  168. /// 1. Does not allow empty for storage.
  169. /// 2. Does not allow name other than "interfaces"
  170. /// 3. Parses list of interfaces and adds them to CfgMgr
  171. /// 4. Parses wildcard interface name and sets a CfgMgr flag which indicates
  172. /// that server will listen on all interfaces.
  173. TEST_F(DhcpParserTest, interfaceListParserTest) {
  174. const std::string name = "interfaces";
  175. // Verify that parser constructor fails if parameter name isn't "interface"
  176. EXPECT_THROW(InterfaceListConfigParser("bogus_name"), isc::BadValue);
  177. boost::scoped_ptr<InterfaceListConfigParser>
  178. parser(new InterfaceListConfigParser(name));
  179. ElementPtr list_element = Element::createList();
  180. list_element->add(Element::create("eth0"));
  181. list_element->add(Element::create("eth1"));
  182. // Make sure there are no interfaces added yet.
  183. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
  184. ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
  185. // This should parse the configuration and add eth0 and eth1 to the list
  186. // of interfaces that server should listen on.
  187. parser->build(list_element);
  188. parser->commit();
  189. // Use CfgMgr instance to check if eth0 and eth1 was added, and that
  190. // eth2 was not added.
  191. CfgMgr& cfg_mgr = CfgMgr::instance();
  192. EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
  193. EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
  194. EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
  195. // Add keyword all to the configuration. This should activate all
  196. // interfaces, including eth2, even though it has not been explicitly
  197. // added.
  198. list_element->add(Element::create("*"));
  199. // Reset parser's state.
  200. parser.reset(new InterfaceListConfigParser(name));
  201. parser->build(list_element);
  202. parser->commit();
  203. EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
  204. EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
  205. EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
  206. }
  207. // Checks whether option space can be detected as vendor-id
  208. TEST_F(DhcpParserTest, vendorOptionSpace) {
  209. EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId(""));
  210. EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId("dhcp4"));
  211. EXPECT_EQ(0, SubnetConfigParser::optionSpaceToVendorId("vendor-"));
  212. EXPECT_EQ(1, SubnetConfigParser::optionSpaceToVendorId("vendor-1"));
  213. EXPECT_EQ(4491, SubnetConfigParser::optionSpaceToVendorId("vendor-4491"));
  214. EXPECT_EQ(12345678, SubnetConfigParser::optionSpaceToVendorId("vendor-12345678"));
  215. }
  216. /// @brief Test Implementation of abstract OptionDataParser class. Allows
  217. /// testing basic option parsing.
  218. class UtestOptionDataParser : public OptionDataParser {
  219. public:
  220. UtestOptionDataParser(const std::string&,
  221. OptionStoragePtr options, ParserContextPtr global_context)
  222. :OptionDataParser("", options, global_context) {
  223. }
  224. static OptionDataParser* factory(const std::string& param_name,
  225. OptionStoragePtr options, ParserContextPtr global_context) {
  226. return (new UtestOptionDataParser(param_name, options, global_context));
  227. }
  228. protected:
  229. // Dummy out last two params since test derivation doesn't use them.
  230. virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
  231. std::string&, uint32_t) {
  232. OptionDefinitionPtr def;
  233. // always return empty
  234. return (def);
  235. }
  236. };
  237. /// @brief Test Fixture class which provides basic structure for testing
  238. /// configuration parsing. This is essentially the same structure provided
  239. /// by dhcp servers.
  240. class ParseConfigTest : public ::testing::Test {
  241. public:
  242. /// @brief Constructor
  243. ParseConfigTest() {
  244. reset_context();
  245. }
  246. ~ParseConfigTest() {
  247. reset_context();
  248. }
  249. /// @brief Parses a configuration.
  250. ///
  251. /// Parse the given configuration, populating the context storage with
  252. /// the parsed elements.
  253. ///
  254. /// @param config_set is the set of elements to parse.
  255. /// @return returns an ConstElementPtr containing the numeric result
  256. /// code and outcome comment.
  257. isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
  258. config_set) {
  259. // Answer will hold the result.
  260. ConstElementPtr answer;
  261. if (!config_set) {
  262. answer = isc::config::createAnswer(1,
  263. string("Can't parse NULL config"));
  264. return (answer);
  265. }
  266. // option parsing must be done last, so save it if we hit if first
  267. ParserPtr option_parser;
  268. ConfigPair config_pair;
  269. try {
  270. // Iterate over the config elements.
  271. const std::map<std::string, ConstElementPtr>& values_map =
  272. config_set->mapValue();
  273. BOOST_FOREACH(config_pair, values_map) {
  274. // Create the parser based on element name.
  275. ParserPtr parser(createConfigParser(config_pair.first));
  276. // Options must be parsed last
  277. if (config_pair.first == "option-data") {
  278. option_parser = parser;
  279. } else {
  280. // Anything else we can call build straight away.
  281. parser->build(config_pair.second);
  282. parser->commit();
  283. }
  284. }
  285. // The option values parser is the next one to be run.
  286. std::map<std::string, ConstElementPtr>::const_iterator
  287. option_config = values_map.find("option-data");
  288. if (option_config != values_map.end()) {
  289. option_parser->build(option_config->second);
  290. option_parser->commit();
  291. }
  292. // Everything was fine. Configuration is successful.
  293. answer = isc::config::createAnswer(0, "Configuration committed.");
  294. } catch (const isc::Exception& ex) {
  295. answer = isc::config::createAnswer(1,
  296. string("Configuration parsing failed: ") + ex.what());
  297. } catch (...) {
  298. answer = isc::config::createAnswer(1,
  299. string("Configuration parsing failed"));
  300. }
  301. return (answer);
  302. }
  303. /// @brief Create an element parser based on the element name.
  304. ///
  305. /// Creates a parser for the appropriate element and stores a pointer to it
  306. /// in the appropriate class variable.
  307. ///
  308. /// Note that the method currently it only supports option-defs, option-data
  309. /// and hooks-libraries.
  310. ///
  311. /// @param config_id is the name of the configuration element.
  312. ///
  313. /// @return returns a shared pointer to DhcpConfigParser.
  314. ///
  315. /// @throw throws NotImplemented if element name isn't supported.
  316. ParserPtr createConfigParser(const std::string& config_id) {
  317. ParserPtr parser;
  318. if (config_id.compare("option-data") == 0) {
  319. parser.reset(new OptionDataListParser(config_id,
  320. parser_context_->options_,
  321. parser_context_,
  322. UtestOptionDataParser::factory));
  323. } else if (config_id.compare("option-def") == 0) {
  324. parser.reset(new OptionDefListParser(config_id,
  325. parser_context_->option_defs_));
  326. } else if (config_id.compare("hooks-libraries") == 0) {
  327. parser.reset(new HooksLibrariesParser(config_id));
  328. hooks_libraries_parser_ =
  329. boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
  330. } else if (config_id.compare("dhcp-ddns") == 0) {
  331. parser.reset(new D2ClientConfigParser(config_id));
  332. } else {
  333. isc_throw(NotImplemented,
  334. "Parser error: configuration parameter not supported: "
  335. << config_id);
  336. }
  337. return (parser);
  338. }
  339. /// @brief Convenience method for parsing a configuration
  340. ///
  341. /// Given a configuration string, convert it into Elements
  342. /// and parse them.
  343. /// @param config is the configuration string to parse
  344. ///
  345. /// @return retuns 0 if the configuration parsed successfully,
  346. /// non-zero otherwise failure.
  347. int parseConfiguration(const std::string& config) {
  348. int rcode_ = 1;
  349. // Turn config into elements.
  350. // Test json just to make sure its valid.
  351. ElementPtr json = Element::fromJSON(config);
  352. EXPECT_TRUE(json);
  353. if (json) {
  354. ConstElementPtr status = parseElementSet(json);
  355. ConstElementPtr comment = parseAnswer(rcode_, status);
  356. error_text_ = comment->stringValue();
  357. }
  358. return (rcode_);
  359. }
  360. /// @brief Find an option definition for a given space and code within
  361. /// the parser context.
  362. /// @param space is the space name of the desired option.
  363. /// @param code is the numeric "type" of the desired option.
  364. /// @return returns an OptionDefinitionPtr which points to the found
  365. /// definition or is empty.
  366. /// ASSERT_ tests don't work inside functions that return values
  367. OptionDefinitionPtr getOptionDef(std::string space, uint32_t code)
  368. {
  369. OptionDefinitionPtr def;
  370. OptionDefContainerPtr defs =
  371. parser_context_->option_defs_->getItems(space);
  372. // Should always be able to get definitions list even if it is empty.
  373. EXPECT_TRUE(defs);
  374. if (defs) {
  375. // Attempt to find desired definiton.
  376. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  377. const OptionDefContainerTypeRange& range = idx.equal_range(code);
  378. int cnt = std::distance(range.first, range.second);
  379. EXPECT_EQ(1, cnt);
  380. if (cnt == 1) {
  381. def = *(idx.begin());
  382. }
  383. }
  384. return (def);
  385. }
  386. /// @brief Find an option for a given space and code within the parser
  387. /// context.
  388. /// @param space is the space name of the desired option.
  389. /// @param code is the numeric "type" of the desired option.
  390. /// @return returns an OptionPtr which points to the found
  391. /// option or is empty.
  392. /// ASSERT_ tests don't work inside functions that return values
  393. OptionPtr getOptionPtr(std::string space, uint32_t code)
  394. {
  395. OptionPtr option_ptr;
  396. Subnet::OptionContainerPtr options =
  397. parser_context_->options_->getItems(space);
  398. // Should always be able to get options list even if it is empty.
  399. EXPECT_TRUE(options);
  400. if (options) {
  401. // Attempt to find desired option.
  402. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  403. const Subnet::OptionContainerTypeRange& range =
  404. idx.equal_range(code);
  405. int cnt = std::distance(range.first, range.second);
  406. EXPECT_EQ(1, cnt);
  407. if (cnt == 1) {
  408. Subnet::OptionDescriptor desc = *(idx.begin());
  409. option_ptr = desc.option;
  410. EXPECT_TRUE(option_ptr);
  411. }
  412. }
  413. return (option_ptr);
  414. }
  415. /// @brief Wipes the contents of the context to allowing another parsing
  416. /// during a given test if needed.
  417. void reset_context(){
  418. // Note set context universe to V6 as it has to be something.
  419. CfgMgr::instance().deleteSubnets4();
  420. CfgMgr::instance().deleteSubnets6();
  421. CfgMgr::instance().deleteOptionDefs();
  422. parser_context_.reset(new ParserContext(Option::V6));
  423. // Ensure no hooks libraries are loaded.
  424. HooksManager::unloadLibraries();
  425. // Set it to minimal, disabled config
  426. D2ClientConfigPtr tmp(new D2ClientConfig());
  427. CfgMgr::instance().setD2ClientConfig(tmp);
  428. }
  429. /// @brief Parsers used in the parsing of the configuration
  430. ///
  431. /// Allows the tests to interrogate the state of the parsers (if required).
  432. boost::shared_ptr<HooksLibrariesParser> hooks_libraries_parser_;
  433. /// @brief Parser context - provides storage for options and definitions
  434. ParserContextPtr parser_context_;
  435. /// @brief Error string if the parsing failed
  436. std::string error_text_;
  437. };
  438. /// @brief Check Basic parsing of option definitions.
  439. ///
  440. /// Note that this tests basic operation of the OptionDefinitionListParser and
  441. /// OptionDefinitionParser. It uses a simple configuration consisting of one
  442. /// one definition and verifies that it is parsed and committed to storage
  443. /// correctly.
  444. TEST_F(ParseConfigTest, basicOptionDefTest) {
  445. // Configuration string.
  446. std::string config =
  447. "{ \"option-def\": [ {"
  448. " \"name\": \"foo\","
  449. " \"code\": 100,"
  450. " \"type\": \"ipv4-address\","
  451. " \"array\": False,"
  452. " \"record-types\": \"\","
  453. " \"space\": \"isc\","
  454. " \"encapsulate\": \"\""
  455. " } ]"
  456. "}";
  457. // Verify that the configuration string parses.
  458. int rcode = parseConfiguration(config);
  459. ASSERT_TRUE(rcode == 0);
  460. // Verify that the option definition can be retrieved.
  461. OptionDefinitionPtr def = getOptionDef("isc", 100);
  462. ASSERT_TRUE(def);
  463. // Verify that the option definition is correct.
  464. EXPECT_EQ("foo", def->getName());
  465. EXPECT_EQ(100, def->getCode());
  466. EXPECT_FALSE(def->getArrayType());
  467. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
  468. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  469. }
  470. /// @brief Check Basic parsing of options.
  471. ///
  472. /// Note that this tests basic operation of the OptionDataListParser and
  473. /// OptionDataParser. It uses a simple configuration consisting of one
  474. /// one definition and matching option data. It verifies that the option
  475. /// is parsed and committed to storage correctly.
  476. TEST_F(ParseConfigTest, basicOptionDataTest) {
  477. // Configuration string.
  478. std::string config =
  479. "{ \"option-def\": [ {"
  480. " \"name\": \"foo\","
  481. " \"code\": 100,"
  482. " \"type\": \"ipv4-address\","
  483. " \"array\": False,"
  484. " \"record-types\": \"\","
  485. " \"space\": \"isc\","
  486. " \"encapsulate\": \"\""
  487. " } ], "
  488. " \"option-data\": [ {"
  489. " \"name\": \"foo\","
  490. " \"space\": \"isc\","
  491. " \"code\": 100,"
  492. " \"data\": \"192.0.2.0\","
  493. " \"csv-format\": True"
  494. " } ]"
  495. "}";
  496. // Verify that the configuration string parses.
  497. int rcode = parseConfiguration(config);
  498. ASSERT_TRUE(rcode == 0);
  499. // Verify that the option can be retrieved.
  500. OptionPtr opt_ptr = getOptionPtr("isc", 100);
  501. ASSERT_TRUE(opt_ptr);
  502. // Verify that the option definition is correct.
  503. std::string val = "type=100, len=4, data fields:\n "
  504. " #0 192.0.2.0 ( ipv4-address ) \n";
  505. EXPECT_EQ(val, opt_ptr->toText());
  506. }
  507. }; // Anonymous namespace
  508. /// These tests check basic operation of the HooksLibrariesParser.
  509. // hooks-libraries that do not contain anything.
  510. TEST_F(ParseConfigTest, noHooksLibrariesTest) {
  511. // Configuration with hooks-libraries not present.
  512. string config = "{ \"hooks-libraries\": [] }";
  513. // Verify that the configuration string parses.
  514. int rcode = parseConfiguration(config);
  515. ASSERT_TRUE(rcode == 0) << error_text_;
  516. // Check that the parser recorded no change to the current state
  517. // (as the test starts with no hooks libraries loaded).
  518. std::vector<std::string> libraries;
  519. bool changed;
  520. hooks_libraries_parser_->getLibraries(libraries, changed);
  521. EXPECT_TRUE(libraries.empty());
  522. EXPECT_FALSE(changed);
  523. // Load a single library and repeat the parse.
  524. vector<string> basic_library;
  525. basic_library.push_back(string(CALLOUT_LIBRARY_1));
  526. HooksManager::loadLibraries(basic_library);
  527. rcode = parseConfiguration(config);
  528. ASSERT_TRUE(rcode == 0) << error_text_;
  529. // This time the change should have been recorded.
  530. hooks_libraries_parser_->getLibraries(libraries, changed);
  531. EXPECT_TRUE(libraries.empty());
  532. EXPECT_TRUE(changed);
  533. // But repeating it again and we are back to no change.
  534. rcode = parseConfiguration(config);
  535. ASSERT_TRUE(rcode == 0) << error_text_;
  536. hooks_libraries_parser_->getLibraries(libraries, changed);
  537. EXPECT_TRUE(libraries.empty());
  538. EXPECT_FALSE(changed);
  539. }
  540. TEST_F(ParseConfigTest, validHooksLibrariesTest) {
  541. // Configuration string. This contains a set of valid libraries.
  542. const std::string quote("\"");
  543. const std::string comma(", ");
  544. const std::string config =
  545. std::string("{ ") +
  546. std::string("\"hooks-libraries\": [") +
  547. quote + std::string(CALLOUT_LIBRARY_1) + quote + comma +
  548. quote + std::string(CALLOUT_LIBRARY_2) + quote +
  549. std::string("]") +
  550. std::string("}");
  551. // Verify that the configuration string parses.
  552. int rcode = parseConfiguration(config);
  553. ASSERT_TRUE(rcode == 0) << error_text_;
  554. // Check that the parser holds two libraries and the configuration is
  555. // recorded as having changed.
  556. std::vector<std::string> libraries;
  557. bool changed;
  558. hooks_libraries_parser_->getLibraries(libraries, changed);
  559. EXPECT_EQ(2, libraries.size());
  560. EXPECT_TRUE(changed);
  561. // The expected libraries should be the list of libraries specified
  562. // in the given order.
  563. std::vector<std::string> expected;
  564. expected.push_back(CALLOUT_LIBRARY_1);
  565. expected.push_back(CALLOUT_LIBRARY_2);
  566. EXPECT_TRUE(expected == libraries);
  567. // Parse the string again.
  568. rcode = parseConfiguration(config);
  569. ASSERT_TRUE(rcode == 0) << error_text_;
  570. // The list has not changed, and this is what we should see.
  571. hooks_libraries_parser_->getLibraries(libraries, changed);
  572. EXPECT_EQ(2, libraries.size());
  573. EXPECT_FALSE(changed);
  574. }
  575. // Check with a set of libraries, some of which are invalid.
  576. TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
  577. /// @todo Initialize global library context to null
  578. // Configuration string. This contains an invalid library which should
  579. // trigger an error in the "build" stage.
  580. const std::string quote("\"");
  581. const std::string comma(", ");
  582. const std::string config =
  583. std::string("{ ") +
  584. std::string("\"hooks-libraries\": [") +
  585. quote + std::string(CALLOUT_LIBRARY_1) + quote + comma +
  586. quote + std::string(NOT_PRESENT_LIBRARY) + quote + comma +
  587. quote + std::string(CALLOUT_LIBRARY_2) + quote +
  588. std::string("]") +
  589. std::string("}");
  590. // Verify that the configuration fails to parse. (Syntactically it's OK,
  591. // but the library is invalid).
  592. int rcode = parseConfiguration(config);
  593. ASSERT_FALSE(rcode == 0) << error_text_;
  594. // Check that the message contains the library in error.
  595. EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
  596. "Error text returned from parse failure is " << error_text_;
  597. }
  598. /// @brief Checks that a valid, enabled D2 client configuration works correctly.
  599. TEST_F(ParseConfigTest, validD2Config) {
  600. // Configuration string containing valid values.
  601. std::string config_str =
  602. "{ \"dhcp-ddns\" :"
  603. " {"
  604. " \"enable-updates\" : true, "
  605. " \"server-ip\" : \"192.0.2.0\", "
  606. " \"server-port\" : 3432, "
  607. " \"ncr-protocol\" : \"UDP\", "
  608. " \"ncr-format\" : \"JSON\", "
  609. " \"always-include-fqdn\" : true, "
  610. " \"override-no-update\" : true, "
  611. " \"override-client-update\" : true, "
  612. " \"replace-client-name\" : true, "
  613. " \"generated-prefix\" : \"test.prefix\", "
  614. " \"qualifying-suffix\" : \"test.suffix.\" "
  615. " }"
  616. "}";
  617. // Verify that the configuration string parses.
  618. int rcode = parseConfiguration(config_str);
  619. ASSERT_TRUE(rcode == 0) << error_text_;
  620. // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
  621. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  622. D2ClientConfigPtr d2_client_config;
  623. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  624. ASSERT_TRUE(d2_client_config);
  625. // Verify that the configuration values are as expected.
  626. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  627. EXPECT_EQ("192.0.2.0", d2_client_config->getServerIp().toText());
  628. EXPECT_EQ(3432, d2_client_config->getServerPort());
  629. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  630. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  631. EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
  632. EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
  633. EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
  634. EXPECT_TRUE(d2_client_config->getReplaceClientName());
  635. EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
  636. EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
  637. // Another valid Configuration string.
  638. // This one is disabled, has IPV6 server ip, control flags false,
  639. // empty prefix/suffix
  640. std::string config_str2 =
  641. "{ \"dhcp-ddns\" :"
  642. " {"
  643. " \"enable-updates\" : false, "
  644. " \"server-ip\" : \"2001:db8::\", "
  645. " \"server-port\" : 43567, "
  646. " \"ncr-protocol\" : \"UDP\", "
  647. " \"ncr-format\" : \"JSON\", "
  648. " \"always-include-fqdn\" : false, "
  649. " \"override-no-update\" : false, "
  650. " \"override-client-update\" : false, "
  651. " \"replace-client-name\" : false, "
  652. " \"generated-prefix\" : \"\", "
  653. " \"qualifying-suffix\" : \"\" "
  654. " }"
  655. "}";
  656. // Verify that the configuration string parses.
  657. rcode = parseConfiguration(config_str2);
  658. ASSERT_TRUE(rcode == 0) << error_text_;
  659. // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
  660. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  661. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  662. ASSERT_TRUE(d2_client_config);
  663. // Verify that the configuration values are as expected.
  664. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  665. EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
  666. EXPECT_EQ(43567, d2_client_config->getServerPort());
  667. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  668. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  669. EXPECT_FALSE(d2_client_config->getAlwaysIncludeFqdn());
  670. EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
  671. EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
  672. EXPECT_FALSE(d2_client_config->getReplaceClientName());
  673. EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
  674. EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
  675. }
  676. /// @brief Checks that D2 client can be configured with enable flag of
  677. /// false only.
  678. TEST_F(ParseConfigTest, validDisabledD2Config) {
  679. // Configuration string. This contains a set of valid libraries.
  680. std::string config_str =
  681. "{ \"dhcp-ddns\" :"
  682. " {"
  683. " \"enable-updates\" : false"
  684. " }"
  685. "}";
  686. // Verify that the configuration string parses.
  687. int rcode = parseConfiguration(config_str);
  688. ASSERT_TRUE(rcode == 0) << error_text_;
  689. // Verify that DHCP-DDNS is disabled.
  690. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  691. // Make sure fetched config agrees.
  692. D2ClientConfigPtr d2_client_config;
  693. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  694. EXPECT_TRUE(d2_client_config);
  695. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  696. }
  697. /// @brief Check various invalid D2 client configurations.
  698. TEST_F(ParseConfigTest, invalidD2Config) {
  699. std::string invalid_configs[] = {
  700. // only the enable flag of true
  701. "{ \"dhcp-ddns\" :"
  702. " {"
  703. " \"enable-updates\" : true"
  704. " }"
  705. "}",
  706. // Missing server ip value
  707. "{ \"dhcp-ddns\" :"
  708. " {"
  709. " \"enable-updates\" : true, "
  710. //" \"server-ip\" : \"192.0.2.0\", "
  711. " \"server-port\" : 53001, "
  712. " \"ncr-protocol\" : \"UDP\", "
  713. " \"ncr-format\" : \"JSON\", "
  714. " \"always-include-fqdn\" : true, "
  715. " \"override-no-update\" : true, "
  716. " \"override-client-update\" : true, "
  717. " \"replace-client-name\" : true, "
  718. " \"generated-prefix\" : \"test.prefix\", "
  719. " \"qualifying-suffix\" : \"test.suffix.\" "
  720. " }"
  721. "}",
  722. // Invalid server ip value
  723. "{ \"dhcp-ddns\" :"
  724. " {"
  725. " \"enable-updates\" : true, "
  726. " \"server-ip\" : \"x192.0.2.0\", "
  727. " \"server-port\" : 53001, "
  728. " \"ncr-protocol\" : \"UDP\", "
  729. " \"ncr-format\" : \"JSON\", "
  730. " \"always-include-fqdn\" : true, "
  731. " \"override-no-update\" : true, "
  732. " \"override-client-update\" : true, "
  733. " \"replace-client-name\" : true, "
  734. " \"generated-prefix\" : \"test.prefix\", "
  735. " \"qualifying-suffix\" : \"test.suffix.\" "
  736. " }"
  737. "}",
  738. // Unknown protocol
  739. "{ \"dhcp-ddns\" :"
  740. " {"
  741. " \"enable-updates\" : true, "
  742. " \"server-ip\" : \"192.0.2.0\", "
  743. " \"server-port\" : 53001, "
  744. " \"ncr-protocol\" : \"Bogus\", "
  745. " \"ncr-format\" : \"JSON\", "
  746. " \"always-include-fqdn\" : true, "
  747. " \"override-no-update\" : true, "
  748. " \"override-client-update\" : true, "
  749. " \"replace-client-name\" : true, "
  750. " \"generated-prefix\" : \"test.prefix\", "
  751. " \"qualifying-suffix\" : \"test.suffix.\" "
  752. " }"
  753. "}",
  754. // Unsupported protocol
  755. "{ \"dhcp-ddns\" :"
  756. " {"
  757. " \"enable-updates\" : true, "
  758. " \"server-ip\" : \"192.0.2.0\", "
  759. " \"server-port\" : 53001, "
  760. " \"ncr-protocol\" : \"TCP\", "
  761. " \"ncr-format\" : \"JSON\", "
  762. " \"always-include-fqdn\" : true, "
  763. " \"override-no-update\" : true, "
  764. " \"override-client-update\" : true, "
  765. " \"replace-client-name\" : true, "
  766. " \"generated-prefix\" : \"test.prefix\", "
  767. " \"qualifying-suffix\" : \"test.suffix.\" "
  768. " }"
  769. "}",
  770. // Unknown format
  771. "{ \"dhcp-ddns\" :"
  772. " {"
  773. " \"enable-updates\" : true, "
  774. " \"server-ip\" : \"192.0.2.0\", "
  775. " \"server-port\" : 53001, "
  776. " \"ncr-protocol\" : \"UDP\", "
  777. " \"ncr-format\" : \"Bogus\", "
  778. " \"always-include-fqdn\" : true, "
  779. " \"override-no-update\" : true, "
  780. " \"override-client-update\" : true, "
  781. " \"replace-client-name\" : true, "
  782. " \"generated-prefix\" : \"test.prefix\", "
  783. " \"qualifying-suffix\" : \"test.suffix.\" "
  784. " }"
  785. "}",
  786. // Missig Port
  787. "{ \"dhcp-ddns\" :"
  788. " {"
  789. " \"enable-updates\" : true, "
  790. " \"server-ip\" : \"192.0.2.0\", "
  791. // " \"server-port\" : 53001, "
  792. " \"ncr-protocol\" : \"UDP\", "
  793. " \"ncr-format\" : \"JSON\", "
  794. " \"always-include-fqdn\" : true, "
  795. " \"override-no-update\" : true, "
  796. " \"override-client-update\" : true, "
  797. " \"replace-client-name\" : true, "
  798. " \"generated-prefix\" : \"test.prefix\", "
  799. " \"qualifying-suffix\" : \"test.suffix.\" "
  800. " }"
  801. "}",
  802. // stop
  803. ""
  804. };
  805. // Fetch the original config.
  806. D2ClientConfigPtr original_config;
  807. ASSERT_NO_THROW(original_config = CfgMgr::instance().getD2ClientConfig());
  808. // Iterate through the invalid configuration strings, attempting to
  809. // parse each one. They should fail to parse, but fail gracefully.
  810. D2ClientConfigPtr current_config;
  811. int i = 0;
  812. while (!invalid_configs[i].empty()) {
  813. // Verify that the configuration string parses without throwing.
  814. int rcode = parseConfiguration(invalid_configs[i]);
  815. // Verify that parse result indicates a parsing error.
  816. ASSERT_TRUE(rcode != 0) << "Invalid config #: " << i
  817. << " should not have passed!";
  818. // Verify that the "official" config still matches the original config.
  819. ASSERT_NO_THROW(current_config =
  820. CfgMgr::instance().getD2ClientConfig());
  821. EXPECT_EQ(*original_config, *current_config);
  822. ++i;
  823. }
  824. }
  825. /// @brief DHCP Configuration Parser Context test fixture.
  826. class ParserContextTest : public ::testing::Test {
  827. public:
  828. /// @brief Constructor
  829. ParserContextTest() { }
  830. /// @brief Check that the storages of the specific type hold the
  831. /// same value.
  832. ///
  833. /// This function assumes that the ref_values storage holds exactly
  834. /// one parameter called 'foo'.
  835. ///
  836. /// @param ref_values A storage holding reference value. In the typical
  837. /// case it is a storage held in the original context, which is assigned
  838. /// to another context.
  839. /// @param values A storage holding value to be checked.
  840. /// @tparam ContainerType A type of the storage.
  841. /// @tparam ValueType A type of the value in the container.
  842. template<typename ContainerType, typename ValueType>
  843. void checkValueEq(const boost::shared_ptr<ContainerType>& ref_values,
  844. const boost::shared_ptr<ContainerType>& values) {
  845. ASSERT_NO_THROW(values->getParam("foo"));
  846. EXPECT_EQ(ref_values->getParam("foo"), values->getParam("foo"));
  847. }
  848. /// @brief Check that the storages of the specific type hold different
  849. /// value.
  850. ///
  851. /// This function assumes that the ref_values storage holds exactly
  852. /// one parameter called 'foo'.
  853. ///
  854. /// @param ref_values A storage holding reference value. In the typical
  855. /// case it is a storage held in the original context, which is assigned
  856. /// to another context.
  857. /// @param values A storage holding value to be checked.
  858. /// @tparam ContainerType A type of the storage.
  859. /// @tparam ValueType A type of the value in the container.
  860. template<typename ContainerType, typename ValueType>
  861. void checkValueNeq(const boost::shared_ptr<ContainerType>& ref_values,
  862. const boost::shared_ptr<ContainerType>& values) {
  863. ASSERT_NO_THROW(values->getParam("foo"));
  864. EXPECT_NE(ref_values->getParam("foo"), values->getParam("foo"));
  865. }
  866. /// @brief Check that option definition storage in the context holds
  867. /// one option definition of the specified type.
  868. ///
  869. /// @param ctx A pointer to a context.
  870. /// @param opt_type Expected option type.
  871. void checkOptionDefinitionType(const ParserContext& ctx,
  872. const uint16_t opt_type) {
  873. OptionDefContainerPtr opt_defs =
  874. ctx.option_defs_->getItems("option-space");
  875. ASSERT_TRUE(opt_defs);
  876. OptionDefContainerTypeIndex& idx = opt_defs->get<1>();
  877. OptionDefContainerTypeRange range = idx.equal_range(opt_type);
  878. EXPECT_EQ(1, std::distance(range.first, range.second));
  879. }
  880. /// @brief Check that option storage in the context holds one option
  881. /// of the specified type.
  882. ///
  883. /// @param ctx A pointer to a context.
  884. /// @param opt_type Expected option type.
  885. void checkOptionType(const ParserContext& ctx, const uint16_t opt_type) {
  886. Subnet::OptionContainerPtr options =
  887. ctx.options_->getItems("option-space");
  888. ASSERT_TRUE(options);
  889. Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  890. Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
  891. ASSERT_EQ(1, std::distance(range.first, range.second));
  892. }
  893. /// @brief Test copy constructor or assignment operator when values
  894. /// being copied are NULL.
  895. ///
  896. /// @param copy Indicates that copy constructor should be tested
  897. /// (if true), or assignment operator (if false).
  898. void testCopyAssignmentNull(const bool copy) {
  899. ParserContext ctx(Option::V6);
  900. // Release all pointers in the context.
  901. ctx.boolean_values_.reset();
  902. ctx.uint32_values_.reset();
  903. ctx.string_values_.reset();
  904. ctx.options_.reset();
  905. ctx.option_defs_.reset();
  906. ctx.hooks_libraries_.reset();
  907. // Even if the fields of the context are NULL, it should get
  908. // copied.
  909. ParserContextPtr ctx_new(new ParserContext(Option::V6));
  910. if (copy) {
  911. ASSERT_NO_THROW(ctx_new.reset(new ParserContext(ctx)));
  912. } else {
  913. *ctx_new = ctx;
  914. }
  915. // The resulting context has its fields equal to NULL.
  916. EXPECT_FALSE(ctx_new->boolean_values_);
  917. EXPECT_FALSE(ctx_new->uint32_values_);
  918. EXPECT_FALSE(ctx_new->string_values_);
  919. EXPECT_FALSE(ctx_new->options_);
  920. EXPECT_FALSE(ctx_new->option_defs_);
  921. EXPECT_FALSE(ctx_new->hooks_libraries_);
  922. }
  923. /// @brief Test copy constructor or assignment operator.
  924. ///
  925. /// @param copy Indicates that copy constructor should be tested (if true),
  926. /// or assignment operator (if false).
  927. void testCopyAssignment(const bool copy) {
  928. // Create new context. It will be later copied/assigned to another
  929. // context.
  930. ParserContext ctx(Option::V6);
  931. // Set boolean parameter 'foo'.
  932. ASSERT_TRUE(ctx.boolean_values_);
  933. ctx.boolean_values_->setParam("foo", true);
  934. // Set uint32 parameter 'foo'.
  935. ASSERT_TRUE(ctx.uint32_values_);
  936. ctx.uint32_values_->setParam("foo", 123);
  937. // Ser string parameter 'foo'.
  938. ASSERT_TRUE(ctx.string_values_);
  939. ctx.string_values_->setParam("foo", "some string");
  940. // Add new option, with option code 10, to the context.
  941. ASSERT_TRUE(ctx.options_);
  942. OptionPtr opt1(new Option(Option::V6, 10));
  943. Subnet::OptionDescriptor desc1(opt1, false);
  944. std::string option_space = "option-space";
  945. ASSERT_TRUE(desc1.option);
  946. ctx.options_->addItem(desc1, option_space);
  947. // Add new option definition, with option code 123.
  948. OptionDefinitionPtr def1(new OptionDefinition("option-foo", 123,
  949. "string"));
  950. ctx.option_defs_->addItem(def1, option_space);
  951. // Allocate container for hooks libraries and add one library name.
  952. ctx.hooks_libraries_.reset(new std::vector<std::string>());
  953. ctx.hooks_libraries_->push_back("library1");
  954. // We will use ctx_new to assign another context to it or copy
  955. // construct.
  956. ParserContextPtr ctx_new(new ParserContext(Option::V4));;
  957. if (copy) {
  958. ctx_new.reset(new ParserContext(ctx));
  959. } else {
  960. *ctx_new = ctx;
  961. }
  962. // New context has the same boolean value.
  963. ASSERT_TRUE(ctx_new->boolean_values_);
  964. {
  965. SCOPED_TRACE("Check that boolean values are equal in both"
  966. " contexts");
  967. checkValueEq<BooleanStorage, bool>(ctx.boolean_values_,
  968. ctx_new->boolean_values_);
  969. }
  970. // New context has the same uint32 value.
  971. ASSERT_TRUE(ctx_new->uint32_values_);
  972. {
  973. SCOPED_TRACE("Check that uint32_t values are equal in both"
  974. " contexts");
  975. checkValueEq<Uint32Storage, uint32_t>(ctx.uint32_values_,
  976. ctx_new->uint32_values_);
  977. }
  978. // New context has the same string value.
  979. ASSERT_TRUE(ctx_new->string_values_);
  980. {
  981. SCOPED_TRACE("Check that string values are equal in both contexts");
  982. checkValueEq<StringStorage, std::string>(ctx.string_values_,
  983. ctx_new->string_values_);
  984. }
  985. // New context has the same option.
  986. ASSERT_TRUE(ctx_new->options_);
  987. {
  988. SCOPED_TRACE("Check that the option values are equal in both"
  989. " contexts");
  990. checkOptionType(*ctx_new, 10);
  991. }
  992. // New context has the same option definition.
  993. ASSERT_TRUE(ctx_new->option_defs_);
  994. {
  995. SCOPED_TRACE("check that the option definition is equal in both"
  996. " contexts");
  997. checkOptionDefinitionType(*ctx_new, 123);
  998. }
  999. // New context has the same hooks library.
  1000. ASSERT_TRUE(ctx_new->hooks_libraries_);
  1001. {
  1002. ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
  1003. EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
  1004. }
  1005. // New context has the same universe.
  1006. EXPECT_EQ(ctx.universe_, ctx_new->universe_);
  1007. // Change the value of the boolean parameter. This should not affect the
  1008. // corresponding value in the new context.
  1009. {
  1010. SCOPED_TRACE("Check that boolean value isn't changed when original"
  1011. " value is changed");
  1012. ctx.boolean_values_->setParam("foo", false);
  1013. checkValueNeq<BooleanStorage, bool>(ctx.boolean_values_,
  1014. ctx_new->boolean_values_);
  1015. }
  1016. // Change the value of the uint32_t parameter. This should not affect
  1017. // the corresponding value in the new context.
  1018. {
  1019. SCOPED_TRACE("Check that uint32_t value isn't changed when original"
  1020. " value is changed");
  1021. ctx.uint32_values_->setParam("foo", 987);
  1022. checkValueNeq<Uint32Storage, uint32_t>(ctx.uint32_values_,
  1023. ctx_new->uint32_values_);
  1024. }
  1025. // Change the value of the string parameter. This should not affect the
  1026. // corresponding value in the new context.
  1027. {
  1028. SCOPED_TRACE("Check that string value isn't changed when original"
  1029. " value is changed");
  1030. ctx.string_values_->setParam("foo", "different string");
  1031. checkValueNeq<StringStorage, std::string>(ctx.string_values_,
  1032. ctx_new->string_values_);
  1033. }
  1034. // Change the option. This should not affect the option instance in the
  1035. // new context.
  1036. {
  1037. SCOPED_TRACE("Check that option remains the same in the new context"
  1038. " when the option in the original context is changed");
  1039. ctx.options_->clearItems();
  1040. Subnet::OptionDescriptor desc(OptionPtr(new Option(Option::V6,
  1041. 100)),
  1042. false);
  1043. ASSERT_TRUE(desc.option);
  1044. ctx.options_->addItem(desc, "option-space");
  1045. checkOptionType(*ctx_new, 10);
  1046. }
  1047. // Change the option definition. This should not affect the option
  1048. // definition in the new context.
  1049. {
  1050. SCOPED_TRACE("Check that option definition remains the same in"
  1051. " the new context when the option definition in the"
  1052. " original context is changed");
  1053. ctx.option_defs_->clearItems();
  1054. OptionDefinitionPtr def(new OptionDefinition("option-foo", 234,
  1055. "string"));
  1056. ctx.option_defs_->addItem(def, option_space);
  1057. checkOptionDefinitionType(*ctx_new, 123);
  1058. }
  1059. // Change the list of libraries. this should not affect the list in the
  1060. // new context.
  1061. ctx.hooks_libraries_->clear();
  1062. ctx.hooks_libraries_->push_back("library2");
  1063. ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
  1064. EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
  1065. // Change the universe. This should not affect the universe value in the
  1066. // new context.
  1067. ctx.universe_ = Option::V4;
  1068. EXPECT_EQ(Option::V6, ctx_new->universe_);
  1069. }
  1070. };
  1071. // Check that the assignment operator of the ParserContext class copies all
  1072. // fields correctly.
  1073. TEST_F(ParserContextTest, assignment) {
  1074. testCopyAssignment(false);
  1075. }
  1076. // Check that the assignment operator of the ParserContext class copies all
  1077. // fields correctly when these fields are NULL.
  1078. TEST_F(ParserContextTest, assignmentNull) {
  1079. testCopyAssignmentNull(false);
  1080. }
  1081. // Check that the context is copy constructed correctly.
  1082. TEST_F(ParserContextTest, copyConstruct) {
  1083. testCopyAssignment(true);
  1084. }
  1085. // Check that the context is copy constructed correctly, when context fields
  1086. // are NULL.
  1087. TEST_F(ParserContextTest, copyConstructNull) {
  1088. testCopyAssignmentNull(true);
  1089. }