diff --git a/include/drv/BG95_base.h b/include/drv/BG95_base.h index 9e9e9cd..fdf8030 100644 --- a/include/drv/BG95_base.h +++ b/include/drv/BG95_base.h @@ -250,23 +250,58 @@ class BG95_base // cmd: "AT+CREG?" // expected: "\r\n+CREG: 0,%\r\n\r\nOK\r\n" + + /*! + * \brief + * Send a command to modem and check if the response matches to + * \c expected. If so read any token inside response marked with + * \c Marker, convert the value into type \c T and write it to \c t + * + * \param cmd The comand to send (null terminated) + * \param expected The expected response + * \param t The value to return + * \param timeout The timeout in CPU time (leave it for 0 - no timeout) + * + * \tparam T The type of the value to read from response marked with \c Marker + * \tparam Marker The marker character + * \return True on success + * + * example + * \code + * BG95<256> modem; + * std::thread th1 ([&](){ + * modem.inetd(false); + * }); + * int status; + * bool done = modem.command("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n\r\nOK\r\n", status); + * if (done && status == 1) + * std::cout << "Connected to home network\n" + * \endcode + */ template - bool command (const char* cmd, const str_view_t expected, T& t) { + bool command (const char* cmd, const str_view_t expected, T& t, clock_t timeout =0) { char buffer[N]; transmit(cmd); for (size_t pos =0, i=0, j=0 ; pos < expected.size(); pos += j) { str_view_t ex = expected.substr(pos); // get starting point of expected - size_t sz = receive(buffer, 1); // load the answer + size_t sz =0; + clock_t mark = clock(); + do { + sz = receive(buffer); // load the answer with timeout + if ((timeout != 0 )&& ((clock() - mark) >= timeout)) { + return false; + } + } while (!sz); for (i=0, j=0 ; i rx_q{}; - std::atomic streams_{}; + equeue rx_q{}; + std::atomic streams_{}; }; } // namespace tbx; diff --git a/test/Makefile b/test/Makefile index 396620a..157780b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -221,6 +221,19 @@ build-clang: $(BUILD_DIR)/$(TARGET) .PHONY: debug debug: $(BUILD_DIR)/$(TARGET) + +.PHONY: test_asan +test_asan: CFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds-strict +test_asan: LDFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds-strict +test_asan: $(BUILD_DIR)/$(TARGET) + + +.PHONY: test_tsan +test_tsan: CFLAGS += -fsanitize=thread +test_tsan: LDFLAGS += -fsanitize=thread +test_tsan: $(BUILD_DIR)/$(TARGET) + + .PHONY: release release: CFLAGS := $(REL_CFLAGS) release: $(BUILD_DIR)/$(TARGET) diff --git a/test/main.cpp b/test/main.cpp index 0c399ad..fc6a0d0 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -28,185 +28,6 @@ #include #include -//#include -//#include -//#include -////#include -// -////#include -//#include -//#include -// -//#ifndef WIN_TRHEADS -//#include -//#include -//#else -//#include -//#include -//#endif -// -//using namespace tbx; -// -//using Q = equeue; -// -//// BG95 implementer mock -//class BG95 : public BG95_base { -// using base_type = BG95_base; -// -// public: -// enum class event { -// MQTT_DISCONNECT, MQTT_RXDATA -// }; -// // simulated modem operation -// private: -// struct cmd_pair { -// const char *cmd; -// const char *resp; -// }; -// struct event_pair { -// event e; -// const char* resp; -// }; -// -// std::array cmd_map = {{ -// {"ERROR", "\r\nERROR\r\n"}, -// {"ATE0\r\n", "\r\nATE0\r\nOK\r\n"}, -// {"AT\r\n", "\r\nOK\r\n"}, -// {"AT+QCFG=\"nwscanseq\"\r\n", "\r\n+QCFG: \"nwscanseq\",020301\r\n"}, -// {"AT+QCFG=\"nwscanseq\",010302\r\n", "\r\nOK\r\n"}, -// {"AT+CREG?\r\n", "\r\n+CREG: 0,5\r\n\r\nOK\r\n"}, -// {"AT+CSQ\r\n", "\r\n+CSQ: 19,99\r\n\r\nOK\r\n"}, -// {"AT+QNWINFO\r\n", "\r\n+QNWINFO: \"EDGE\",\"20201\",\"GSM 1800\",865\r\n\r\nOK\r\n"}, -// // Files -// {"AT+QFLST\r\n", "\r\n+QFLST: \"cacert.pem\",1220\r\n+QFLST: \"security/\",2\r\nOK\r\n"}, -// // MQTT config -// {"AT+QSSLCFG=\"ignorelocaltime\",2,1\r\n", "\r\nOK\r\n"}, -// {"AT+QSSLCFG=\"seclevel\",2,1\r\n", "\r\nOK\r\n"}, -// {"AT+QSSLCFG=\"sslversion\",2,4\r\n", "\r\nOK\r\n"}, -// {"AT+QSSLCFG=\"ciphersuite\",2\r\n", "\r\n+QSSLCFG: \"ciphersuite\",2,0XFFFF\r\n\r\nOK\r\n"}, -// {"AT+QMTCFG=\"ssl\",0,1,2\r\n", "\r\nOK\r\n"}, -// {"AT+QMTCFG=\"keepalive\",0,3600\r\n", "\r\nOK\r\n"}, -// // MQTT -// {"AT+QMTOPEN=0,\"server.com\",8883\r\n", "\r\nOK\r\n\r\n+QMTOPEN: 0,0\r\n"}, -// {"AT+QMTCONN=0,\"myID\",\"user\",\"pass\"\r\n", "\r\nOK\r\n\r\n+QMTCONN: 0,0,0\r\n"}, -// {"AT+QMTSUB=0,1,\"/path/topic1\",2\r\n", "\r\nOK\r\n\r\n+QMTSUB: 0,1,0,2\r\n"}, -// {"AT+QMTPUB=0,0,0,0,\"/path/topic2\",9\r\n", "\r\n> \r\nOK\r\n\r\n+QMTPUB: 0,0,0\r\n"}, -// }}; -// std::array event_map {{ -// {event::MQTT_DISCONNECT, "\r\n+QMTSTAT: 0,1\r\n"}, -// {event::MQTT_RXDATA, "\r\n+QMTRECV: 0,1,\"/path/topic1\",\"BR: hello to all of my subscribers\""} -// }}; -// const char* cmd_responce (const char* cmd) { -// for (auto& it : cmd_map) { -// if (!strcmp(it.cmd, cmd)) -// return it.resp; -// } -// return cmd_map[0].resp; -// } -// const char* event_responce (const event e) { -// for (auto& it : event_map) { -// if (e == it.e) -// return it.resp; -// } -// return nullptr; // non reachable -// } -// -// // data -// Q rx_q{}; -// std::atomic lines{}; -// public: -// using range_t = typename Q::range_t; -// -// public: -// // BG95_base driver requirements -// BG95() : -// rx_q(Q::data_match::MATCH_PUSH, base_type::delimiter, [&](){ -// lines.fetch_add(1, std::memory_order_acq_rel); -// }), lines(0) { } -// -// size_t get(char* data, bool wait =false) { -// do { -// if (lines.load(std::memory_order_acquire)) { -// size_t n =0; -// do{ -// *data << rx_q; -// ++n; -// } while (*data++ != base_type::delimiter); -// lines.fetch_sub(1, std::memory_order_acq_rel); -// return n; -// } -// } while (wait); -// return 0; -// } -// size_t put (const char* data, size_t n) { -// const char* reply = cmd_responce (data); -// while (*reply) -// rx_q << *reply++; -// return n; -// } -// const range_t contents() const { -// return range_t {rx_q.begin(), rx_q.end()}; -// } -// clock_t clock() { static clock_t t=0; return ++t; } -// -// // extra helper for testing purposes -// void async (event e) { -// const char* reply =event_responce (e); -// while (*reply) -// rx_q << *reply++; -// } -//}; -// -//// Behavior flag -//bool handler_flag = false; -//void handler (const char* data, size_t n) { -// (void)*data; -// (void)n; -//// std::cout << "* handler called\n"; -// handler_flag = true; -//} -//void clear_flag () { -// handler_flag = false; -//} -// -//int main(int argc, char **argv) try { -// BG95 modem; -// -// const BG95::async_handlers<2> async = {{ -// {"+QMTOPEN:", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, -// {"+QMT", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, -// }}; -// const BG95::script_t<5, 2> script = {{ -// /* 0 */{BG95::control_t::NOP, {"", BG95::match_t::NO, nullptr, BG95::action_t::GOTO, 1}, 1000}, -// /* 1 */{BG95::control_t::SEND, {"ATE0\r\n", BG95::match_t::NO, nullptr, BG95::action_t::NEXT, 0}, 0}, -// /* 2 */{BG95::control_t::EXPECT, {{ -// {"OK\r\n", BG95::match_t::ENDS_WITH, nullptr, BG95::action_t::NEXT, 0}, -// {"ERROR", BG95::match_t::CONTAINS, nullptr, BG95::action_t::EXIT_ERROR, 0} }}, -// 1000 -// }, -// /* 3 */{BG95::control_t::SEND, {"AT+CSQ\r\n", BG95::match_t::NO, nullptr, BG95::action_t::NEXT, 0}, 0}, -// /* 4 */{BG95::control_t::EXPECT, {{ -// {"OK\r\n", BG95::match_t::ENDS_WITH, nullptr, BG95::action_t::EXIT_OK, 0}, -// {"ERROR", BG95::match_t::CONTAINS, nullptr, BG95::action_t::EXIT_ERROR, 0} }}, -// 1000 -// }, -// }}; -// -// std::atomic lock(true); -// std::thread th1 ([&](){ -// do -// modem.inetd(async, false); -// while (lock.load(std::memory_order_acquire)); -// }); -// EXPECT_EQ (modem.run(script), true); -// lock.store(false, std::memory_order_acq_rel); -// th1.join(); -// -//} -//catch (std::exception& e) { -// std::cout << "Exception: " << e.what() << '\n'; -//} - GTEST_API_ int main(int argc, char **argv) try { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/tests/BG95_base.cpp b/test/tests/BG95_base.cpp index b1c0e97..40689a1 100644 --- a/test/tests/BG95_base.cpp +++ b/test/tests/BG95_base.cpp @@ -46,11 +46,14 @@ namespace test_bg95_base { using namespace tbx; - using Q = equeue; + // BG95 implementer mock - class BG95 : public BG95_base { - using base_type = BG95_base; + template + class BG95 : public BG95_base, equeue, N> { + + using Q = equeue; + using base_type = BG95_base, Q, N>; public: enum class event { @@ -172,18 +175,18 @@ namespace test_bg95_base { * Test inetd in non blocking mode */ TEST(TBG95_base, inetd_non_blocking) { - BG95 modem; + BG95<256> modem; char buffer[256]; - const BG95::async_handlers<2> async = {{ - {"+QMTOPEN:", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, - {"+QMT", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, + const BG95<256>::async_handlers<2> async = {{ + {"+QMTOPEN:", BG95<256>::match_t::STARTS_WITH, handler, BG95<256>::action_t::NO, 0}, + {"+QMT", BG95<256>::match_t::STARTS_WITH, handler, BG95<256>::action_t::NO, 0}, }}; clear_flag(); modem.inetd(false, &async); EXPECT_EQ (handler_flag, false); - modem.async(BG95::event::MQTT_DISCONNECT); + modem.async(BG95<256>::event::MQTT_DISCONNECT); modem.inetd(false, &async); // parse "\r\n" EXPECT_EQ (handler_flag, false); modem.inetd(false, &async); // parse "+QMT*\r\n" and dispatch to handler() @@ -220,47 +223,51 @@ namespace test_bg95_base { } TEST(TBG95_base, run) { - BG95 modem; + BG95<256> modem; - const BG95::async_handlers<2> async = {{ - {"+QMTOPEN:", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, - {"+QMT", BG95::match_t::STARTS_WITH, handler, BG95::action_t::NO, 0}, + const BG95<256>::async_handlers<2> async = {{ + {"+QMTOPEN:", BG95<256>::match_t::STARTS_WITH, handler, BG95<256>::action_t::NO, 0}, + {"+QMT", BG95<256>::match_t::STARTS_WITH, handler, BG95<256>::action_t::NO, 0}, }}; - const BG95::script_t<5> script = {{ - /* 0 */{BG95::control_t::NOP, {"", BG95::match_t::NO, nullptr, BG95::action_t::GOTO, 1}, 1000}, - /* 1 */{BG95::control_t::SEND, {"ATE0\r\n", BG95::match_t::NO, nullptr, BG95::action_t::NEXT, 0}, 0}, - /* 2 */{BG95::control_t::EXPECT, {{ - {"OK\r\n", BG95::match_t::ENDS_WITH, nullptr, BG95::action_t::NEXT, 0}, - {"ERROR", BG95::match_t::CONTAINS, nullptr, BG95::action_t::EXIT_ERROR, 0} }}, + const BG95<256>::script_t<5> script = {{ + /* 0 */{BG95<256>::control_t::NOP, {"", BG95<256>::match_t::NO, nullptr, BG95<256>::action_t::GOTO, 1}, 1000}, + /* 1 */{BG95<256>::control_t::SEND, {"ATE0\r\n", BG95<256>::match_t::NO, nullptr, BG95<256>::action_t::NEXT, 0}, 0}, + /* 2 */{BG95<256>::control_t::EXPECT, {{ + {"OK\r\n", BG95<256>::match_t::ENDS_WITH, nullptr, BG95<256>::action_t::NEXT, 0}, + {"ERROR", BG95<256>::match_t::CONTAINS, nullptr, BG95<256>::action_t::EXIT_ERROR, 0} }}, 1000 }, - /* 3 */{BG95::control_t::SEND, {"AT+CSQ\r\n", BG95::match_t::NO, nullptr, BG95::action_t::NEXT, 0}, 0}, - /* 4 */{BG95::control_t::EXPECT, {{ - {"OK\r\n", BG95::match_t::ENDS_WITH, nullptr, BG95::action_t::EXIT_OK, 0}, - {"ERROR", BG95::match_t::CONTAINS, nullptr, BG95::action_t::EXIT_ERROR, 0} }}, + /* 3 */{BG95<256>::control_t::SEND, {"AT+CSQ\r\n", BG95<256>::match_t::NO, nullptr, BG95<256>::action_t::NEXT, 0}, 0}, + /* 4 */{BG95<256>::control_t::EXPECT, {{ + {"OK\r\n", BG95<256>::match_t::ENDS_WITH, nullptr, BG95<256>::action_t::EXIT_OK, 0}, + {"ERROR", BG95<256>::match_t::CONTAINS, nullptr, BG95<256>::action_t::EXIT_ERROR, 0} }}, 1000 }, }}; - std::atomic lock(true); + std::mutex m; + m.lock(); std::thread th1 ([&](){ do modem.inetd(false, &async); - while (lock.load(std::memory_order_acquire)); + while (!m.try_lock()); + m.unlock(); }); EXPECT_EQ (modem.run(script), true); - lock.store(false, std::memory_order_acq_rel); + m.unlock(); // stop and join inetd th1.join(); } TEST(TBG95_base, command) { - BG95 modem; + BG95<256> modem; - std::atomic lock(true); + std::mutex m; + m.lock(); std::thread th1 ([&](){ do modem.inetd(false); - while (lock.load(std::memory_order_acquire)); + while (!m.try_lock()); + m.unlock(); }); int status; EXPECT_EQ (modem.command("AT+CREG?\r\n", "\r\n+CREG: 0,%\r\n\r\nOK\r\n", status), true); @@ -270,7 +277,7 @@ namespace test_bg95_base { EXPECT_EQ (modem.command("AT+CREG?\r\n", "\r\n%\r\n\r\nOK\r\n", substr), true); EXPECT_EQ (strcmp("+CREG: 0,5", substr), 0); - lock.store(false, std::memory_order_acq_rel); // stop and join inetd + m.unlock(); // stop and join inetd th1.join(); }