context_unittest.cc 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <eval/token.h>
  8. #include <eval/eval_context.h>
  9. #include <eval/token.h>
  10. #include <dhcp/option.h>
  11. #include <dhcp/pkt4.h>
  12. #include <asiolink/io_address.h>
  13. #include <boost/shared_ptr.hpp>
  14. #include <boost/scoped_ptr.hpp>
  15. #include <gtest/gtest.h>
  16. using namespace std;
  17. using namespace isc::asiolink;
  18. using namespace isc::dhcp;
  19. namespace {
  20. /// @brief Test class for testing EvalContext aka class test parsing
  21. class EvalContextTest : public ::testing::Test {
  22. public:
  23. /// @brief constructor to initialize members
  24. EvalContextTest() : ::testing::Test(),
  25. universe_(Option::V4), parsed_(false)
  26. { }
  27. /// @brief checks if the given token is a string with the expected value
  28. void checkTokenString(const TokenPtr& token, const std::string& expected) {
  29. ASSERT_TRUE(token);
  30. boost::shared_ptr<TokenString> str =
  31. boost::dynamic_pointer_cast<TokenString>(token);
  32. ASSERT_TRUE(str);
  33. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  34. ValueStack values;
  35. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  36. ASSERT_EQ(1, values.size());
  37. EXPECT_EQ(expected, values.top());
  38. }
  39. /// @brief checks if the given token is a hex string with the expected value
  40. void checkTokenHexString(const TokenPtr& token,
  41. const std::string& expected) {
  42. ASSERT_TRUE(token);
  43. boost::shared_ptr<TokenHexString> hex =
  44. boost::dynamic_pointer_cast<TokenHexString>(token);
  45. ASSERT_TRUE(hex);
  46. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  47. ValueStack values;
  48. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  49. ASSERT_EQ(1, values.size());
  50. EXPECT_EQ(expected, values.top());
  51. }
  52. /// @brief checks if the given token is an IP address with the expected value
  53. void checkTokenIpAddress(const TokenPtr& token,
  54. const std::string& expected) {
  55. ASSERT_TRUE(token);
  56. boost::shared_ptr<TokenIpAddress> ipaddr =
  57. boost::dynamic_pointer_cast<TokenIpAddress>(token);
  58. ASSERT_TRUE(ipaddr);
  59. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  60. ValueStack values;
  61. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  62. ASSERT_EQ(1, values.size());
  63. string value = values.top();
  64. boost::scoped_ptr<IOAddress> exp_ip;
  65. ASSERT_NO_THROW(exp_ip.reset(new IOAddress(expected)));
  66. vector<uint8_t> exp_addr = exp_ip->toBytes();
  67. ASSERT_EQ(exp_addr.size(), value.size());
  68. EXPECT_EQ(0, memcmp(&exp_addr[0], &value[0], value.size()));
  69. }
  70. /// @brief checks if the given token is an equal operator
  71. void checkTokenEq(const TokenPtr& token) {
  72. ASSERT_TRUE(token);
  73. boost::shared_ptr<TokenEqual> eq =
  74. boost::dynamic_pointer_cast<TokenEqual>(token);
  75. EXPECT_TRUE(eq);
  76. }
  77. /// @brief checks if the given token is an option with the expected code
  78. /// and representation type
  79. /// @param token token to be checked
  80. /// @param expected_code expected option code
  81. /// @param expected_repr expected representation (text, hex, exists)
  82. void checkTokenOption(const TokenPtr& token,
  83. uint16_t expected_code,
  84. TokenOption::RepresentationType expected_repr) {
  85. ASSERT_TRUE(token);
  86. boost::shared_ptr<TokenOption> opt =
  87. boost::dynamic_pointer_cast<TokenOption>(token);
  88. ASSERT_TRUE(opt);
  89. EXPECT_EQ(expected_code, opt->getCode());
  90. EXPECT_EQ(expected_repr, opt->getRepresentation());
  91. }
  92. /// @brief check if the given token is relay4 with the expected code
  93. /// and representation type
  94. /// @param token token to be checked
  95. /// @param expected_code expected option code
  96. /// @param expected_repr expected representation (text, hex, exists)
  97. void checkTokenRelay4(const TokenPtr& token,
  98. uint16_t expected_code,
  99. TokenOption::RepresentationType expected_repr) {
  100. ASSERT_TRUE(token);
  101. boost::shared_ptr<TokenRelay4Option> relay4 =
  102. boost::dynamic_pointer_cast<TokenRelay4Option>(token);
  103. EXPECT_TRUE(relay4);
  104. if (relay4) {
  105. EXPECT_EQ(expected_code, relay4->getCode());
  106. EXPECT_EQ(expected_repr, relay4->getRepresentation());
  107. }
  108. }
  109. /// @brief checks if the given token is a TokenRelay6Option with
  110. /// the correct nesting level, option code and representation.
  111. /// @param token token to be checked
  112. /// @param expected_level expected nesting level
  113. /// @param expected_code expected option code
  114. /// @param expected_repr expected representation (text, hex, exists)
  115. void checkTokenRelay6Option(const TokenPtr& token,
  116. uint8_t expected_level,
  117. uint16_t expected_code,
  118. TokenOption::RepresentationType expected_repr) {
  119. ASSERT_TRUE(token);
  120. boost::shared_ptr<TokenRelay6Option> opt =
  121. boost::dynamic_pointer_cast<TokenRelay6Option>(token);
  122. ASSERT_TRUE(opt);
  123. EXPECT_EQ(expected_level, opt->getNest());
  124. EXPECT_EQ(expected_code, opt->getCode());
  125. EXPECT_EQ(expected_repr, opt->getRepresentation());
  126. }
  127. /// @brief This tests attempts to parse the expression then checks
  128. /// if the number of tokens is correct and the TokenRelay6Option
  129. /// is as expected.
  130. ///
  131. /// @param expr expression to be parsed
  132. /// @param exp_level expected level to be parsed
  133. /// @param exp_code expected option code to be parsed
  134. /// @param exp_repr expected representation to be parsed
  135. /// @param exp_tokens expected number of tokens
  136. void testRelay6Option(std::string expr,
  137. uint8_t exp_level,
  138. uint16_t exp_code,
  139. TokenOption::RepresentationType exp_repr,
  140. int exp_tokens) {
  141. EvalContext eval(Option::V6);
  142. // parse the expression
  143. try {
  144. parsed_ = eval.parseString(expr);
  145. }
  146. catch (const EvalParseError& ex) {
  147. FAIL() <<"Exception thrown: " << ex.what();
  148. return;
  149. }
  150. // Parsing should succed and return a token.
  151. EXPECT_TRUE(parsed_);
  152. // There should be the expected number of tokens.
  153. ASSERT_EQ(exp_tokens, eval.expression.size());
  154. // checkt that the first token is TokenRelay6Option and that
  155. // is has the correct attributes
  156. checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr);
  157. }
  158. /// @brief check if the given token is a Pkt of specified type
  159. /// @param token token to be checked
  160. /// @param type expected type of the Pkt metadata
  161. void checkTokenPkt(const TokenPtr& token, TokenPkt::MetadataType type) {
  162. ASSERT_TRUE(token);
  163. boost::shared_ptr<TokenPkt> pkt =
  164. boost::dynamic_pointer_cast<TokenPkt>(token);
  165. ASSERT_TRUE(pkt);
  166. EXPECT_EQ(type, pkt->getType());
  167. }
  168. /// @brief Test that verifies access to the DHCP packet metadatas.
  169. ///
  170. /// This test attempts to parse the expression, will check if the number
  171. /// of tokens is exactly as expected and then will try to verify if the
  172. /// first token represents the expected metadata in DHCP packet.
  173. ///
  174. /// @param expr expression to be parsed
  175. /// @param exp_type expected metadata type to be parsed
  176. /// @param exp_tokens expected number of tokens
  177. void testPktMetadata(std::string expr,
  178. TokenPkt::MetadataType exp_type,
  179. int exp_tokens) {
  180. EvalContext eval(Option::V6);
  181. // Parse the expression.
  182. try {
  183. parsed_ = eval.parseString(expr);
  184. }
  185. catch (const EvalParseError& ex) {
  186. FAIL() << "Exception thrown: " << ex.what();
  187. return;
  188. }
  189. // Parsing should succeed and return a token.
  190. EXPECT_TRUE(parsed_);
  191. // There should be exactly the expected number of tokens.
  192. ASSERT_EQ(exp_tokens, eval.expression.size());
  193. // Check that the first token is TokenPkt instance and has correct type.
  194. checkTokenPkt(eval.expression.at(0), exp_type);
  195. }
  196. /// @brief checks if the given token is Pkt4 of specified type
  197. /// @param token token to be checked
  198. /// @param type expected type of the Pkt4 field
  199. void checkTokenPkt4(const TokenPtr& token, TokenPkt4::FieldType type) {
  200. ASSERT_TRUE(token);
  201. boost::shared_ptr<TokenPkt4> pkt =
  202. boost::dynamic_pointer_cast<TokenPkt4>(token);
  203. ASSERT_TRUE(pkt);
  204. EXPECT_EQ(type, pkt->getType());
  205. }
  206. /// @brief Test that verifies access to the DHCPv4 packet fields.
  207. ///
  208. /// This test attempts to parse the expression, will check if the number
  209. /// of tokens is exactly as expected and then will try to verify if the
  210. /// first token represents the expected field in DHCPv4 packet.
  211. ///
  212. /// @param expr expression to be parsed
  213. /// @param exp_type expected field type to be parsed
  214. /// @param exp_tokens expected number of tokens
  215. void testPkt4Field(std::string expr,
  216. TokenPkt4::FieldType exp_type,
  217. int exp_tokens) {
  218. EvalContext eval(Option::V4);
  219. // Parse the expression.
  220. try {
  221. parsed_ = eval.parseString(expr);
  222. }
  223. catch (const EvalParseError& ex) {
  224. FAIL() << "Exception thrown: " << ex.what();
  225. return;
  226. }
  227. // Parsing should succeed and return a token.
  228. EXPECT_TRUE(parsed_);
  229. // There should be exactly the expected number of tokens.
  230. ASSERT_EQ(exp_tokens, eval.expression.size());
  231. // Check that the first token is TokenPkt4 instance and has correct type.
  232. checkTokenPkt4(eval.expression.at(0), exp_type);
  233. }
  234. /// @brief checks if the given token is Pkt6 of specified type
  235. /// @param token token to be checked
  236. /// @param exp_type expected type of the Pkt6 field
  237. void checkTokenPkt6(const TokenPtr& token,
  238. TokenPkt6::FieldType exp_type) {
  239. ASSERT_TRUE(token);
  240. boost::shared_ptr<TokenPkt6> pkt =
  241. boost::dynamic_pointer_cast<TokenPkt6>(token);
  242. ASSERT_TRUE(pkt);
  243. EXPECT_EQ(exp_type, pkt->getType());
  244. }
  245. /// @brief Test that verifies access to the DHCPv6 packet fields.
  246. ///
  247. /// This test attempts to parse the expression, will check if the number
  248. /// of tokens is exactly as planned and then will try to verify if the
  249. /// first token represents expected the field in DHCPv6 packet.
  250. ///
  251. /// @param expr expression to be parsed
  252. /// @param exp_type expected field type to be parsed
  253. /// @param exp_tokens expected number of tokens
  254. void testPkt6Field(std::string expr, TokenPkt6::FieldType exp_type,
  255. int exp_tokens) {
  256. EvalContext eval(Option::V6);
  257. // Parse the expression.
  258. try {
  259. parsed_ = eval.parseString(expr);
  260. }
  261. catch (const EvalParseError& ex) {
  262. FAIL() << "Exception thrown: " << ex.what();
  263. return;
  264. }
  265. // Parsing should succeed and return a token.
  266. EXPECT_TRUE(parsed_);
  267. // There should be the requested number of tokens
  268. ASSERT_EQ(exp_tokens, eval.expression.size());
  269. // Check that the first token is TokenPkt6 instance and has correct type.
  270. checkTokenPkt6(eval.expression.at(0), exp_type);
  271. }
  272. /// @brief checks if the given token is a TokenRelay with the
  273. /// correct nesting level and field type.
  274. /// @param token token to be checked
  275. /// @param expected_level expected nesting level
  276. /// @param expected_code expected option code
  277. /// @param expected_repr expected representation (text, hex, exists)
  278. void checkTokenRelay6Field(const TokenPtr& token,
  279. uint8_t expected_level,
  280. TokenRelay6Field::FieldType expected_type) {
  281. ASSERT_TRUE(token);
  282. boost::shared_ptr<TokenRelay6Field> opt =
  283. boost::dynamic_pointer_cast<TokenRelay6Field>(token);
  284. ASSERT_TRUE(opt);
  285. EXPECT_EQ(expected_level, opt->getNest());
  286. EXPECT_EQ(expected_type, opt->getType());
  287. }
  288. /// @brief This tests attempts to parse the expression then checks
  289. /// if the number of tokens is correct and the TokenRelay6Field is as
  290. /// expected.
  291. ///
  292. /// @param expr expression to be parsed
  293. /// @param exp_level expected level to be parsed
  294. /// @param exp_type expected field type to be parsed
  295. /// @param exp_tokens expected number of tokens
  296. void testRelay6Field(std::string expr,
  297. uint8_t exp_level,
  298. TokenRelay6Field::FieldType exp_type,
  299. int exp_tokens) {
  300. EvalContext eval(Option::V6);
  301. // parse the expression
  302. try {
  303. parsed_ = eval.parseString(expr);
  304. }
  305. catch (const EvalParseError& ex) {
  306. FAIL() <<"Exception thrown: " << ex.what();
  307. return;
  308. }
  309. // Parsing should succed and return a token.
  310. EXPECT_TRUE(parsed_);
  311. // There should be the expected number of tokens.
  312. ASSERT_EQ(exp_tokens, eval.expression.size());
  313. // checkt that the first token is TokenRelay6Field and that
  314. // is has the correct attributes
  315. checkTokenRelay6Field(eval.expression.at(0), exp_level, exp_type);
  316. }
  317. /// @brief checks if the given token is a substring operator
  318. void checkTokenSubstring(const TokenPtr& token) {
  319. ASSERT_TRUE(token);
  320. boost::shared_ptr<TokenSubstring> sub =
  321. boost::dynamic_pointer_cast<TokenSubstring>(token);
  322. EXPECT_TRUE(sub);
  323. }
  324. /// @brief checks if the given token is a concat operator
  325. void checkTokenConcat(const TokenPtr& token) {
  326. ASSERT_TRUE(token);
  327. boost::shared_ptr<TokenConcat> conc =
  328. boost::dynamic_pointer_cast<TokenConcat>(token);
  329. EXPECT_TRUE(conc);
  330. }
  331. /// @brief checks if the given expression raises the expected message
  332. /// when it is parsed.
  333. void checkError(const string& expr, const string& msg) {
  334. EvalContext eval(universe_);
  335. parsed_ = false;
  336. try {
  337. parsed_ = eval.parseString(expr);
  338. FAIL() << "Expected EvalParseError but nothing was raised";
  339. }
  340. catch (const EvalParseError& ex) {
  341. EXPECT_EQ(msg, ex.what());
  342. EXPECT_FALSE(parsed_);
  343. }
  344. catch (...) {
  345. FAIL() << "Expected EvalParseError but something else was raised";
  346. }
  347. }
  348. /// @brief sets the universe
  349. /// @note the default universe is DHCPv4
  350. void setUniverse(const Option::Universe& universe) {
  351. universe_ = universe;
  352. }
  353. Option::Universe universe_;
  354. bool parsed_; ///< Parsing status
  355. };
  356. // Test the error method without location
  357. TEST_F(EvalContextTest, error) {
  358. EvalContext eval(Option::V4);
  359. EXPECT_THROW(eval.error("an error"), EvalParseError);
  360. }
  361. // Test the fatal method
  362. TEST_F(EvalContextTest, fatal) {
  363. EvalContext eval(Option::V4);
  364. EXPECT_THROW(eval.fatal("a fatal error"), isc::Unexpected);
  365. }
  366. // Test the convertOptionCode method with an illegal input
  367. TEST_F(EvalContextTest, badOptionCode) {
  368. EvalContext eval(Option::V4);
  369. // the option code must be a number
  370. EXPECT_THROW(eval.convertOptionCode("bad", location(position())),
  371. EvalParseError);
  372. }
  373. // Test the convertNestLevelNumber method with an illegal input
  374. TEST_F(EvalContextTest, badNestLevelNumber) {
  375. EvalContext eval(Option::V4);
  376. // the nest level number must be a number
  377. EXPECT_THROW(eval.convertNestLevelNumber("bad", location(position())),
  378. EvalParseError);
  379. }
  380. // Test the parsing of a basic expression
  381. TEST_F(EvalContextTest, basic) {
  382. EvalContext eval(Option::V4);
  383. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'"));
  384. EXPECT_TRUE(parsed_);
  385. }
  386. // Test the parsing of a string terminal
  387. TEST_F(EvalContextTest, string) {
  388. EvalContext eval(Option::V4);
  389. EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
  390. EXPECT_TRUE(parsed_);
  391. ASSERT_EQ(3, eval.expression.size());
  392. TokenPtr tmp1 = eval.expression.at(0);
  393. TokenPtr tmp2 = eval.expression.at(1);
  394. checkTokenString(tmp1, "foo");
  395. checkTokenString(tmp2, "bar");
  396. }
  397. // Test the parsing of a basic expression using integers
  398. TEST_F(EvalContextTest, integer) {
  399. EvalContext eval(Option::V4);
  400. EXPECT_NO_THROW(parsed_ =
  401. eval.parseString("substring(option[123].text, 0, 2) == '42'"));
  402. EXPECT_TRUE(parsed_);
  403. }
  404. // Test the parsing of a hexstring terminal
  405. TEST_F(EvalContextTest, hexstring) {
  406. EvalContext eval(Option::V4);
  407. EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
  408. EXPECT_TRUE(parsed_);
  409. ASSERT_EQ(3, eval.expression.size());
  410. TokenPtr tmp = eval.expression.at(0);
  411. checkTokenHexString(tmp, "foo");
  412. }
  413. // Test the parsing of a hexstring terminal with an odd number of
  414. // hexadecimal digits
  415. TEST_F(EvalContextTest, oddHexstring) {
  416. EvalContext eval(Option::V4);
  417. EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
  418. EXPECT_TRUE(parsed_);
  419. ASSERT_EQ(3, eval.expression.size());
  420. TokenPtr tmp = eval.expression.at(0);
  421. checkTokenHexString(tmp, "\a");
  422. }
  423. // Test the parsing of an IPv4 address
  424. TEST_F(EvalContextTest, ipaddress4) {
  425. EvalContext eval(Option::V6);
  426. EXPECT_NO_THROW(parsed_ = eval.parseString("10.0.0.1 == 'foo'"));
  427. EXPECT_TRUE(parsed_);
  428. ASSERT_EQ(3, eval.expression.size());
  429. TokenPtr tmp = eval.expression.at(0);
  430. checkTokenIpAddress(tmp, "10.0.0.1");
  431. }
  432. // Test the parsing of an IPv6 address
  433. TEST_F(EvalContextTest, ipaddress6) {
  434. EvalContext eval(Option::V6);
  435. EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8::1 == 'foo'"));
  436. EXPECT_TRUE(parsed_);
  437. ASSERT_EQ(3, eval.expression.size());
  438. TokenPtr tmp = eval.expression.at(0);
  439. checkTokenIpAddress(tmp, "2001:db8::1");
  440. }
  441. // Test the parsing of an IPv4 compatible IPv6 address
  442. TEST_F(EvalContextTest, ipaddress46) {
  443. EvalContext eval(Option::V6);
  444. EXPECT_NO_THROW(parsed_ = eval.parseString("::10.0.0.1 == 'foo'"));
  445. EXPECT_TRUE(parsed_);
  446. ASSERT_EQ(3, eval.expression.size());
  447. TokenPtr tmp = eval.expression.at(0);
  448. checkTokenIpAddress(tmp, "::10.0.0.1");
  449. }
  450. // Test the parsing of the unspecified IPv6 address
  451. TEST_F(EvalContextTest, ipaddress6unspec) {
  452. EvalContext eval(Option::V6);
  453. EXPECT_NO_THROW(parsed_ = eval.parseString(":: == 'foo'"));
  454. EXPECT_TRUE(parsed_);
  455. ASSERT_EQ(3, eval.expression.size());
  456. TokenPtr tmp = eval.expression.at(0);
  457. checkTokenIpAddress(tmp, "::");
  458. }
  459. // Test the parsing of an IPv6 prefix
  460. TEST_F(EvalContextTest, ipaddress6prefix) {
  461. EvalContext eval(Option::V6);
  462. EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8:: == 'foo'"));
  463. EXPECT_TRUE(parsed_);
  464. ASSERT_EQ(3, eval.expression.size());
  465. TokenPtr tmp = eval.expression.at(0);
  466. checkTokenIpAddress(tmp, "2001:db8::");
  467. }
  468. // Test the parsing of an equal expression
  469. TEST_F(EvalContextTest, equal) {
  470. EvalContext eval(Option::V4);
  471. EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
  472. EXPECT_TRUE(parsed_);
  473. ASSERT_EQ(3, eval.expression.size());
  474. TokenPtr tmp1 = eval.expression.at(0);
  475. TokenPtr tmp2 = eval.expression.at(1);
  476. TokenPtr tmp3 = eval.expression.at(2);
  477. checkTokenString(tmp1, "foo");
  478. checkTokenString(tmp2, "bar");
  479. checkTokenEq(tmp3);
  480. }
  481. // Test the parsing of an option terminal
  482. TEST_F(EvalContextTest, option) {
  483. EvalContext eval(Option::V4);
  484. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
  485. EXPECT_TRUE(parsed_);
  486. ASSERT_EQ(3, eval.expression.size());
  487. checkTokenOption(eval.expression.at(0), 123, TokenOption::TEXTUAL);
  488. }
  489. // Test parsing of an option identified by name.
  490. TEST_F(EvalContextTest, optionWithName) {
  491. EvalContext eval(Option::V4);
  492. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  493. EXPECT_NO_THROW(parsed_ = eval.parseString("option[host-name].text == 'foo'"));
  494. EXPECT_TRUE(parsed_);
  495. ASSERT_EQ(3, eval.expression.size());
  496. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  497. }
  498. // Test parsing of an option existence
  499. TEST_F(EvalContextTest, optionExists) {
  500. EvalContext eval(Option::V4);
  501. EXPECT_NO_THROW(parsed_ = eval.parseString("option[100].exists"));
  502. EXPECT_TRUE(parsed_);
  503. ASSERT_EQ(1, eval.expression.size());
  504. checkTokenOption(eval.expression.at(0), 100, TokenOption::EXISTS);
  505. }
  506. // Test checking that whitespace can surround option name.
  507. TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
  508. EvalContext eval(Option::V4);
  509. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  510. EXPECT_NO_THROW(parsed_ = eval.parseString("option[ host-name ].text == 'foo'"));
  511. EXPECT_TRUE(parsed_);
  512. ASSERT_EQ(3, eval.expression.size());
  513. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  514. }
  515. // Test checking that newlines can surround option name.
  516. TEST_F(EvalContextTest, optionWithNameAndNewline) {
  517. EvalContext eval(Option::V4);
  518. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  519. EXPECT_NO_THROW(parsed_ =
  520. eval.parseString("option[\n host-name \n ].text == \n'foo'"));
  521. EXPECT_TRUE(parsed_);
  522. ASSERT_EQ(3, eval.expression.size());
  523. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  524. }
  525. // Test parsing of an option represented as hexadecimal string.
  526. TEST_F(EvalContextTest, optionHex) {
  527. EvalContext eval(Option::V4);
  528. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
  529. EXPECT_TRUE(parsed_);
  530. ASSERT_EQ(3, eval.expression.size());
  531. checkTokenOption(eval.expression.at(0), 123, TokenOption::HEXADECIMAL);
  532. }
  533. // This test checks that the relay4[code].hex can be used in expressions.
  534. TEST_F(EvalContextTest, relay4Option) {
  535. EvalContext eval(Option::V4);
  536. EXPECT_NO_THROW(parsed_ =
  537. eval.parseString("relay4[13].hex == 'thirteen'"));
  538. EXPECT_TRUE(parsed_);
  539. ASSERT_EQ(3, eval.expression.size());
  540. TokenPtr tmp1 = eval.expression.at(0);
  541. TokenPtr tmp2 = eval.expression.at(1);
  542. TokenPtr tmp3 = eval.expression.at(2);
  543. checkTokenRelay4(tmp1, 13, TokenOption::HEXADECIMAL);
  544. checkTokenString(tmp2, "thirteen");
  545. checkTokenEq(tmp3);
  546. }
  547. // This test check the relay4[code].exists is supported.
  548. TEST_F(EvalContextTest, relay4Exists) {
  549. EvalContext eval(Option::V4);
  550. EXPECT_NO_THROW(parsed_ = eval.parseString("relay4[13].exists"));
  551. EXPECT_TRUE(parsed_);
  552. ASSERT_EQ(1, eval.expression.size());
  553. checkTokenRelay4(eval.expression.at(0), 13, TokenOption::EXISTS);
  554. }
  555. // Verify that relay4[13] is not usable in v6
  556. // There will be a separate relay accessor for v6.
  557. TEST_F(EvalContextTest, relay4Error) {
  558. universe_ = Option::V6;
  559. checkError("relay4[13].hex == 'thirteen'",
  560. "<string>:1.1-6: relay4 can only be used in DHCPv4.");
  561. }
  562. // Test the parsing of a relay6 option
  563. TEST_F(EvalContextTest, relay6Option) {
  564. EvalContext eval(Option::V6);
  565. testRelay6Option("relay6[0].option[123].text == 'foo'",
  566. 0, 123, TokenOption::TEXTUAL, 3);
  567. }
  568. // Test the parsing of existence for a relay6 option
  569. TEST_F(EvalContextTest, relay6OptionExists) {
  570. EvalContext eval(Option::V6);
  571. testRelay6Option("relay6[1].option[75].exists",
  572. 1, 75, TokenOption::EXISTS, 1);
  573. }
  574. // Test the parsing of hex for a relay6 option
  575. TEST_F(EvalContextTest, relay6OptionHex) {
  576. EvalContext eval(Option::V6);
  577. testRelay6Option("relay6[2].option[85].hex == 'foo'",
  578. 2, 85, TokenOption::HEXADECIMAL, 3);
  579. }
  580. // Test the nest level of a relay6 option should be in [0..32[
  581. TEST_F(EvalContextTest, relay6OptionLimits) {
  582. EvalContext eval(Option::V6);
  583. // max nest level is hop count limit minus one so 31
  584. testRelay6Option("relay6[31].option[123].text == 'foo'",
  585. 31, 123, TokenOption::TEXTUAL, 3);
  586. universe_ = Option::V6;
  587. checkError("relay6[32].option[123].text == 'foo'",
  588. "<string>:1.8-9: Nest level has invalid value in 32. "
  589. "Allowed range: 0..31");
  590. // next level must be a positive number
  591. checkError("relay6[-1].option[123].text == 'foo'",
  592. "<string>:1.8-9: Nest level has invalid value in -1. "
  593. "Allowed range: 0..31");
  594. }
  595. // Verify that relay6[13].option is not usable in v4
  596. TEST_F(EvalContextTest, relay6OptionError) {
  597. universe_ = Option::V4;
  598. // nest_level is reduced first so raises the error
  599. // (if we'd like to get a relay6 error we have to insert an
  600. // intermediate action to check the universe)
  601. checkError("relay6[0].option[123].text == 'foo'",
  602. "<string>:1.8: Nest level invalid for DHCPv4 packets");
  603. }
  604. // Tests whether iface metadata in DHCP can be accessed.
  605. TEST_F(EvalContextTest, pktMetadataIface) {
  606. testPktMetadata("pkt.iface == 'eth0'", TokenPkt::IFACE, 3);
  607. }
  608. // Tests whether src metadata in DHCP can be accessed.
  609. TEST_F(EvalContextTest, pktMetadataSrc) {
  610. testPktMetadata("pkt.src == fe80::1", TokenPkt::SRC, 3);
  611. }
  612. // Tests whether dst metadata in DHCP can be accessed.
  613. TEST_F(EvalContextTest, pktMetadataDst) {
  614. testPktMetadata("pkt.dst == fe80::2", TokenPkt::DST, 3);
  615. }
  616. // Tests whether len metadata in DHCP can be accessed.
  617. TEST_F(EvalContextTest, pktMetadataLen) {
  618. testPktMetadata("pkt.len == 0x00000100", TokenPkt::LEN, 3);
  619. }
  620. // Tests whether chaddr field in DHCPv4 can be accessed.
  621. TEST_F(EvalContextTest, pkt4FieldChaddr) {
  622. testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3);
  623. }
  624. // Tests whether hlen field in DHCPv4 can be accessed.
  625. TEST_F(EvalContextTest, pkt4FieldHlen) {
  626. testPkt4Field("pkt4.hlen == 0x6", TokenPkt4::HLEN, 3);
  627. }
  628. // Tests whether htype field in DHCPv4 can be accessed.
  629. TEST_F(EvalContextTest, pkt4FieldHtype) {
  630. testPkt4Field("pkt4.htype == 0x1", TokenPkt4::HTYPE, 3);
  631. }
  632. // Tests whether ciaddr field in DHCPv4 can be accessed.
  633. TEST_F(EvalContextTest, pkt4FieldCiaddr) {
  634. testPkt4Field("pkt4.ciaddr == 192.0.2.1", TokenPkt4::CIADDR, 3);
  635. }
  636. // Tests whether giaddr field in DHCPv4 can be accessed.
  637. TEST_F(EvalContextTest, pkt4FieldGiaddr) {
  638. testPkt4Field("pkt4.giaddr == 192.0.2.1", TokenPkt4::GIADDR, 3);
  639. }
  640. // Tests whether yiaddr field in DHCPv4 can be accessed.
  641. TEST_F(EvalContextTest, pkt4FieldYiaddr) {
  642. testPkt4Field("pkt4.yiaddr == 192.0.2.1", TokenPkt4::YIADDR, 3);
  643. }
  644. // Tests whether siaddr field in DHCPv4 can be accessed.
  645. TEST_F(EvalContextTest, pkt4FieldSiaddr) {
  646. testPkt4Field("pkt4.siaddr == 192.0.2.1", TokenPkt4::SIADDR, 3);
  647. }
  648. // Tests whether message type field in DHCPv6 can be accessed.
  649. TEST_F(EvalContextTest, pkt6FieldMsgtype) {
  650. testPkt6Field("pkt6.msgtype == '1'", TokenPkt6::MSGTYPE, 3);
  651. }
  652. // Tests whether transaction id field in DHCPv6 can be accessed.
  653. TEST_F(EvalContextTest, pkt6FieldTransid) {
  654. testPkt6Field("pkt6.transid == '1'", TokenPkt6::TRANSID, 3);
  655. }
  656. // Tests if the linkaddr field in a Relay6 encapsulation can be accessed.
  657. TEST_F(EvalContextTest, relay6FieldLinkAddr) {
  658. testRelay6Field("relay6[0].linkaddr == ::",
  659. 0, TokenRelay6Field::LINKADDR, 3);
  660. }
  661. // Tests if the peeraddr field in a Relay6 encapsulation can be accessed.
  662. TEST_F(EvalContextTest, relay6FieldPeerAddr) {
  663. testRelay6Field("relay6[1].peeraddr == ::",
  664. 1, TokenRelay6Field::PEERADDR, 3);
  665. }
  666. // Verify that relay6[13].<field> is not usable in v4
  667. TEST_F(EvalContextTest, relay6FieldError) {
  668. universe_ = Option::V4;
  669. // nest_level is reduced first so raises the error
  670. // (if we'd like to get a relay6 error we have to insert an
  671. // intermediate action to check the universe)
  672. checkError("relay6[0].linkaddr == ::",
  673. "<string>:1.8: Nest level invalid for DHCPv4 packets");
  674. }
  675. // Test parsing of logical operators
  676. TEST_F(EvalContextTest, logicalOps) {
  677. // option.exists
  678. EvalContext eval0(Option::V4);
  679. EXPECT_NO_THROW(parsed_ = eval0.parseString("option[123].exists"));
  680. EXPECT_TRUE(parsed_);
  681. ASSERT_EQ(1, eval0.expression.size());
  682. TokenPtr token = eval0.expression.at(0);
  683. ASSERT_TRUE(token);
  684. boost::shared_ptr<TokenOption> opt =
  685. boost::dynamic_pointer_cast<TokenOption>(token);
  686. EXPECT_TRUE(opt);
  687. // not
  688. EvalContext evaln(Option::V4);
  689. EXPECT_NO_THROW(parsed_ = evaln.parseString("not option[123].exists"));
  690. EXPECT_TRUE(parsed_);
  691. ASSERT_EQ(2, evaln.expression.size());
  692. token = evaln.expression.at(1);
  693. ASSERT_TRUE(token);
  694. boost::shared_ptr<TokenNot> tnot =
  695. boost::dynamic_pointer_cast<TokenNot>(token);
  696. EXPECT_TRUE(tnot);
  697. // and
  698. EvalContext evala(Option::V4);
  699. EXPECT_NO_THROW(parsed_ =
  700. evala.parseString("option[123].exists and option[123].exists"));
  701. EXPECT_TRUE(parsed_);
  702. ASSERT_EQ(3, evala.expression.size());
  703. token = evala.expression.at(2);
  704. ASSERT_TRUE(token);
  705. boost::shared_ptr<TokenAnd> tand =
  706. boost::dynamic_pointer_cast<TokenAnd>(token);
  707. EXPECT_TRUE(tand);
  708. // or
  709. EvalContext evalo(Option::V4);
  710. EXPECT_NO_THROW(parsed_ =
  711. evalo.parseString("option[123].exists or option[123].exists"));
  712. EXPECT_TRUE(parsed_);
  713. ASSERT_EQ(3, evalo.expression.size());
  714. token = evalo.expression.at(2);
  715. ASSERT_TRUE(token);
  716. boost::shared_ptr<TokenOr> tor =
  717. boost::dynamic_pointer_cast<TokenOr>(token);
  718. EXPECT_TRUE(tor);
  719. }
  720. // Test parsing of logical operators with precedence
  721. TEST_F(EvalContextTest, logicalPrecedence) {
  722. // not precedence > and precedence
  723. EvalContext evalna(Option::V4);
  724. EXPECT_NO_THROW(parsed_ =
  725. evalna.parseString("not option[123].exists and option[123].exists"));
  726. EXPECT_TRUE(parsed_);
  727. ASSERT_EQ(4, evalna.expression.size());
  728. TokenPtr token = evalna.expression.at(3);
  729. ASSERT_TRUE(token);
  730. boost::shared_ptr<TokenAnd> tand =
  731. boost::dynamic_pointer_cast<TokenAnd>(token);
  732. EXPECT_TRUE(tand);
  733. // and precedence > or precedence
  734. EvalContext evaloa(Option::V4);
  735. EXPECT_NO_THROW(parsed_ =
  736. evaloa.parseString("option[123].exists or option[123].exists "
  737. "and option[123].exists"));
  738. EXPECT_TRUE(parsed_);
  739. ASSERT_EQ(5, evaloa.expression.size());
  740. token = evaloa.expression.at(4);
  741. ASSERT_TRUE(token);
  742. boost::shared_ptr<TokenOr> tor =
  743. boost::dynamic_pointer_cast<TokenOr>(token);
  744. EXPECT_TRUE(tor);
  745. }
  746. // Test parsing of logical operators with parentheses (same than
  747. // with precedence but using parentheses to overwrite precedence)
  748. TEST_F(EvalContextTest, logicalParentheses) {
  749. // not precedence > and precedence
  750. EvalContext evalna(Option::V4);
  751. EXPECT_NO_THROW(parsed_ =
  752. evalna.parseString("not (option[123].exists and option[123].exists)"));
  753. EXPECT_TRUE(parsed_);
  754. ASSERT_EQ(4, evalna.expression.size());
  755. TokenPtr token = evalna.expression.at(3);
  756. ASSERT_TRUE(token);
  757. boost::shared_ptr<TokenNot> tnot =
  758. boost::dynamic_pointer_cast<TokenNot>(token);
  759. EXPECT_TRUE(tnot);
  760. // and precedence > or precedence
  761. EvalContext evaloa(Option::V4);
  762. EXPECT_NO_THROW(parsed_ =
  763. evaloa.parseString("(option[123].exists or option[123].exists) "
  764. "and option[123].exists"));
  765. EXPECT_TRUE(parsed_);
  766. ASSERT_EQ(5, evaloa.expression.size());
  767. token = evaloa.expression.at(4);
  768. ASSERT_TRUE(token);
  769. boost::shared_ptr<TokenAnd> tand =
  770. boost::dynamic_pointer_cast<TokenAnd>(token);
  771. EXPECT_TRUE(tand);
  772. }
  773. // Test the parsing of a substring expression
  774. TEST_F(EvalContextTest, substring) {
  775. EvalContext eval(Option::V4);
  776. EXPECT_NO_THROW(parsed_ =
  777. eval.parseString("substring('foobar',2,all) == 'obar'"));
  778. EXPECT_TRUE(parsed_);
  779. ASSERT_EQ(6, eval.expression.size());
  780. TokenPtr tmp1 = eval.expression.at(0);
  781. TokenPtr tmp2 = eval.expression.at(1);
  782. TokenPtr tmp3 = eval.expression.at(2);
  783. TokenPtr tmp4 = eval.expression.at(3);
  784. checkTokenString(tmp1, "foobar");
  785. checkTokenString(tmp2, "2");
  786. checkTokenString(tmp3, "all");
  787. checkTokenSubstring(tmp4);
  788. }
  789. // Test the parsing of a concat expression
  790. TEST_F(EvalContextTest, concat) {
  791. EvalContext eval(Option::V4);
  792. EXPECT_NO_THROW(parsed_ =
  793. eval.parseString("concat('foo','bar') == 'foobar'"));
  794. EXPECT_TRUE(parsed_);
  795. ASSERT_EQ(5, eval.expression.size());
  796. TokenPtr tmp1 = eval.expression.at(0);
  797. TokenPtr tmp2 = eval.expression.at(1);
  798. TokenPtr tmp3 = eval.expression.at(2);
  799. checkTokenString(tmp1, "foo");
  800. checkTokenString(tmp2, "bar");
  801. checkTokenConcat(tmp3);
  802. }
  803. //
  804. // Test some scanner error cases
  805. TEST_F(EvalContextTest, scanErrors) {
  806. checkError("'", "<string>:1.1: Invalid character: '");
  807. checkError("'\''", "<string>:1.3: Invalid character: '");
  808. checkError("'\n'", "<string>:1.1: Invalid character: '");
  809. checkError("0x123h", "<string>:1.6: Invalid character: h");
  810. checkError(":1", "<string>:1.1: Invalid character: :");
  811. checkError("=", "<string>:1.1: Invalid character: =");
  812. checkError("subtring", "<string>:1.1: Invalid character: s");
  813. checkError("foo", "<string>:1.1: Invalid character: f");
  814. checkError(" bar", "<string>:1.2: Invalid character: b");
  815. checkError("relay[12].hex == 'foo'", "<string>:1.1: Invalid character: r");
  816. checkError("pkt4.ziaddr", "<string>:1.6: Invalid character: z");
  817. }
  818. // Tests some scanner/parser error cases
  819. TEST_F(EvalContextTest, scanParseErrors) {
  820. checkError("", "<string>:1.1: syntax error, unexpected end of file");
  821. checkError(" ", "<string>:1.2: syntax error, unexpected end of file");
  822. checkError("0x", "<string>:1.1: syntax error, unexpected integer");
  823. checkError("0abc",
  824. "<string>:1.1: syntax error, unexpected integer");
  825. checkError("10.0.1", "<string>:1.1-2: syntax error, unexpected integer");
  826. checkError("10.256.0.1",
  827. "<string>:1.1-10: Failed to convert 10.256.0.1 to "
  828. "an IP address.");
  829. checkError(":::",
  830. "<string>:1.1-3: Failed to convert ::: to an IP address.");
  831. checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
  832. checkError("option[-1].text",
  833. "<string>:1.8-9: Option code has invalid "
  834. "value in -1. Allowed range: 0..255");
  835. checkError("option[256].text",
  836. "<string>:1.8-10: Option code has invalid "
  837. "value in 256. Allowed range: 0..255");
  838. setUniverse(Option::V6);
  839. checkError("option[65536].text",
  840. "<string>:1.8-12: Option code has invalid "
  841. "value in 65536. Allowed range: 0..65535");
  842. setUniverse(Option::V4);
  843. checkError("option[12345678901234567890].text",
  844. "<string>:1.8-27: Failed to convert 12345678901234567890 "
  845. "to an integer.");
  846. checkError("option[123]",
  847. "<string>:1.12: syntax error, unexpected end of file,"
  848. " expecting .");
  849. checkError("option[123].text < 'foo'", "<string>:1.18: Invalid"
  850. " character: <");
  851. checkError("option[-ab].text", "<string>:1.8: Invalid character: -");
  852. checkError("option[0ab].text",
  853. "<string>:1.9-10: syntax error, unexpected option name, "
  854. "expecting ]");
  855. checkError("option[ab_].hex", "<string>:1.8: Invalid character: a");
  856. checkError("option[\nhost-name\n].hex =\n= 'foo'",
  857. "<string>:3.7: Invalid character: =");
  858. checkError("substring('foo',12345678901234567890,1)",
  859. "<string>:1.17-36: Failed to convert 12345678901234567890 "
  860. "to an integer.");
  861. }
  862. // Tests some parser error cases
  863. TEST_F(EvalContextTest, parseErrors) {
  864. checkError("'foo''bar'",
  865. "<string>:1.6-10: syntax error, unexpected constant string, "
  866. "expecting ==");
  867. checkError("'foo' (",
  868. "<string>:1.7: syntax error, unexpected (, expecting ==");
  869. checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
  870. checkError("'foo' ==",
  871. "<string>:1.9: syntax error, unexpected end of file");
  872. checkError("('foo' == 'bar'",
  873. "<string>:1.16: syntax error, unexpected end of file, "
  874. "expecting ) or and or or");
  875. checkError("('foo' == 'bar') ''",
  876. "<string>:1.18-19: syntax error, unexpected constant string, "
  877. "expecting end of file");
  878. checkError("not",
  879. "<string>:1.4: syntax error, unexpected end of file");
  880. checkError("not 'foo'",
  881. "<string>:1.10: syntax error, unexpected end of file, "
  882. "expecting ==");
  883. checkError("not()",
  884. "<string>:1.5: syntax error, unexpected )");
  885. checkError("(not('foo' 'bar')",
  886. "<string>:1.12-16: syntax error, unexpected constant string, "
  887. "expecting ==");
  888. checkError("and",
  889. "<string>:1.1-3: syntax error, unexpected and");
  890. checkError("'foo' and",
  891. "<string>:1.7-9: syntax error, unexpected and, expecting ==");
  892. checkError("'foo' == 'bar' and",
  893. "<string>:1.19: syntax error, unexpected end of file");
  894. checkError("'foo' == 'bar' and ''",
  895. "<string>:1.22: syntax error, unexpected end of file, "
  896. "expecting ==");
  897. checkError("or",
  898. "<string>:1.1-2: syntax error, unexpected or");
  899. checkError("'foo' or",
  900. "<string>:1.7-8: syntax error, unexpected or, expecting ==");
  901. checkError("'foo' == 'bar' or",
  902. "<string>:1.18: syntax error, unexpected end of file");
  903. checkError("'foo' == 'bar' or ''",
  904. "<string>:1.21: syntax error, unexpected end of file, "
  905. "expecting ==");
  906. checkError("option 'ab'",
  907. "<string>:1.8-11: syntax error, unexpected "
  908. "constant string, expecting [");
  909. checkError("option(10) == 'ab'",
  910. "<string>:1.7: syntax error, "
  911. "unexpected (, expecting [");
  912. checkError("option['ab'].text == 'foo'",
  913. "<string>:1.8-11: syntax error, "
  914. "unexpected constant string, "
  915. "expecting integer or option name");
  916. checkError("option[ab].text == 'foo'",
  917. "<string>:1.8-9: option 'ab' is not defined");
  918. checkError("option[0xa].text == 'ab'",
  919. "<string>:1.8-10: syntax error, "
  920. "unexpected constant hexstring, "
  921. "expecting integer or option name");
  922. checkError("option[10].bin", "<string>:1.12: Invalid character: b");
  923. checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
  924. checkError("option[10].exists == 'foo'",
  925. "<string>:1.19-20: syntax error, unexpected ==, "
  926. "expecting end of file");
  927. checkError("substring('foobar') == 'f'",
  928. "<string>:1.19: syntax error, unexpected ), expecting \",\"");
  929. checkError("substring('foobar',3) == 'bar'",
  930. "<string>:1.21: syntax error, unexpected ), expecting \",\"");
  931. checkError("substring('foobar','3',3) == 'bar'",
  932. "<string>:1.20-22: syntax error, unexpected constant string, "
  933. "expecting integer");
  934. checkError("substring('foobar',1,a) == 'foo'",
  935. "<string>:1.22: Invalid character: a");
  936. checkError("concat('foobar') == 'f'",
  937. "<string>:1.16: syntax error, unexpected ), expecting \",\"");
  938. checkError("concat('foo','bar','') == 'foobar'",
  939. "<string>:1.19: syntax error, unexpected \",\", expecting )");
  940. }
  941. // Tests some type error cases
  942. TEST_F(EvalContextTest, typeErrors) {
  943. checkError("'foobar'",
  944. "<string>:1.9: syntax error, unexpected end of file, "
  945. "expecting ==");
  946. checkError("substring('foobar',all,1) == 'foo'",
  947. "<string>:1.20-22: syntax error, unexpected all, "
  948. "expecting integer");
  949. checkError("substring('foobar',0x32,1) == 'foo'",
  950. "<string>:1.20-23: syntax error, unexpected constant "
  951. "hexstring, expecting integer");
  952. checkError("concat('foo',3) == 'foo3'",
  953. "<string>:1.14: syntax error, unexpected integer");
  954. checkError("concat(3,'foo') == '3foo'",
  955. "<string>:1.8: syntax error, unexpected integer");
  956. checkError("('foo' == 'bar') == 'false'",
  957. "<string>:1.18-19: syntax error, unexpected ==, "
  958. "expecting end of file");
  959. checkError("not 'true'",
  960. "<string>:1.11: syntax error, unexpected end of file, "
  961. "expecting ==");
  962. checkError("'true' and 'false'",
  963. "<string>:1.8-10: syntax error, unexpected and, expecting ==");
  964. checkError("'true' or 'false'",
  965. "<string>:1.8-9: syntax error, unexpected or, expecting ==");
  966. }
  967. };