From bca998f8792a52b4286bbed1d247572c326af9fe Mon Sep 17 00:00:00 2001 From: Zhennan Tu Date: Sun, 5 Apr 2026 23:48:37 +0800 Subject: [PATCH 1/3] add tests for ltlib --- src/ltlib/CMakeLists.txt | 33 +++++++++ src/ltlib/reconnect_interval_tests.cpp | 39 +++++++++++ src/ltlib/settings_tests.cpp | 94 ++++++++++++++++++++++++++ src/ltlib/strings_tests.cpp | 57 ++++++++++++++++ src/ltlib/transform_tests.cpp | 59 ++++++++++++++++ 5 files changed, 282 insertions(+) create mode 100644 src/ltlib/reconnect_interval_tests.cpp create mode 100644 src/ltlib/strings_tests.cpp create mode 100644 src/ltlib/transform_tests.cpp diff --git a/src/ltlib/CMakeLists.txt b/src/ltlib/CMakeLists.txt index 08ec2843..3242c9b3 100644 --- a/src/ltlib/CMakeLists.txt +++ b/src/ltlib/CMakeLists.txt @@ -123,6 +123,39 @@ if (LT_ENABLE_TEST AND BUILD_TESTING) set(LT_MODULE_LTLIB_TEST_PLAT_LIBS) endif() + add_executable(test_reconnect_interval + ${CMAKE_CURRENT_SOURCE_DIR}/reconnect_interval_tests.cpp + ) + target_link_libraries(test_reconnect_interval + GTest::gtest + GTest::gtest_main + lt_module_ltlib + ${LT_MODULE_LTLIB_TEST_PLAT_LIBS} + ) + add_test(NAME test_reconnect_interval COMMAND test_reconnect_interval) + + add_executable(test_transform + ${CMAKE_CURRENT_SOURCE_DIR}/transform_tests.cpp + ) + target_link_libraries(test_transform + GTest::gtest + GTest::gtest_main + lt_module_ltlib + ${LT_MODULE_LTLIB_TEST_PLAT_LIBS} + ) + add_test(NAME test_transform COMMAND test_transform) + + add_executable(test_strings + ${CMAKE_CURRENT_SOURCE_DIR}/strings_tests.cpp + ) + target_link_libraries(test_strings + GTest::gtest + GTest::gtest_main + lt_module_ltlib + ${LT_MODULE_LTLIB_TEST_PLAT_LIBS} + ) + add_test(NAME test_strings COMMAND test_strings) + add_executable(test_settings ${CMAKE_CURRENT_SOURCE_DIR}/settings_tests.cpp ) diff --git a/src/ltlib/reconnect_interval_tests.cpp b/src/ltlib/reconnect_interval_tests.cpp new file mode 100644 index 00000000..c9fc5a18 --- /dev/null +++ b/src/ltlib/reconnect_interval_tests.cpp @@ -0,0 +1,39 @@ +#include + +#include + +#include + +namespace { + +TEST(ReconnectIntervalTest, FirstValueIsInitialBackoff) { + ltlib::ReconnectInterval reconnect; + EXPECT_EQ(reconnect.next(), 100); +} + +TEST(ReconnectIntervalTest, SequenceSaturatesAtUpperBound) { + ltlib::ReconnectInterval reconnect; + constexpr std::array kExpected{100, 500, 1000, 2000, 5000, 10000, 30000, 60000}; + + for (size_t i = 0; i < kExpected.size(); ++i) { + EXPECT_EQ(reconnect.next(), kExpected[i]); + } + + for (int i = 0; i < 16; ++i) { + EXPECT_EQ(reconnect.next(), 60000); + } +} + +TEST(ReconnectIntervalTest, ResetRestartsFromFirstValue) { + ltlib::ReconnectInterval reconnect; + (void)reconnect.next(); + (void)reconnect.next(); + (void)reconnect.next(); + + reconnect.reset(); + + EXPECT_EQ(reconnect.next(), 100); + EXPECT_EQ(reconnect.next(), 500); +} + +} // namespace \ No newline at end of file diff --git a/src/ltlib/settings_tests.cpp b/src/ltlib/settings_tests.cpp index b4b3d2ea..79619b98 100644 --- a/src/ltlib/settings_tests.cpp +++ b/src/ltlib/settings_tests.cpp @@ -2,6 +2,13 @@ #include #include +#include +#include +#include +#include +#include +#include + static const char* DBName = "SettingsSqlite.db"; class SettingsSqliteTest : public testing::Test { @@ -68,4 +75,91 @@ TEST_F(SettingsSqliteTest, UpdateTime) { auto updated_at = settings_->getUpdateTime("int_key"); ASSERT_TRUE(updated_at.has_value()); EXPECT_TRUE(updated_at.value() >= now - 1 && updated_at.value() <= now + 1); +} + +TEST_F(SettingsSqliteTest, KeysStartWithAndDeleteKey) { + settings_->setInteger("pref.alpha", 1); + settings_->setInteger("pref.beta", 2); + settings_->setInteger("other.gamma", 3); + + auto keys = settings_->getKeysStartWith("pref."); + std::sort(keys.begin(), keys.end()); + ASSERT_EQ(keys.size(), 2U); + EXPECT_EQ(keys[0], "pref.alpha"); + EXPECT_EQ(keys[1], "pref.beta"); + + settings_->deleteKey("pref.alpha"); + EXPECT_EQ(settings_->getInteger("pref.alpha"), std::nullopt); + EXPECT_EQ(settings_->getInteger("pref.beta"), 2); +} + +TEST_F(SettingsSqliteTest, EmptyKeyIsIgnored) { + settings_->setInteger("", 123); + settings_->setBoolean("", true); + settings_->setString("", "value"); + + EXPECT_EQ(settings_->getInteger(""), std::nullopt); + EXPECT_EQ(settings_->getBoolean(""), std::nullopt); + EXPECT_EQ(settings_->getString(""), std::nullopt); + EXPECT_TRUE(settings_->getKeysStartWith("").empty()); +} + +TEST_F(SettingsSqliteTest, MismatchedValueTypeReturnsNullopt) { + settings_->setString("mixed", "text"); + settings_->setInteger("int_only", 42); + + EXPECT_EQ(settings_->getInteger("mixed"), std::nullopt); + EXPECT_EQ(settings_->getBoolean("int_only"), std::nullopt); +} + +TEST_F(SettingsSqliteTest, ConcurrentWritesRemainReadable) { + constexpr int kThreadCount = 8; + constexpr int kLoopCount = 100; + + std::vector workers; + workers.reserve(kThreadCount); + + for (int tid = 0; tid < kThreadCount; ++tid) { + workers.emplace_back([this, tid]() { + const std::string key = "concurrent." + std::to_string(tid); + for (int i = 0; i < kLoopCount; ++i) { + settings_->setInteger(key, tid * 1000 + i); + } + }); + } + + for (auto& worker : workers) { + worker.join(); + } + + for (int tid = 0; tid < kThreadCount; ++tid) { + const std::string key = "concurrent." + std::to_string(tid); + const auto value = settings_->getInteger(key); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(value.value(), tid * 1000 + (kLoopCount - 1)); + } +} + +TEST(SettingsSqliteStandaloneTest, CorruptedFileNeedsRecreateToRecover) { + static const char* kCorruptedDbName = "SettingsSqliteCorrupted.db"; + + { + std::ofstream out(kCorruptedDbName, std::ios::binary | std::ios::trunc); + ASSERT_TRUE(out.is_open()); + out << "not a sqlite database"; + } + + auto broken = ltlib::Settings::createWithPathForTest(ltlib::Settings::Storage::Sqlite, + kCorruptedDbName); + EXPECT_EQ(broken, nullptr); + + std::remove(kCorruptedDbName); + + auto recovered = ltlib::Settings::createWithPathForTest(ltlib::Settings::Storage::Sqlite, + kCorruptedDbName); + ASSERT_NE(recovered, nullptr); + recovered->setString("ok", "1"); + EXPECT_EQ(recovered->getString("ok"), "1"); + + std::remove(kCorruptedDbName); } \ No newline at end of file diff --git a/src/ltlib/strings_tests.cpp b/src/ltlib/strings_tests.cpp new file mode 100644 index 00000000..e6fa910b --- /dev/null +++ b/src/ltlib/strings_tests.cpp @@ -0,0 +1,57 @@ +#include +#include + +#include + +#include + +namespace { + +TEST(StringsTest, Utf8AndUtf16RoundTripAsciiCjkAndEmoji) { + const std::array cases{ + "", + "hello, world", + "\xE4\xBD\xA0\xE5\xA5\xBD", + "\xF0\x9F\x99\x82", + }; + + for (const auto& utf8 : cases) { + const std::wstring utf16 = ltlib::utf8To16(utf8); + EXPECT_EQ(ltlib::utf16To8(utf16), utf8); + } +} + +TEST(StringsTest, Utf16AndUtf8RoundTripAsciiCjkAndEmoji) { + const std::array cases{ + L"", + L"hello", + std::wstring{0x4F60, 0x597D}, + std::wstring{0xD83D, 0xDE42}, + }; + + for (const auto& utf16 : cases) { + const std::string utf8 = ltlib::utf16To8(utf16); + EXPECT_EQ(ltlib::utf8To16(utf8), utf16); + } +} + +TEST(StringsTest, Base64EncodeDecodeRoundTrip) { + const std::string raw{"abc\0xyz", 7}; + + const std::string encoded = ltlib::base64Encode(raw); + EXPECT_EQ(encoded, "YWJjAHh5eg=="); + EXPECT_EQ(ltlib::base64Decode(encoded), raw); +} + +TEST(StringsTest, Base64DecodeStopsAtInvalidCharacter) { + const std::string withInvalid = "aGVs#bG8="; + + EXPECT_EQ(ltlib::base64Decode(withInvalid), ltlib::base64Decode("aGVs")); +} + +TEST(StringsTest, Base64HandlesEmptyInput) { + EXPECT_TRUE(ltlib::base64Encode("").empty()); + EXPECT_TRUE(ltlib::base64Decode("").empty()); +} + +} // namespace \ No newline at end of file diff --git a/src/ltlib/transform_tests.cpp b/src/ltlib/transform_tests.cpp new file mode 100644 index 00000000..5c9a11c8 --- /dev/null +++ b/src/ltlib/transform_tests.cpp @@ -0,0 +1,59 @@ +#include + +#include + +namespace { + +void expectRectEq(const ltlib::Rect& actual, const ltlib::Rect& expected) { + EXPECT_EQ(actual.x, expected.x); + EXPECT_EQ(actual.y, expected.y); + EXPECT_EQ(actual.w, expected.w); + EXPECT_EQ(actual.h, expected.h); +} + +TEST(TransformTest, FitFourByThreeIntoSixteenByNineContainer) { + const ltlib::Rect outer{0, 0, 1920, 1080}; + const ltlib::Rect innerOriginal{0, 0, 1024, 768}; + + const ltlib::Rect actual = ltlib::calcMaxInnerRect(outer, innerOriginal); + + expectRectEq(actual, ltlib::Rect{240, 0, 1440, 1080}); +} + +TEST(TransformTest, FitSixteenByNineIntoFourByThreeContainer) { + const ltlib::Rect outer{0, 0, 1024, 768}; + const ltlib::Rect innerOriginal{0, 0, 1920, 1080}; + + const ltlib::Rect actual = ltlib::calcMaxInnerRect(outer, innerOriginal); + + expectRectEq(actual, ltlib::Rect{0, 96, 1024, 576}); +} + +TEST(TransformTest, FitLandscapeIntoPortraitContainer) { + const ltlib::Rect outer{0, 0, 1080, 1920}; + const ltlib::Rect innerOriginal{0, 0, 1920, 1080}; + + const ltlib::Rect actual = ltlib::calcMaxInnerRect(outer, innerOriginal); + + expectRectEq(actual, ltlib::Rect{0, 656, 1080, 607}); +} + +TEST(TransformTest, HandlesZeroOuterWidthBoundary) { + const ltlib::Rect outer{0, 0, 0, 1080}; + const ltlib::Rect innerOriginal{0, 0, 1920, 1080}; + + const ltlib::Rect actual = ltlib::calcMaxInnerRect(outer, innerOriginal); + + expectRectEq(actual, ltlib::Rect{0, 540, 0, 0}); +} + +TEST(TransformTest, HandlesZeroInnerWidthBoundary) { + const ltlib::Rect outer{0, 0, 1920, 1080}; + const ltlib::Rect innerOriginal{0, 0, 0, 1080}; + + const ltlib::Rect actual = ltlib::calcMaxInnerRect(outer, innerOriginal); + + expectRectEq(actual, ltlib::Rect{960, 0, 0, 1080}); +} + +} // namespace \ No newline at end of file From c539f5f40cccb0f5070ceeeea3d04088729aa158 Mon Sep 17 00:00:00 2001 From: Zhennan Tu Date: Mon, 6 Apr 2026 00:29:52 +0800 Subject: [PATCH 2/3] add tests for inputs/audio --- src/audio/CMakeLists.txt | 15 +++ .../capturer/fake_audio_capturer_tests.cpp | 31 ++++++ src/inputs/CMakeLists.txt | 12 +++ src/inputs/capturer/input_event_tests.cpp | 99 +++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 src/audio/capturer/fake_audio_capturer_tests.cpp create mode 100644 src/inputs/capturer/input_event_tests.cpp diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index ec689bb6..62d634d1 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -40,3 +40,18 @@ target_link_libraries(lt_module_audio ) set_code_analysis(lt_module_audio ${LT_ENABLE_CODE_ANALYSIS}) + +if (LT_ENABLE_TEST AND BUILD_TESTING) + add_executable(test_fake_audio_capturer + ${CMAKE_CURRENT_SOURCE_DIR}/capturer/fake_audio_capturer_tests.cpp + ) + target_link_libraries(test_fake_audio_capturer + GTest::gtest + GTest::gtest_main + protobuf::libprotobuf-lite + transport_api + lt_module_ltlib + lt_module_audio + ) + add_test(NAME test_fake_audio_capturer COMMAND test_fake_audio_capturer) +endif() diff --git a/src/audio/capturer/fake_audio_capturer_tests.cpp b/src/audio/capturer/fake_audio_capturer_tests.cpp new file mode 100644 index 00000000..43909720 --- /dev/null +++ b/src/audio/capturer/fake_audio_capturer_tests.cpp @@ -0,0 +1,31 @@ +#include + +#include