|
- /*!
- * \file sequencer.cpp
- *
- * \copyright Copyright (C) 2020 Christos Choutouridis <christos@choutouridis.net>
- *
- * <dl class=\"section copyright\"><dt>License</dt><dd>
- * The MIT License (MIT)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * </dd></dl>
- *
- */
- #include <com/sequencer.h>
- #include <gtest/gtest.h>
-
- #include <type_traits>
- #include <cstring>
- #include <ctime>
-
- namespace test_sequencer {
- using namespace tbx;
-
- // test settings
- using data_type = char;
- constexpr size_t size = 64;
-
- // Sequencer implementer mock
- class Seq : public sequencer<Seq, data_type, size> {
- static constexpr int NrCommands =5;
- static constexpr int NoCommand =-1;
-
- std::array<const char*, NrCommands> command = {
- "cmd1",
- "cmd2\n",
- "cmd3\r\n",
- "cmd4\n\r",
- "cmd5\n",
- };
- std::array<const char*, NrCommands> reply {
- "reply1",
- "reply2\n",
- "reply3\n text \r text text\n",
- "reply4\n text\n text \r text\r\n",
- "reply5\n",
- };
- int cmd =NoCommand;
- clock_t t =0;
-
- public:
- size_t get(char* data) {
- static int ans = 0;
- if ((++ans % 3) == 0)
- return 0;
-
- if (cmd == NoCommand) {
- std::strcpy(data, "ERROR\n");
- return 6;
- } else {
- std::strcpy(data, reply[cmd]);
- size_t s = std::strlen(reply[cmd]);
- cmd =NoCommand;
- return s;
- }
- }
- size_t contents (char* data) {
- if (cmd == NoCommand) {
- std::strcpy(data, "");
- return 0;
- } else {
- std::strcpy(data, reply[cmd]);
- return std::strlen(reply[cmd]);
- }
- }
- size_t put (const char* data, size_t n) {
- for (size_t i =0 ; i<NrCommands ; ++i) {
- if (!std::strcmp(data, command[i])) {
- cmd =i;
- return n;
- }
- }
- cmd =NoCommand;
- return n;
- }
- clock_t clock() { return ++t; }
- void clear_clock() { t =0; }
- };
-
- /*
- * Test sequencer object
- */
- TEST (Tsequencer, traits) {
- EXPECT_EQ ( std::is_default_constructible<Seq>::value, true);
- EXPECT_EQ ( std::is_nothrow_default_constructible<Seq>::value, true);
- EXPECT_EQ (!std::is_copy_constructible<Seq>::value, true);
- EXPECT_EQ (!std::is_copy_assignable<Seq>::value, true);
-
- EXPECT_EQ ((std::is_same_v<Seq::value_type, data_type>), true);
- EXPECT_EQ ((std::is_same_v<Seq::pointer_type, data_type*>), true);
- EXPECT_EQ ((std::is_same_v<Seq::size_type, size_t>), true);
- EXPECT_EQ ((std::is_same_v<Seq::string_view, std::basic_string_view<data_type>>), true);
-
- Seq s;
- EXPECT_EQ (s.size(), size);
- }
-
- TEST (Tsequencer, predicates) {
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::equals), Seq::string_view, Seq::string_view>::value), true);
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::starts_with), Seq::string_view, Seq::string_view>::value), true);
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::ends_with), Seq::string_view, Seq::string_view>::value), true);
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::contains), Seq::string_view, Seq::string_view>::value), true);
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::always_true), Seq::string_view, Seq::string_view>::value), true);
- EXPECT_EQ ((std::is_invocable_r<bool, decltype(Seq::always_false), Seq::string_view, Seq::string_view>::value), true);
-
- EXPECT_EQ (Seq::nil, nullptr);
- }
-
- TEST (Tsequencer, actions) {
- EXPECT_EQ ( std::is_default_constructible<Seq::action_t>::value, true);
- EXPECT_EQ ( std::is_nothrow_default_constructible<Seq::action_t>::value, true);
- EXPECT_EQ ( std::is_copy_constructible<Seq::action_t>::value, true);
- EXPECT_EQ ( std::is_copy_assignable<Seq::action_t>::value, true);
-
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::no_action)>), true);
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::next)>), true);
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit_ok)>), true);
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit_error)>), true);
-
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::go_to<0>)>), true);
- EXPECT_EQ ((std::is_same_v<const Seq::action_t, decltype(Seq::exit<0>)>), true);
- }
-
- bool handler_flag = false;
- const char* text = "abc";
-
- //static bool check_handle (const str_view_t buffer, const str_view_t token, match_ft match, handler_ft handle)
- TEST(Tsequencer, check_handle) {
- Seq s;
- // foo (5);
- // bar (5);
- using str_t = Seq::string_view;
- using val_t = Seq::value_type;
-
- auto match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return true; };
- auto no_match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return false; };
- auto check_match = [] (const str_t x, const str_t y) ->bool {
- return x == y;
- };
- auto handler = [](const val_t* v, size_t s){ (void)*v; (void)s; handler_flag = true; };
- auto set_if_abc = [](const val_t* v, size_t s){
- (void)*v; (void)s;
- handler_flag = (str_t(v, s) == "abc");
- };
-
- EXPECT_EQ (s.check_handle("", "", nullptr, nullptr), false);
- EXPECT_EQ (s.check_handle("", "", no_match, nullptr), false);
- EXPECT_EQ (s.check_handle("", "", match, nullptr), false);
-
- handler_flag = false;
- EXPECT_EQ (s.check_handle("", "", no_match, handler), false);
- EXPECT_EQ (handler_flag, false);
-
- handler_flag = false;
- EXPECT_EQ (s.check_handle("", "", match, handler), true);
- EXPECT_EQ (handler_flag, true);
-
- handler_flag = false;
- EXPECT_EQ (s.check_handle("abcd", "abc", check_match, set_if_abc), false);
- EXPECT_EQ (handler_flag, false);
-
- handler_flag = false;
- EXPECT_EQ (s.check_handle("abc", "abc", check_match, set_if_abc), true);
- EXPECT_EQ (handler_flag, true);
-
- handler_flag = false;
- EXPECT_EQ (s.check_handle("abc", "abcd", check_match, set_if_abc), false);
- EXPECT_EQ (handler_flag, false);
-
- }
-
- TEST(Tsequencer, run_nop_and_exits) {
- Seq s;
- const Seq::script_t<1> script1 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
- }};
- s.clear_clock();
- EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
- EXPECT_GE (s.clock(), (clock_t)1000);
-
- const Seq::script_t<1> script2 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_error, 1000}
- }};
- s.clear_clock();
- EXPECT_EQ (s.run(script2), Seq::exit_error.value);
- EXPECT_GE (s.clock(), (clock_t)1000);
-
- const Seq::script_t<3> script3 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 1000},
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 1000},
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
- }};
- s.clear_clock();
- EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
- EXPECT_GE (s.clock(), (clock_t)3000);
- }
-
- TEST(Tsequencer, run_send) {
- Seq s;
-
- auto send_wrapper = [](const data_type* d, size_t s){
- (void)*d; (void)s; handler_flag = true;
- };
- auto send_chk_text = [](const data_type* d, size_t s){
- handler_flag = (Seq::string_view(d,s) == Seq::string_view(text));
- };
-
- const Seq::script_t<2> script1 = {{
- {Seq::control_t::SEND, "", Seq::nil, Seq::nil, Seq::next, 0},
- {Seq::control_t::SEND, "", Seq::nil, send_wrapper, Seq::exit_ok, 0}
- }};
- handler_flag =false;
- EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, true);
-
-
- const Seq::script_t<1> script2 = {{
- {Seq::control_t::SEND, "abcd", Seq::nil, send_chk_text, Seq::exit_ok, 0}
- }};
- handler_flag =false;
- EXPECT_EQ (s.run(script2), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, false);
-
- const Seq::script_t<2> script3 = {{
- {Seq::control_t::SEND, text, Seq::nil, send_chk_text, Seq::exit_ok, 0}
- }};
- handler_flag =false;
- EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, true);
- }
-
- TEST(Tsequencer, run_expect) {
- Seq s;
-
- const Seq::script_t<7> script = {{
- {Seq::control_t::EXPECT, "reply1", Seq::equals, Seq::nil, Seq::exit<1UL>, 1000},
- {Seq::control_t::OR_EXPECT, "reply2", Seq::starts_with, Seq::nil, Seq::exit<2UL>, 0},
- {Seq::control_t::OR_EXPECT, "reply3", Seq::starts_with, Seq::nil, Seq::exit<3UL>, 0},
- {Seq::control_t::OR_EXPECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::exit<4UL>, 0},
- {Seq::control_t::OR_EXPECT, "reply5", Seq::starts_with, Seq::nil, Seq::exit<5UL>, 0},
- {Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit<6UL>, 0},
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_error, 1000}
- }};
-
- s.clear_clock();
- s.put("cmd1", std::strlen("cmd1"));
- EXPECT_EQ (s.run(script), 1UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd2\n", std::strlen("cmd2\n"));
- EXPECT_EQ (s.run(script), 2UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd3\r\n", std::strlen("cmd3\r\n"));
- EXPECT_EQ (s.run(script), 3UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd4\n\r", std::strlen("cmd4\n\r"));
- EXPECT_EQ (s.run(script), 4UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd5\n", std::strlen("cmd5\n"));
- EXPECT_EQ (s.run(script), 5UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd", std::strlen("cmd"));
- EXPECT_EQ (s.run(script), 6UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
- }
-
- TEST(Tsequencer, run_detect) {
- Seq s;
-
- const Seq::script_t<7> script = {{
- {Seq::control_t::DETECT, "reply1", Seq::equals, Seq::nil, Seq::exit<1UL>, 1000},
- {Seq::control_t::OR_DETECT, "reply2", Seq::starts_with, Seq::nil, Seq::exit<2UL>, 0},
- {Seq::control_t::OR_DETECT, "reply3", Seq::starts_with, Seq::nil, Seq::exit<3UL>, 0},
- {Seq::control_t::OR_DETECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::exit<4UL>, 0},
- {Seq::control_t::OR_DETECT, "reply5", Seq::starts_with, Seq::nil, Seq::exit<5UL>, 0},
- {Seq::control_t::OR_DETECT, "ERROR", Seq::contains, Seq::nil, Seq::exit<6UL>, 0},
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
- }};
-
- s.clear_clock();
- s.put("cmd1", std::strlen("cmd1"));
- EXPECT_EQ (s.run(script), 1UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd2\n", std::strlen("cmd2\n"));
- EXPECT_EQ (s.run(script), 2UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd3\r\n", std::strlen("cmd3\r\n"));
- EXPECT_EQ (s.run(script), 3UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd4\n\r", std::strlen("cmd4\n\r"));
- EXPECT_EQ (s.run(script), 4UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd5\n", std::strlen("cmd5\n"));
- EXPECT_EQ (s.run(script), 5UL);
- EXPECT_LT (s.clock(), (clock_t)1000);
-
- s.clear_clock();
- s.put("cmd", std::strlen("cmd"));
- EXPECT_EQ (s.run(script), Seq::exit_error.value);
- EXPECT_GT (s.clock(), (clock_t)1000);
- }
-
- TEST(Tsequencer, run_script_blocks_n_gotos) {
- Seq s;
- const Seq::script_t<15> script = {{
- /* 0 */{Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<1>, 1000},
-
- /* 1 */{Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
- /* 2 */{Seq::control_t::EXPECT, "reply1", Seq::starts_with, Seq::nil, Seq::next, 1000},
- /* 3 */{Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
-
- /* 4 */{Seq::control_t::SEND, "cmd2\n", Seq::nil, Seq::nil, Seq::next, 0},
- /* 5 */{Seq::control_t::DETECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 1000},
- /* 6 */{Seq::control_t::OR_DETECT, "reply2", Seq::contains, Seq::nil, Seq::go_to<11>, 0},
-
- /* 7 */{Seq::control_t::SEND, "cmd3\r\n", Seq::nil, Seq::nil, Seq::next, 0},
- /* 8 */{Seq::control_t::EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 1000},
- /* 9 */{Seq::control_t::OR_EXPECT, "lalala", Seq::starts_with, Seq::nil, Seq::exit_error, 0},
- /*10 */{Seq::control_t::OR_EXPECT, "text\n", Seq::ends_with, Seq::nil, Seq::go_to<14>, 0},
-
- /*11 */{Seq::control_t::SEND, "cmd4\n\r", Seq::nil, Seq::nil, Seq::next, 0},
- /*12 */{Seq::control_t::EXPECT, "reply4\n", Seq::starts_with, Seq::nil, Seq::go_to<7>, 1000},
- /*13 */{Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
-
- /*14 */{Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::exit_ok, 1000}
- }};
- s.clear_clock();
- EXPECT_EQ (s.run(script), Seq::exit_ok.value);
- EXPECT_GT (s.clock(), (clock_t)2000);
- }
-
- TEST(Tsequencer, run_match_n_handler) {
- Seq s;
-
- using str_t = Seq::string_view;
- using val_t = Seq::value_type;
-
- auto match = [](const str_t x, const str_t y) ->bool { (void)x; (void)y; return true; };
- auto check_match = [] (const str_t x, const str_t y) ->bool {
- return x == y;
- };
- auto handler = [](const val_t* v, size_t s){ (void)*v; (void)s; handler_flag = true; };
- auto set_if_rpl2 = [](const val_t* v, size_t s){
- (void)*v; (void)s;
- handler_flag = (str_t(v, s) == "reply2\n");
- };
-
- const Seq::script_t<4> script1 = {{
- {Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
- {Seq::control_t::EXPECT, "", match, Seq::nil, Seq::next, 1000},
- {Seq::control_t::OR_EXPECT, "ERROR", Seq::contains, Seq::nil, Seq::exit_error, 0},
-
- {Seq::control_t::SEND, "cmd1", Seq::nil, handler, Seq::exit_ok, 0}
- }};
- handler_flag = false;
- s.clear_clock();
- EXPECT_EQ (s.run(script1), Seq::exit_ok.value);
- EXPECT_LT (s.clock(), (clock_t)1000);
- EXPECT_EQ (handler_flag, true);
-
- const Seq::script_t<2> script2 = {{
- {Seq::control_t::SEND, "cmd1", Seq::nil, Seq::nil, Seq::next, 0},
- {Seq::control_t::EXPECT, "reply1", check_match, set_if_rpl2, Seq::exit_ok, 1000},
- }};
- handler_flag = false;
- EXPECT_EQ (s.run(script2), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, false);
-
- const Seq::script_t<2> script3 = {{
- {Seq::control_t::SEND, "cmd2\n", Seq::nil, Seq::nil, Seq::next, 0},
- {Seq::control_t::EXPECT, "reply2\n", check_match, set_if_rpl2, Seq::exit_ok, 1000},
- }};
- handler_flag = false;
- EXPECT_EQ (s.run(script3), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, true);
-
- const Seq::script_t<1> script4 = {{
- {Seq::control_t::SEND, "cmd1", Seq::nil, handler, Seq::exit_ok, 0}
- }};
- handler_flag = false;
- EXPECT_EQ (s.run(script4), Seq::exit_ok.value);
- EXPECT_EQ (handler_flag, true);
- }
-
- TEST(Tsequencer, run_boundaries) {
- Seq s;
-
- const Seq::script_t<1> script1 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::next, 0},
- }};
- EXPECT_EQ (s.run(script1), Seq::exit_error.value);
-
- const Seq::script_t<1> script2 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<1>, 0},
- }};
- EXPECT_EQ (s.run(script2), Seq::exit_error.value);
-
- const Seq::script_t<1> script3 = {{
- {Seq::control_t::NOP, "", Seq::nil, Seq::nil, Seq::go_to<(size_t)-1>, 0},
- }};
- EXPECT_EQ (s.run(script3), Seq::exit_error.value);
-
- const Seq::script_t<1> script4 = {{
- {Seq::control_t::EXPECT, "abc", Seq::nil, Seq::nil, Seq::next, 1000},
- }};
- s.clear_clock();
- EXPECT_EQ (s.run(script4), Seq::exit_error.value);
- EXPECT_GT (s.clock(), (clock_t)1000);
- }
- }
|