diff --git a/UnitTest/CMakeLists.txt b/UnitTest/CMakeLists.txt index b2142fb..dad5ee9 100644 --- a/UnitTest/CMakeLists.txt +++ b/UnitTest/CMakeLists.txt @@ -31,6 +31,9 @@ add_executable(UnitTest main.cpp HttpProxy/BoostUrlTest.cpp Universal/BoostLogTest.cpp + Universal/DateTimeTest.cpp + Universal/MessageManagerTest.cpp + Universal/SingletonTest.cpp ) target_compile_definitions(UnitTest diff --git a/UnitTest/Universal/DateTimeTest.cpp b/UnitTest/Universal/DateTimeTest.cpp new file mode 100644 index 0000000..339e6b9 --- /dev/null +++ b/UnitTest/Universal/DateTimeTest.cpp @@ -0,0 +1,83 @@ +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(DateTimeTest, *boost::unit_test::enabled()) + +BOOST_AUTO_TEST_CASE(BasicUse) { + std::time_t c_current = std::time(nullptr); + auto c_local = localtime(&c_current); + + auto now = DateTime::currentDateTime(); + BOOST_CHECK_EQUAL(now.year(), c_local->tm_year + 1900); + BOOST_CHECK_EQUAL(now.month(), c_local->tm_mon + 1); + BOOST_CHECK_EQUAL(now.day(), c_local->tm_mday); + BOOST_CHECK_EQUAL(now.hour(), c_local->tm_hour); + BOOST_CHECK_EQUAL(now.minute(), c_local->tm_min); + BOOST_CHECK_EQUAL(now.second(), c_local->tm_sec); + + // std::cout << "now: " << DateTime::toString(std::chrono::system_clock::now()) << std::endl; + // std::cout << "now: " << DateTime::currentDateTime().toString() << std::endl; + + auto day = DateTime::makeDateTime(2018, 8, 15, 13, 14, 59); + DateTime r(day); + BOOST_CHECK_EQUAL(r.week(), DateTime::Wednesday); + BOOST_CHECK_EQUAL(DateTime::toString(day), "2018-08-15 13:14:59"); +} + +BOOST_AUTO_TEST_CASE(IsLeapYearTest) { + BOOST_TEST(DateTime::isLeapYear(2008)); + BOOST_TEST(!DateTime::isLeapYear(2100)); +} + +BOOST_AUTO_TEST_CASE(TomorrowTest) { + DateTime day(2018, 8, 15, 13, 14, 59); + auto t = day.tomorrow(); + BOOST_CHECK_EQUAL(t.toString(), "2018-08-16 13:14:59"); + + day = DateTime(2018, 12, 31, 13, 14, 59); + t = day.tomorrow(); + BOOST_CHECK_EQUAL(t.toString(), "2019-01-01 13:14:59"); + + day = DateTime(2020, 2, 28, 13, 14, 59); + t = day.tomorrow(); + BOOST_CHECK_EQUAL(t.toString(), "2020-02-29 13:14:59"); + + day = DateTime(2021, 2, 28, 13, 14, 59); + t = day.tomorrow(); + BOOST_CHECK_EQUAL(t.toString(), "2021-03-01 13:14:59"); + + day = DateTime(2021, 10, 25, 22, 31, 59); + t = day.tomorrow(); + BOOST_CHECK_EQUAL(t.toString(), "2021-10-26 22:31:59"); +} + +BOOST_AUTO_TEST_CASE(CurrentMSecsSinceEpoch) { + BOOST_CHECK_EQUAL(DateTime::currentMSecsSinceEpoch(), + std::chrono::time_point_cast(std::chrono::system_clock::now()) + .time_since_epoch() + .count()); +} + +BOOST_AUTO_TEST_CASE(ParseTimeString) { + auto [hour, minute, second] = DateTime::parseTime("12:14:15"); + BOOST_CHECK_EQUAL(hour, 12); + BOOST_CHECK_EQUAL(minute, 14); + BOOST_CHECK_EQUAL(second, 15); + + std::tie(hour, minute, second) = DateTime::parseTime("15:12"); + BOOST_CHECK_EQUAL(hour, 15); + BOOST_CHECK_EQUAL(minute, 12); + BOOST_CHECK_EQUAL(second, 0); + + std::tie(hour, minute, second) = DateTime::parseTime("00:09:07"); + BOOST_CHECK_EQUAL(hour, 0); + BOOST_CHECK_EQUAL(minute, 9); + BOOST_CHECK_EQUAL(second, 7); + + std::tie(hour, minute, second) = DateTime::parseTime("15:12"); + BOOST_CHECK_EQUAL(hour, 15); + BOOST_CHECK_EQUAL(minute, 12); + BOOST_CHECK_EQUAL(second, 0); +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/UnitTest/Universal/MessageManagerTest.cpp b/UnitTest/Universal/MessageManagerTest.cpp new file mode 100644 index 0000000..b32c94c --- /dev/null +++ b/UnitTest/Universal/MessageManagerTest.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include + +static int test(int a, int b) { + return a + b; +} + +class Test { +public: + int test(int a, int b) { + result = a + b + 3; + return result; + } + int result{0}; +}; + +BOOST_AUTO_TEST_SUITE(MessageManagerTest, *boost::unit_test::enabled()) +// returned-function-return-type (* function-name (parameter-list) ) (function-to-return-parameter-list) +BOOST_AUTO_TEST_CASE(FunctionTraitsNormalFunctionTest) { + using namespace std::placeholders; + MessageManager m; + + m.registerTopic("123", [](int a) -> void { a = a + 102; }); + BOOST_CHECK_EQUAL(m.topicCount("123"), 1); + + Test t; + m.registerTopic("123", [&t](int a, int b) { return t.test(a, b); }); + BOOST_CHECK_EQUAL(m.topicCount("123"), 1); + + m.registerTopic("123", std::bind(&Test::test, &t, _1, _2)); + BOOST_CHECK_EQUAL(m.topicCount("123"), 2); + + m.registerTopic("123", &test); + BOOST_CHECK_EQUAL(m.topicCount("123"), 3); + + m.removeTopic("123"); + BOOST_CHECK_EQUAL(m.topicCount("123"), 0); + + m.registerTopic("123", [](const std::string &title) {}); + + m.registerTopic( + "123", [](int type, const std::string &title, const std::string &message, const std::function &yes) {}); +} + +BOOST_AUTO_TEST_CASE(SyncMessage) { + using namespace std::placeholders; + MessageManager manager; + + int result1 = 0; + std::thread::id id; + manager.registerTopic("123", [&result1](int a) { + result1 += a; + return a + 102; + }); + int result2 = 0; + manager.registerTopic("123", [&result2](const int &a) { + result2 = a + 102; + return result2; + }); + manager.sendMessage("123", 5); + BOOST_CHECK_EQUAL(result1, 5); + BOOST_CHECK_EQUAL(result2, 107); + + int result3 = 0; + manager.registerTopic("123", [&result3](int a, int b) { + result3 = a + b; + return 0; + }); + Test t; + manager.registerTopic("123", std::bind(&Test::test, &t, _1, _2)); + manager.sendMessage("123", 12, 13); + BOOST_CHECK_EQUAL(result3, 25); + BOOST_CHECK_EQUAL(t.result, 28); + + int result4 = 0; + manager.registerTopic("123", [&result4]() { + result4 = 102; + return result4; + }); + manager.sendMessage("123"); + BOOST_CHECK_EQUAL(result4, 102); + + constexpr auto magic = "MagicString"; + std::string result5; + manager.registerTopic("test", [&result5](const std::string &text) { result5 = text; }); + // manager.sendMessage("test", magic); // 编译不通过 + + // manager.sendMessage("test", magic); // const char*无法匹配到std::string + // BOOST_CHECK_EQUAL(result5, magic); + // result5.clear(); + + manager.sendMessage("test", magic); + BOOST_CHECK_EQUAL(result5, magic); + result5.clear(); + + manager.sendMessage("test", std::string(magic)); + BOOST_CHECK_EQUAL(result5, magic); + result5.clear(); + + manager.sendMessage("test", magic); + BOOST_CHECK_EQUAL(result5, magic); + result5.clear(); + + manager.sendMessage("test", std::string(magic)); + BOOST_CHECK_EQUAL(result5, magic); +} + +BOOST_AUTO_TEST_CASE(SyncMessageWithVoidArg) { + MessageManager manager; + constexpr auto Topic = MessageManager::Message<>("test1"); + int result = 0; + manager.registerTopic("test1", [&result]() -> void { result = 250; }); + manager.sendMessage("test1"); + BOOST_CHECK_EQUAL(result, 250); + + result = 0; + manager.sendMessage(Topic); + BOOST_CHECK_EQUAL(result, 250); +} + +BOOST_AUTO_TEST_CASE(AsyncMessage) { + int result = 0; + std::thread::id id; + auto test1 = [&result, &id](int a, int b) -> void { + result = a + b; + id = std::this_thread::get_id(); + }; + + MessageManager manager; + manager.registerTopic("test1", test1); + manager.sendAsyncMessage("test1", 2, 6); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + BOOST_CHECK_EQUAL(result, 8); + BOOST_CHECK_NE(std::this_thread::get_id(), id); + + constexpr auto magic = "MagicString"; + manager.registerTopic("StringCompare", [magic](const std::string &text) { BOOST_CHECK_EQUAL(text, magic); }); + { + std::string string(magic); + manager.sendAsyncMessage("StringCompare", string); + } +} + +BOOST_AUTO_TEST_CASE(AsyncMessageImplictConversion) { + float result1 = 0; + auto test1 = [&result1](int a, int b) -> void { result1 = a + b; }; + + float result2 = 0; + auto test2 = [&result2](int a, int b) -> void { result2 = a + b; }; + + MessageManager manager; + + manager.registerTopic("test1", test1); + manager.registerTopic("test2", test2); + manager.sendAsyncMessage("test1", 2.6, 6); + manager.sendAsyncMessage("test2", (int)3.6, 6); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + BOOST_CHECK_EQUAL(result1, 8); + BOOST_CHECK_EQUAL(result2, 9); +} + +BOOST_AUTO_TEST_CASE(DefineTopicUseMessage) { + constexpr auto TestTopic1 = MessageManager::Message("TestTopic1"); + int result1 = 0; + auto test1 = [&result1](int a, int b) -> void { result1 = a + b; }; + MessageManager manager; + manager.registerTopic(TestTopic1, test1); + manager.sendMessage(TestTopic1, 2, 3); + BOOST_CHECK_EQUAL(result1, 5); + + std::string result2; + constexpr auto TestTopic2 = MessageManager::Message("TestTopic2"); + auto test2 = [&result2](const std::string &text) -> void { result2 = text; }; + manager.registerTopic(TestTopic2, test2); + manager.sendMessage(TestTopic2, std::string("hello")); + std::string message("hello"); + // manager.sendMessage(TestTopic2, message); + // manager.sendMessage(TestTopic2, "hello"); + BOOST_CHECK_EQUAL(result2, "hello"); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/UnitTest/Universal/SingletonTest.cpp b/UnitTest/Universal/SingletonTest.cpp new file mode 100644 index 0000000..7d75fc0 --- /dev/null +++ b/UnitTest/Universal/SingletonTest.cpp @@ -0,0 +1,39 @@ +#include "Singleton.h" +#include + +class Test { +public: + size_t index; +}; + +BOOST_AUTO_TEST_CASE(LocalInstance) { + auto test = Amass::Singleton::instance(); + BOOST_TEST(!test); + + { + auto local = Amass::Singleton::instance(); + BOOST_TEST(local); + + auto refer = Amass::Singleton::instance(); + BOOST_TEST(refer); + } + + test = Amass::Singleton::instance(); + BOOST_TEST(!test); +} + +BOOST_AUTO_TEST_CASE(GlobalInstance) { + auto test = Amass::Singleton::instance(); + BOOST_TEST(test); + + { + auto local = Amass::Singleton::instance(); + BOOST_TEST(local); + + auto refer = Amass::Singleton::instance(); + BOOST_TEST(refer); + } + + test = Amass::Singleton::instance(); + BOOST_TEST(test); +} diff --git a/Universal/ApplicationSettings.cpp b/Universal/ApplicationSettings.cpp new file mode 100644 index 0000000..baf6cda --- /dev/null +++ b/Universal/ApplicationSettings.cpp @@ -0,0 +1,36 @@ +#include "ApplicationSettings.h" +#include "BoostLog.h" +#include +#include + +ApplicationSettings::ApplicationSettings(boost::asio::io_context &ioContext, const std::string &path) + : m_ioContext(ioContext), m_timer(ioContext), m_path(path) { + if (std::filesystem::exists(std::filesystem::path(path))) { + try { + boost::property_tree::read_ini(path, m_ptree); + } catch (const boost::property_tree::ini_parser_error &e) { + LOG(error) << e.what(); + } + } + run(); +} + +void ApplicationSettings::run() { + m_timer.expires_after(std::chrono::seconds(1)); + m_timer.async_wait([this](const boost::system::error_code &error) { + if (error) { + LOG(error) << error.message(); + return; + } + if (m_needSave) { + try { + std::lock_guard locker(m_mutex); + boost::property_tree::write_ini(m_path, m_ptree); + } catch (const boost::property_tree::ini_parser_error &e) { + LOG(error) << e.what(); + } + m_needSave = false; + } + run(); + }); +} diff --git a/Universal/ApplicationSettings.h b/Universal/ApplicationSettings.h new file mode 100644 index 0000000..a4b67ee --- /dev/null +++ b/Universal/ApplicationSettings.h @@ -0,0 +1,38 @@ +#ifndef __APPLICATIONSETTINGS_H__ +#define __APPLICATIONSETTINGS_H__ + +#include +#include +#include + +class ApplicationSettings { +#define BUILD_SETTING_FIELD(Category, Type, Name, DefaultValue) \ + inline void set##Name(const Type &value) { \ + std::lock_guard locker(m_mutex); \ + m_ptree.put(#Category "." #Name, value); \ + m_needSave = true; \ + } \ + inline Type get##Name() const { \ + std::lock_guard locker(m_mutex); \ + return m_ptree.get(#Category "." #Name, DefaultValue); \ + } + +#define BUILD_STATUS(Type, Name, DefaultValue) BUILD_SETTING_FIELD(Status, Type, Name, DefaultValue) + +public: + ApplicationSettings(boost::asio::io_context &ioContext, const std::string &path); + +protected: + void run(); + + boost::property_tree::ptree m_ptree; + bool m_needSave = false; + mutable std::mutex m_mutex; + +private: + boost::asio::io_context &m_ioContext; + boost::asio::steady_timer m_timer; + std::string m_path; +}; + +#endif // __APPLICATIONSETTINGS_H__ \ No newline at end of file diff --git a/Universal/CMakeLists.txt b/Universal/CMakeLists.txt index 54034f1..2840ff1 100644 --- a/Universal/CMakeLists.txt +++ b/Universal/CMakeLists.txt @@ -1,6 +1,7 @@ find_package(Boost REQUIRED COMPONENTS log log_setup program_options) add_library(Universal + ApplicationSettings.h ApplicationSettings.cpp BoostLog.h BoostLog.inl BoostLog.cpp BufferUtility.h BufferUtility.cpp DateTime.h DateTime.cpp diff --git a/Universal/DateTime.h b/Universal/DateTime.h index 04b2f0f..4b3d1ca 100644 --- a/Universal/DateTime.h +++ b/Universal/DateTime.h @@ -36,6 +36,11 @@ public: std::chrono::time_point operator()(); std::string toString(const std::string_view &format = "%F %T") const; + /** + * @brief 2023-10-12 12:10:23 then return 2023-10-13 12:10:23 + * + * @return DateTime + */ DateTime tomorrow(); // Returns the number of milliseconds since 1970-01-01T00:00:00 Universal Coordinated Time. This number is like the