A C++ toolbox repo until the pair uTL/dTL arives
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

sequencer.cpp 18 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /*!
  2. * \file sequencer.cpp
  3. *
  4. * \copyright Copyright (C) 2020 Christos Choutouridis <christos@choutouridis.net>
  5. *
  6. * <dl class=\"section copyright\"><dt>License</dt><dd>
  7. * The MIT License (MIT)
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in all
  17. * copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. * SOFTWARE.
  26. * </dd></dl>
  27. *
  28. */
  29. #include <com/sequencer.h>
  30. #include <gtest/gtest.h>
  31. #include <type_traits>
  32. #include <cstring>
  33. #include <ctime>
  34. namespace test_sequencer {
  35. using namespace tbx;
  36. // test settings
  37. using data_type = char;
  38. constexpr size_t size = 64;
  39. // Sequencer implementer mock
  40. class Seq : public sequencer<Seq, data_type, size> {
  41. static constexpr int NrCommands =5;
  42. static constexpr int NoCommand =-1;
  43. std::array<const char*, NrCommands> command = {
  44. "cmd1",
  45. "cmd2\n",
  46. "cmd3\r\n",
  47. "cmd4\n\r",
  48. "cmd5\n",
  49. };
  50. std::array<const char*, NrCommands> reply {
  51. "reply1",
  52. "reply2\n",
  53. "reply3\n text \r text text\n",
  54. "reply4\n text\n text \r text\r\n",
  55. "reply5\n",
  56. };
  57. int cmd =NoCommand;
  58. clock_t t =0;
  59. public:
  60. size_t get(char* data) {
  61. static int ans = 0;
  62. if ((++ans % 3) == 0)
  63. return 0;
  64. if (cmd == NoCommand) {
  65. std::strcpy(data, "ERROR\n");
  66. return 6;
  67. } else {
  68. std::strcpy(data, reply[cmd]);
  69. size_t s = std::strlen(reply[cmd]);
  70. cmd =NoCommand;
  71. return s;
  72. }
  73. }
  74. size_t contents (char* data) {
  75. if (cmd == NoCommand) {
  76. std::strcpy(data, "");
  77. return 0;
  78. } else {
  79. std::strcpy(data, reply[cmd]);
  80. return std::strlen(reply[cmd]);
  81. }
  82. }
  83. size_t put (const char* data, size_t n) {
  84. for (size_t i =0 ; i<NrCommands ; ++i) {
  85. if (!std::strcmp(data, command[i])) {
  86. cmd =i;
  87. return n;
  88. }
  89. }
  90. cmd =NoCommand;
  91. return n;
  92. }
  93. clock_t clock() { return ++t; }
  94. void clear_clock() { t =0; }
  95. };
  96. /*
  97. * Test sequencer object
  98. */
  99. TEST (Tsequencer, traits) {
  100. EXPECT_EQ ( std::is_default_constructible<Seq>::value, true);
  101. EXPECT_EQ ( std::is_nothrow_default_constructible<Seq>::value, true);
  102. EXPECT_EQ (!std::is_copy_constructible<Seq>::value, true);
  103. EXPECT_EQ (!std::is_copy_assignable<Seq>::value, true);
  104. EXPECT_EQ ((std::is_same_v<Seq::value_type, data_type>), true);
  105. EXPECT_EQ ((std::is_same_v<Seq::pointer_type, data_type*>), true);
  106. EXPECT_EQ ((std::is_same_v<Seq::size_type, size_t>), true);
  107. EXPECT_EQ ((std::is_same_v<Seq::string_view, std::basic_string_view<data_type>>), true);
  108. Seq s;
  109. EXPECT_EQ (s.size(), size);
  110. }
  111. TEST (Tsequencer, predicates) {
  112. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::equals), Seq::string_view, Seq::string_view>::value), true);
  113. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::starts_with), Seq::string_view, Seq::string_view>::value), true);
  114. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::ends_with), Seq::string_view, Seq::string_view>::value), true);
  115. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::contains), Seq::string_view, Seq::string_view>::value), true);
  116. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::always_true), Seq::string_view, Seq::string_view>::value), true);
  117. EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::always_false), Seq::string_view, Seq::string_view>::value), true);
  118. EXPECT_EQ (Seq::nil, nullptr);
  119. }
  120. TEST (Tsequencer, actions) {
  121. EXPECT_EQ ( std::is_default_constructible<Seq::action_t>::value, true);
  122. EXPECT_EQ ( std::is_nothrow_default_constructible<Seq::action_t>::value, true);
  123. EXPECT_EQ ( std::is_copy_constructible<Seq::action_t>::value, true);
  124. EXPECT_EQ ( std::is_copy_assignable<Seq::action_t>::value, true);
  125. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::no_action)>), true);
  126. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::next)>), true);
  127. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit_ok)>), true);
  128. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit_error)>), true);
  129. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::go_to<0>)>), true);
  130. EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit<0>)>), true);
  131. }
  132. bool handler_flag = false;
  133. const char* text = "abc";
  134. //static bool check_handle (const str_view_t buffer, const str_view_t token, match_ft match, handler_ft handle)
  135. TEST(Tsequencer, check_handle) {
  136. Seq s;
  137. // foo (5);
  138. // bar (5);
  139. using str_t = Seq::string_view;
  140. using val_t = Seq::value_type;
  141. auto match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return true; };
  142. auto no_match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return false; };
  143. auto check_match = [] (const str_t x, const str_t y) ->bool {
  144. return x == y;
  145. };
  146. auto handler = [](const val_t* v, size_t s){ (void)*v; (void)s; handler_flag = true; };
  147. auto set_if_abc = [](const val_t* v, size_t s){
  148. (void)*v; (void)s;
  149. handler_flag = (str_t(v, s) == "abc");
  150. };
  151. EXPECT_EQ (s.check_handle("", "", nullptr, nullptr), false);
  152. EXPECT_EQ (s.check_handle("", "", no_match, nullptr), false);
  153. EXPECT_EQ (s.check_handle("", "", match, nullptr), false);
  154. handler_flag = false;
  155. EXPECT_EQ (s.check_handle("", "", no_match, handler), false);
  156. EXPECT_EQ (handler_flag, false);
  157. handler_flag = false;
  158. EXPECT_EQ (s.check_handle("", "", match, handler), true);
  159. EXPECT_EQ (handler_flag, true);
  160. handler_flag = false;
  161. EXPECT_EQ (s.check_handle("abcd", "abc", check_match, set_if_abc), false);
  162. EXPECT_EQ (handler_flag, false);
  163. handler_flag = false;
  164. EXPECT_EQ (s.check_handle("abc", "abc", check_match, set_if_abc), true);
  165. EXPECT_EQ (handler_flag, true);
  166. handler_flag = false;
  167. EXPECT_EQ (s.check_handle("abc", "abcd", check_match, set_if_abc), false);
  168. EXPECT_EQ (handler_flag, false);
  169. }
  170. TEST(Tsequencer, run_nop_and_exits) {
  171. Seq s;
  172. const Seq::script_t<1> script1 = {{
  173. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
  174. }};
  175. s.clear_clock();
  176. EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
  177. EXPECT_GE (s.clock(), (clock_t)1000);
  178. const Seq::script_t<1> script2 = {{
  179. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_error, 1000}
  180. }};
  181. s.clear_clock();
  182. EXPECT_EQ (s.run(script2), Seq::exit_error.value);
  183. EXPECT_GE (s.clock(), (clock_t)1000);
  184. const Seq::script_t<3> script3 = {{
  185. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 1000},
  186. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 1000},
  187. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
  188. }};
  189. s.clear_clock();
  190. EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
  191. EXPECT_GE (s.clock(), (clock_t)3000);
  192. }
  193. TEST(Tsequencer, run_send) {
  194. Seq s;
  195. auto send_wrapper = [](const data_type* d, size_t s){
  196. (void)*d; (void)s; handler_flag = true;
  197. };
  198. auto send_chk_text = [](const data_type* d, size_t s){
  199. handler_flag = (Seq::string_view(d,s) == Seq::string_view(text));
  200. };
  201. const Seq::script_t<2> script1 = {{
  202. {Seq::control_t::SEND, "", Seq::nil, Seq::nil, Seq::next, 0},
  203. {Seq::control_t::SEND, "", Seq::nil, send_wrapper, Seq::exit_ok, 0}
  204. }};
  205. handler_flag =false;
  206. EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
  207. EXPECT_EQ (handler_flag, true);
  208. const Seq::script_t<1> script2 = {{
  209. {Seq::control_t::SEND, "abcd", Seq::nil, send_chk_text, Seq::exit_ok, 0}
  210. }};
  211. handler_flag =false;
  212. EXPECT_EQ (s.run(script2), Seq::exit_ok.value);
  213. EXPECT_EQ (handler_flag, false);
  214. const Seq::script_t<2> script3 = {{
  215. {Seq::control_t::SEND, text, Seq::nil, send_chk_text, Seq::exit_ok, 0}
  216. }};
  217. handler_flag =false;
  218. EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
  219. EXPECT_EQ (handler_flag, true);
  220. }
  221. TEST(Tsequencer, run_expect) {
  222. Seq s;
  223. const Seq::script_t<7> script = {{
  224. {Seq::control_t::EXPECT, "reply1", Seq::equals, Seq::nil, Seq::exit<1UL>, 1000},
  225. {Seq::control_t::OR_EXPECT, "reply2", Seq::starts_with, Seq::nil, Seq::exit<2UL>, 0},
  226. {Seq::control_t::OR_EXPECT, "reply3", Seq::starts_with, Seq::nil, Seq::exit<3UL>, 0},
  227. {Seq::control_t::OR_EXPECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::exit<4UL>, 0},
  228. {Seq::control_t::OR_EXPECT, "reply5", Seq::starts_with, Seq::nil, Seq::exit<5UL>, 0},
  229. {Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit<6UL>, 0},
  230. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_error, 1000}
  231. }};
  232. s.clear_clock();
  233. s.put("cmd1", std::strlen("cmd1"));
  234. EXPECT_EQ (s.run(script), 1UL);
  235. EXPECT_LT (s.clock(), (clock_t)1000);
  236. s.clear_clock();
  237. s.put("cmd2\n", std::strlen("cmd2\n"));
  238. EXPECT_EQ (s.run(script), 2UL);
  239. EXPECT_LT (s.clock(), (clock_t)1000);
  240. s.clear_clock();
  241. s.put("cmd3\r\n", std::strlen("cmd3\r\n"));
  242. EXPECT_EQ (s.run(script), 3UL);
  243. EXPECT_LT (s.clock(), (clock_t)1000);
  244. s.clear_clock();
  245. s.put("cmd4\n\r", std::strlen("cmd4\n\r"));
  246. EXPECT_EQ (s.run(script), 4UL);
  247. EXPECT_LT (s.clock(), (clock_t)1000);
  248. s.clear_clock();
  249. s.put("cmd5\n", std::strlen("cmd5\n"));
  250. EXPECT_EQ (s.run(script), 5UL);
  251. EXPECT_LT (s.clock(), (clock_t)1000);
  252. s.clear_clock();
  253. s.put("cmd", std::strlen("cmd"));
  254. EXPECT_EQ (s.run(script), 6UL);
  255. EXPECT_LT (s.clock(), (clock_t)1000);
  256. }
  257. TEST(Tsequencer, run_detect) {
  258. Seq s;
  259. const Seq::script_t<7> script = {{
  260. {Seq::control_t::DETECT, "reply1", Seq::equals, Seq::nil, Seq::exit<1UL>, 1000},
  261. {Seq::control_t::OR_DETECT, "reply2", Seq::starts_with, Seq::nil, Seq::exit<2UL>, 0},
  262. {Seq::control_t::OR_DETECT, "reply3", Seq::starts_with, Seq::nil, Seq::exit<3UL>, 0},
  263. {Seq::control_t::OR_DETECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::exit<4UL>, 0},
  264. {Seq::control_t::OR_DETECT, "reply5", Seq::starts_with, Seq::nil, Seq::exit<5UL>, 0},
  265. {Seq::control_t::OR_DETECT, "ERROR", Seq::contains, Seq::nil, Seq::exit<6UL>, 0},
  266. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
  267. }};
  268. s.clear_clock();
  269. s.put("cmd1", std::strlen("cmd1"));
  270. EXPECT_EQ (s.run(script), 1UL);
  271. EXPECT_LT (s.clock(), (clock_t)1000);
  272. s.clear_clock();
  273. s.put("cmd2\n", std::strlen("cmd2\n"));
  274. EXPECT_EQ (s.run(script), 2UL);
  275. EXPECT_LT (s.clock(), (clock_t)1000);
  276. s.clear_clock();
  277. s.put("cmd3\r\n", std::strlen("cmd3\r\n"));
  278. EXPECT_EQ (s.run(script), 3UL);
  279. EXPECT_LT (s.clock(), (clock_t)1000);
  280. s.clear_clock();
  281. s.put("cmd4\n\r", std::strlen("cmd4\n\r"));
  282. EXPECT_EQ (s.run(script), 4UL);
  283. EXPECT_LT (s.clock(), (clock_t)1000);
  284. s.clear_clock();
  285. s.put("cmd5\n", std::strlen("cmd5\n"));
  286. EXPECT_EQ (s.run(script), 5UL);
  287. EXPECT_LT (s.clock(), (clock_t)1000);
  288. s.clear_clock();
  289. s.put("cmd", std::strlen("cmd"));
  290. EXPECT_EQ (s.run(script), Seq::exit_error.value);
  291. EXPECT_GT (s.clock(), (clock_t)1000);
  292. }
  293. TEST(Tsequencer, run_script_blocks_n_gotos) {
  294. Seq s;
  295. const Seq::script_t<15> script = {{
  296. /* 0 */{Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<1>, 1000},
  297. /* 1 */{Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
  298. /* 2 */{Seq::control_t::EXPECT, "reply1", Seq::starts_with, Seq::nil, Seq::next, 1000},
  299. /* 3 */{Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
  300. /* 4 */{Seq::control_t::SEND, "cmd2\n", Seq::nil, Seq::nil, Seq::next, 0},
  301. /* 5 */{Seq::control_t::DETECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 1000},
  302. /* 6 */{Seq::control_t::OR_DETECT, "reply2", Seq::contains, Seq::nil, Seq::go_to<11>, 0},
  303. /* 7 */{Seq::control_t::SEND, "cmd3\r\n", Seq::nil, Seq::nil, Seq::next, 0},
  304. /* 8 */{Seq::control_t::EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 1000},
  305. /* 9 */{Seq::control_t::OR_EXPECT, "lalala", Seq::starts_with, Seq::nil, Seq::exit_error, 0},
  306. /*10 */{Seq::control_t::OR_EXPECT, "text\n", Seq::ends_with, Seq::nil, Seq::go_to<14>, 0},
  307. /*11 */{Seq::control_t::SEND, "cmd4\n\r", Seq::nil, Seq::nil, Seq::next, 0},
  308. /*12 */{Seq::control_t::EXPECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::go_to<7>, 1000},
  309. /*13 */{Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
  310. /*14 */{Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
  311. }};
  312. s.clear_clock();
  313. EXPECT_EQ (s.run(script), Seq::exit_ok.value);
  314. EXPECT_GT (s.clock(), (clock_t)2000);
  315. }
  316. TEST(Tsequencer, run_match_n_handler) {
  317. Seq s;
  318. using str_t = Seq::string_view;
  319. using val_t = Seq::value_type;
  320. auto match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return true; };
  321. auto check_match = [] (const str_t x, const str_t y) ->bool {
  322. return x == y;
  323. };
  324. auto handler = [](const val_t* v, size_t s){ (void)*v; (void)s; handler_flag = true; };
  325. auto set_if_rpl2 = [](const val_t* v, size_t s){
  326. (void)*v; (void)s;
  327. handler_flag = (str_t(v, s) == "reply2\n");
  328. };
  329. const Seq::script_t<4> script1 = {{
  330. {Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
  331. {Seq::control_t::EXPECT, "", match, Seq::nil, Seq::next, 1000},
  332. {Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
  333. {Seq::control_t::SEND, "cmd1", Seq::nil, handler, Seq::exit_ok, 0}
  334. }};
  335. handler_flag = false;
  336. s.clear_clock();
  337. EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
  338. EXPECT_LT (s.clock(), (clock_t)1000);
  339. EXPECT_EQ (handler_flag, true);
  340. const Seq::script_t<2> script2 = {{
  341. {Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
  342. {Seq::control_t::EXPECT, "reply1", check_match, set_if_rpl2, Seq::exit_ok, 1000},
  343. }};
  344. handler_flag = false;
  345. EXPECT_EQ (s.run(script2), Seq::exit_ok.value);
  346. EXPECT_EQ (handler_flag, false);
  347. const Seq::script_t<2> script3 = {{
  348. {Seq::control_t::SEND, "cmd2\n", Seq::nil, Seq::nil, Seq::next, 0},
  349. {Seq::control_t::EXPECT, "reply2\n", check_match, set_if_rpl2, Seq::exit_ok, 1000},
  350. }};
  351. handler_flag = false;
  352. EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
  353. EXPECT_EQ (handler_flag, true);
  354. const Seq::script_t<1> script4 = {{
  355. {Seq::control_t::SEND, "cmd1", Seq::nil, handler, Seq::exit_ok, 0}
  356. }};
  357. handler_flag = false;
  358. EXPECT_EQ (s.run(script4), Seq::exit_ok.value);
  359. EXPECT_EQ (handler_flag, true);
  360. }
  361. TEST(Tsequencer, run_boundaries) {
  362. Seq s;
  363. const Seq::script_t<1> script1 = {{
  364. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 0},
  365. }};
  366. EXPECT_EQ (s.run(script1), Seq::exit_error.value);
  367. const Seq::script_t<1> script2 = {{
  368. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<1>, 0},
  369. }};
  370. EXPECT_EQ (s.run(script2), Seq::exit_error.value);
  371. const Seq::script_t<1> script3 = {{
  372. {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<(size_t)-1>, 0},
  373. }};
  374. EXPECT_EQ (s.run(script3), Seq::exit_error.value);
  375. const Seq::script_t<1> script4 = {{
  376. {Seq::control_t::EXPECT, "abc", Seq::nil, Seq::nil, Seq::next, 1000},
  377. }};
  378. s.clear_clock();
  379. EXPECT_EQ (s.run(script4), Seq::exit_error.value);
  380. EXPECT_GT (s.clock(), (clock_t)1000);
  381. }
  382. }