context_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Copyright (C) 2015 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 <eval/token.h>
  16. #include <eval/eval_context.h>
  17. #include <eval/token.h>
  18. #include <dhcp/pkt4.h>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/scoped_ptr.hpp>
  21. #include <gtest/gtest.h>
  22. using namespace std;
  23. using namespace isc::dhcp;
  24. namespace {
  25. /// @brief Test class for testing EvalContext aka class test parsing
  26. class EvalContextTest : public ::testing::Test {
  27. public:
  28. /// @brief checks if the given token is a string with the expected value
  29. void checkTokenString(const TokenPtr& token, const std::string& expected) {
  30. ASSERT_TRUE(token);
  31. boost::shared_ptr<TokenString> str =
  32. boost::dynamic_pointer_cast<TokenString>(token);
  33. ASSERT_TRUE(str);
  34. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  35. ValueStack values;
  36. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  37. ASSERT_EQ(1, values.size());
  38. EXPECT_EQ(expected, values.top());
  39. }
  40. /// @brief checks if the given token is a hex string with the expected value
  41. void checkTokenHexString(const TokenPtr& token,
  42. const std::string& expected) {
  43. ASSERT_TRUE(token);
  44. boost::shared_ptr<TokenHexString> hex =
  45. boost::dynamic_pointer_cast<TokenHexString>(token);
  46. ASSERT_TRUE(hex);
  47. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  48. ValueStack values;
  49. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  50. ASSERT_EQ(1, values.size());
  51. EXPECT_EQ(expected, values.top());
  52. }
  53. /// @brief checks if the given token is an equal operator
  54. void checkTokenEq(const TokenPtr& token) {
  55. ASSERT_TRUE(token);
  56. boost::shared_ptr<TokenEqual> eq =
  57. boost::dynamic_pointer_cast<TokenEqual>(token);
  58. EXPECT_TRUE(eq);
  59. }
  60. /// @brief checks if the given token is an option with the expected code
  61. void checkTokenOption(const TokenPtr& token, uint16_t expected_code) {
  62. ASSERT_TRUE(token);
  63. boost::shared_ptr<TokenOption> opt =
  64. boost::dynamic_pointer_cast<TokenOption>(token);
  65. ASSERT_TRUE(opt);
  66. EXPECT_EQ(expected_code, opt->getCode());
  67. }
  68. /// @brief checks if the given token is a substring operator
  69. void checkTokenSubstring(const TokenPtr& token) {
  70. ASSERT_TRUE(token);
  71. boost::shared_ptr<TokenSubstring> sub =
  72. boost::dynamic_pointer_cast<TokenSubstring>(token);
  73. EXPECT_TRUE(sub);
  74. }
  75. /// @brief checks if the given expression raises the expected message
  76. /// when it is parsed.
  77. void checkError(const string& expr, const string& msg) {
  78. EvalContext eval;
  79. parsed_ = false;
  80. try {
  81. parsed_ = eval.parseString(expr);
  82. FAIL() << "Expected EvalParseError but nothing was raised";
  83. }
  84. catch (const EvalParseError& ex) {
  85. EXPECT_EQ(msg, ex.what());
  86. EXPECT_FALSE(parsed_);
  87. }
  88. catch (...) {
  89. FAIL() << "Expected EvalParseError but something else was raised";
  90. }
  91. }
  92. /// @brief checks if the given expression raises the expected message
  93. /// when it is parsed by the strongly typed parser but is accepted
  94. /// by the untyped one.
  95. void checkTyped(const string& expr, const string& msg) {
  96. EvalContext eval;
  97. parsed_ = false;
  98. EXPECT_NO_THROW(parsed_ = eval.parseString("untyped:" + expr));
  99. EXPECT_TRUE(parsed_);
  100. parsed_ = false;
  101. try {
  102. parsed_ = eval.parseString(expr);
  103. FAIL() << "Expected EvalParseError but nothing was raised";
  104. }
  105. catch (const EvalParseError& ex) {
  106. EXPECT_EQ(msg, ex.what());
  107. EXPECT_FALSE(parsed_);
  108. }
  109. catch (...) {
  110. FAIL() << "Expected EvalParseError but something else was raised";
  111. }
  112. }
  113. bool parsed_; ///< Parsing status
  114. };
  115. // Test the parsing of a basic expression
  116. TEST_F(EvalContextTest, basic) {
  117. EvalContext tmp;
  118. EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123] == 'MSFT'"));
  119. EXPECT_TRUE(parsed_);
  120. }
  121. // Test the parsing of a string terminal
  122. TEST_F(EvalContextTest, string) {
  123. EvalContext eval;
  124. EXPECT_NO_THROW(parsed_ = eval.parseString("untyped: 'foo'"));
  125. EXPECT_TRUE(parsed_);
  126. ASSERT_EQ(1, eval.expression.size());
  127. TokenPtr tmp = eval.expression.at(0);
  128. checkTokenString(tmp, "foo");
  129. }
  130. // Test the parsing of a hexstring terminal
  131. TEST_F(EvalContextTest, hexstring) {
  132. EvalContext eval;
  133. EXPECT_NO_THROW(parsed_ = eval.parseString("untyped: 0x666f6f"));
  134. EXPECT_TRUE(parsed_);
  135. ASSERT_EQ(1, eval.expression.size());
  136. TokenPtr tmp = eval.expression.at(0);
  137. checkTokenHexString(tmp, "foo");
  138. }
  139. // Test the parsing of a hexstring terminal with an odd number of
  140. // hexadecimal digits
  141. TEST_F(EvalContextTest, oddHexstring) {
  142. EvalContext eval;
  143. EXPECT_NO_THROW(parsed_ = eval.parseString("untyped: 0X7"));
  144. EXPECT_TRUE(parsed_);
  145. ASSERT_EQ(1, eval.expression.size());
  146. TokenPtr tmp = eval.expression.at(0);
  147. checkTokenHexString(tmp, "\a");
  148. }
  149. // Test the parsing of an equal expression
  150. TEST_F(EvalContextTest, equal) {
  151. EvalContext eval;
  152. EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
  153. EXPECT_TRUE(parsed_);
  154. ASSERT_EQ(3, eval.expression.size());
  155. TokenPtr tmp1 = eval.expression.at(0);
  156. TokenPtr tmp2 = eval.expression.at(1);
  157. TokenPtr tmp3 = eval.expression.at(2);
  158. checkTokenString(tmp1, "foo");
  159. checkTokenString(tmp2, "bar");
  160. checkTokenEq(tmp3);
  161. }
  162. // Test the parsing of an option terminal
  163. TEST_F(EvalContextTest, option) {
  164. EvalContext eval;
  165. EXPECT_NO_THROW(parsed_ = eval.parseString("untyped: option[123]"));
  166. EXPECT_TRUE(parsed_);
  167. ASSERT_EQ(1, eval.expression.size());
  168. checkTokenOption(eval.expression.at(0), 123);
  169. }
  170. // Test the parsing of a substring expression
  171. TEST_F(EvalContextTest, substring) {
  172. EvalContext eval;
  173. EXPECT_NO_THROW(parsed_ =
  174. eval.parseString("untyped: substring('foobar','2','3')"));
  175. EXPECT_TRUE(parsed_);
  176. ASSERT_EQ(4, eval.expression.size());
  177. TokenPtr tmp1 = eval.expression.at(0);
  178. TokenPtr tmp2 = eval.expression.at(1);
  179. TokenPtr tmp3 = eval.expression.at(2);
  180. TokenPtr tmp4 = eval.expression.at(3);
  181. checkTokenString(tmp1, "foobar");
  182. checkTokenString(tmp2, "2");
  183. checkTokenString(tmp3, "3");
  184. checkTokenSubstring(tmp4);
  185. }
  186. // Test some scanner error cases
  187. TEST_F(EvalContextTest, scanErrors) {
  188. checkError("'", "<string>:1.1: Invalid character: '");
  189. checkError("'\''", "<string>:1.3: Invalid character: '");
  190. checkError("'\n'", "<string>:1.1: Invalid character: '");
  191. checkError("0x123h", "<string>:1.6: Invalid character: h");
  192. checkError("=", "<string>:1.1: Invalid character: =");
  193. checkError("option[65536]",
  194. "<string>:1.8-12: Option code has invalid "
  195. "value in 65536. Allowed range: 0..65535");
  196. checkError("subtring", "<string>:1.1: Invalid character: s");
  197. checkError("untype: 'abc'", "<string>:1.1: Invalid character: u");
  198. checkError("untyped 'abc'", "<string>:1.1: Invalid character: u");
  199. checkError("foo", "<string>:1.1: Invalid character: f");
  200. checkError(" bar", "<string>:1.2: Invalid character: b");
  201. }
  202. // Tests some scanner/parser error cases
  203. TEST_F(EvalContextTest, scanParseErrors) {
  204. checkError("", "<string>:1.1: syntax error, unexpected end of file");
  205. checkError("untyped:",
  206. "<string>:1.9: syntax error, unexpected end of file");
  207. checkError("0x", "<string>:1.1: syntax error, unexpected option code");
  208. checkError("0abc",
  209. "<string>:1.1: syntax error, unexpected option code");
  210. checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
  211. checkError("option[123] < 'foo'", "<string>:1.13: Invalid character: <");
  212. }
  213. // Tests some parser error cases
  214. TEST_F(EvalContextTest, parseErrors) {
  215. checkError("untyped:'foo''bar'",
  216. "<string>:1.14-18: syntax error, unexpected "
  217. "constant string, expecting end of file");
  218. checkError("'foo''bar'",
  219. "<string>:1.6-10: syntax error, unexpected constant string, "
  220. "expecting ==");
  221. checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
  222. checkError("'foo' ==",
  223. "<string>:1.9: syntax error, unexpected end of file");
  224. checkError("option 'ab'",
  225. "<string>:1.8-11: syntax error, unexpected "
  226. "constant string, expecting [");
  227. checkError("option(10) == 'ab'",
  228. "<string>:1.7: syntax error, "
  229. "unexpected (, expecting [");
  230. checkError("option['ab'] == 'foo'",
  231. "<string>:1.8-11: syntax error, "
  232. "unexpected constant string, "
  233. "expecting option code");
  234. checkError("option[0xa] == 'ab'",
  235. "<string>:1.8-10: syntax error, "
  236. "unexpected constant hexstring, "
  237. "expecting option code");
  238. checkError("substring('foobar') == 'f'",
  239. "<string>:1.19: syntax error, "
  240. "unexpected ), expecting \",\"");
  241. checkError("substring('foobar','3') == 'bar'",
  242. "<string>:1.23: syntax error, unexpected ), expecting \",\"");
  243. checkError("substring('foobar',3,3) == 'bar'",
  244. "<string>:1.20: syntax error, unexpected option code, "
  245. "expecting a number in a constant string");
  246. }
  247. // Tests some type error cases (caught only by the strongly typed parser)
  248. TEST_F(EvalContextTest, typeErrors) {
  249. checkTyped("'foobar'",
  250. "<string>:1.9: syntax error, unexpected end of file, "
  251. "expecting ==");
  252. checkTyped("substring('foobar','a','1') == 'foo'",
  253. "<string>:1.20-22: syntax error, unexpected constant string, "
  254. "expecting a number in a constant string");
  255. checkTyped("substring('foobar','1','a') == 'foo'",
  256. "<string>:1.24-26: syntax error, unexpected constant string, "
  257. "expecting a number in a constant string or the all constant "
  258. "string");
  259. checkTyped("substring('foobar',0x32,'1') == 'foo'",
  260. "<string>:1.20-23: syntax error, unexpected constant "
  261. "hexstring, expecting a number in a constant string");
  262. }
  263. };