diff --git a/tests/coop/CMakeLists.txt b/tests/coop/CMakeLists.txt index a672ec4e8..840a5ea60 100644 --- a/tests/coop/CMakeLists.txt +++ b/tests/coop/CMakeLists.txt @@ -69,7 +69,15 @@ add_executable(coop_tests ${COOP_SRC} ${CMAKE_SOURCE_DIR}/tests/session_core/protoendpoint_test.cpp ${CMAKE_SOURCE_DIR}/tests/session_core/protoclient_server_test.cpp ${CMAKE_SOURCE_DIR}/tests/session_core/cooperationcore_test.cpp - ${CMAKE_SOURCE_DIR}/tests/session_core/discover_extra_test.cpp) + ${CMAKE_SOURCE_DIR}/tests/session_core/discover_extra_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/settings_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/configmanager_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/commonutils_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filesystem_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cooperation_extra_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/phone_smoke_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/discover_safe_test.cpp + ${CMAKE_SOURCE_DIR}/tests/session_core/session_integration_test.cpp) target_compile_options(coop_tests PRIVATE -fno-access-control -fprofile-arcs -ftest-coverage) target_compile_definitions(coop_tests PRIVATE EXECUTABLE_PROG_DIR="${CMAKE_BINARY_DIR}" ENABLE_PHONE=1) target_include_directories(coop_tests PRIVATE @@ -91,5 +99,5 @@ endif() if(VNCCLIENT_LIBRARY) target_link_libraries(coop_tests PRIVATE ${VNCCLIENT_LIBRARY}) endif() -target_link_options(coop_tests PRIVATE -fprofile-arcs -ftest-coverage) +target_link_options(coop_tests PRIVATE -fprofile-arcs -ftest-coverage -Wl,--allow-multiple-definition) add_test(NAME coop_tests COMMAND coop_tests) diff --git a/tests/coop/commonutils_test.cpp b/tests/coop/commonutils_test.cpp new file mode 100644 index 000000000..7b5240fe5 --- /dev/null +++ b/tests/coop/commonutils_test.cpp @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "common/commonutils.h" + +#include +#include +#include +#include + +using deepin_cross::CommonUitls; + +TEST(CommonUitlsTest, GenerateRandomPasswordIsSixDigits) +{ + QString pwd = CommonUitls::generateRandomPassword(); + EXPECT_EQ(pwd.length(), 6); + for (const QChar &c : pwd) { + EXPECT_TRUE(c.isDigit()); + } +} + +TEST(CommonUitlsTest, GenerateRandomPasswordProducesVariedOutput) +{ + QString a = CommonUitls::generateRandomPassword(); + QString b = CommonUitls::generateRandomPassword(); + EXPECT_EQ(a.length(), 6); + EXPECT_EQ(b.length(), 6); +} + +TEST(CommonUitlsTest, ElidedTextShortReturnsUnchanged) +{ + EXPECT_EQ(CommonUitls::elidedText("hi", Qt::ElideRight, 5).toStdString(), "hi"); + EXPECT_EQ(CommonUitls::elidedText("hi", Qt::ElideLeft, 5).toStdString(), "hi"); + EXPECT_EQ(CommonUitls::elidedText("hi", Qt::ElideMiddle, 5).toStdString(), "hi"); + EXPECT_EQ(CommonUitls::elidedText("hi", Qt::ElideNone, 5).toStdString(), "hi"); +} + +TEST(CommonUitlsTest, ElidedTextElideRight) +{ + QString out = CommonUitls::elidedText("abcdefghij", Qt::ElideRight, 5); + EXPECT_EQ(out.length(), 8); + EXPECT_TRUE(out.endsWith("...")); + EXPECT_EQ(out.left(5).toStdString(), "abcde"); +} + +TEST(CommonUitlsTest, ElidedTextElideLeft) +{ + QString out = CommonUitls::elidedText("abcdefghij", Qt::ElideLeft, 5); + EXPECT_EQ(out.length(), 8); + EXPECT_TRUE(out.startsWith("...")); + EXPECT_EQ(out.right(5).toStdString(), "fghij"); +} + +TEST(CommonUitlsTest, ElidedTextElideMiddle) +{ + QString out = CommonUitls::elidedText("abcdefghij", Qt::ElideMiddle, 5); + EXPECT_EQ(out.length(), 5); + EXPECT_TRUE(out.contains("...")); +} + +TEST(CommonUitlsTest, ElidedTextElideNoneReturnsOriginal) +{ + QString out = CommonUitls::elidedText("abcdefghij", Qt::ElideNone, 5); + EXPECT_EQ(out.toStdString(), "abcdefghij"); +} + +TEST(CommonUitlsTest, IsPortInUsePrivilegedPortOne) +{ + EXPECT_FALSE(CommonUitls::isPortInUse(1)); +} + +TEST(CommonUitlsTest, GetAvailablePortInRange) +{ + int port = CommonUitls::getAvailablePort(); + EXPECT_GE(port, 13628); + EXPECT_LT(port, 23628); +} + +TEST(CommonUitlsTest, IpcServerNameContainsAppName) +{ + QString name = CommonUitls::ipcServerName("myapp"); + EXPECT_FALSE(name.isEmpty()); + EXPECT_TRUE(name.contains("myapp")); + EXPECT_TRUE(name.contains(".ipc")); +} + +TEST(CommonUitlsTest, TipConfPathEndsWithFlag) +{ + QString path = CommonUitls::tipConfPath(); + EXPECT_FALSE(path.isEmpty()); + EXPECT_TRUE(path.endsWith("tip.flag")); +} + +TEST(CommonUitlsTest, IsFirstStartFlagTransitions) +{ + QString flagPath = QString("%1/%2/%3/first_run.flag") + .arg(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)) + .arg(qApp->organizationName()) + .arg(qApp->applicationName()); + QFile::remove(flagPath); + + EXPECT_TRUE(CommonUitls::isFirstStart()); + EXPECT_FALSE(CommonUitls::isFirstStart()); +} + +TEST(CommonUitlsTest, GetFirstIpDoesNotCrash) +{ + std::string ip = CommonUitls::getFirstIp(); + EXPECT_NO_FATAL_FAILURE(CommonUitls::getFirstIp()); + if (!ip.empty()) { + EXPECT_GT(ip.length(), static_cast(0)); + } +} diff --git a/tests/coop/configmanager_test.cpp b/tests/coop/configmanager_test.cpp new file mode 100644 index 000000000..236ab5978 --- /dev/null +++ b/tests/coop/configmanager_test.cpp @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "configs/settings/configmanager.h" +#include "configs/settings/settings.h" + +#include + +TEST(CoopConfigManagerTest, InstanceIsSingleton) +{ + EXPECT_EQ(ConfigManager::instance(), ConfigManager::instance()); +} + +TEST(CoopConfigManagerTest, AppSettingReturnsValidSettings) +{ + Settings *s = ConfigManager::instance()->appSetting(); + ASSERT_NE(s, nullptr); + EXPECT_NE(s, nullptr); +} + +TEST(CoopConfigManagerTest, SetAndReadAppAttributeRoundTrip) +{ + ConfigManager::instance()->setAppAttribute("CoopUnitTestGroup", "someKey", 12345); + QVariant v = ConfigManager::instance()->appAttribute("CoopUnitTestGroup", "someKey"); + EXPECT_EQ(v.toInt(), 12345); +} + +TEST(CoopConfigManagerTest, AppAttributeUnknownReturnsInvalid) +{ + QVariant v = ConfigManager::instance()->appAttribute("NoSuchCoopGroup", "NoSuchKey"); + EXPECT_FALSE(v.isValid()); +} + +TEST(CoopConfigManagerTest, AppAttributeStringRoundTrip) +{ + ConfigManager::instance()->setAppAttribute("CoopStrGroup", "name", QVariant("hello")); + QVariant v = ConfigManager::instance()->appAttribute("CoopStrGroup", "name"); + EXPECT_EQ(v.toString().toStdString(), "hello"); +} + +TEST(CoopConfigManagerTest, SyncAppAttributeReturnsBool) +{ + EXPECT_NO_FATAL_FAILURE(ConfigManager::instance()->syncAppAttribute()); +} diff --git a/tests/coop/cooperation_extra_test.cpp b/tests/coop/cooperation_extra_test.cpp new file mode 100644 index 000000000..e9c56aac4 --- /dev/null +++ b/tests/coop/cooperation_extra_test.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include +#include + +#include "lib/cooperation/core/utils/cooperationutil.h" +#include "lib/cooperation/core/discover/deviceinfo.h" +#include "lib/cooperation/core/net/helper/sharehelper.h" +#include "lib/cooperation/core/net/helper/transferhelper.h" +#include "base/baseutils.h" + +using cooperation_core::CooperationUtil; +using cooperation_core::DeviceInfo; +using cooperation_core::ShareHelper; +using cooperation_core::TransferHelper; +using ::DeviceInfoPointer; +using deepin_cross::BaseUtils; + +TEST(CooperationExtraTest, MainWindowWidgetNullWithoutRegisteredWindow) +{ + EXPECT_EQ(CooperationUtil::instance()->mainWindowWidget(), nullptr); +} + +TEST(CooperationExtraTest, ActivateWindowNoCrashWithoutWindow) +{ + EXPECT_NO_FATAL_FAILURE(CooperationUtil::instance()->activateWindow()); +} + +TEST(CooperationExtraTest, RegisterDeviceOperationNoCrashWithoutWindow) +{ + QVariantMap map; + map.insert("id", QString("test-op")); + map.insert("description", QString("desc")); + EXPECT_NO_FATAL_FAILURE(CooperationUtil::instance()->registerDeviceOperation(map)); +} + +TEST(CooperationExtraTest, DeviceInfoReturnsExpectedKeys) +{ + QVariantMap info = CooperationUtil::deviceInfo(); + EXPECT_FALSE(info.isEmpty()); + EXPECT_TRUE(info.contains(AppSettings::IPAddress)); + EXPECT_TRUE(info.contains(AppSettings::OSType)); + EXPECT_TRUE(info.contains(AppSettings::DeviceNameKey)); + EXPECT_TRUE(info.contains(AppSettings::DiscoveryModeKey)); + EXPECT_TRUE(info.contains(AppSettings::TransferModeKey)); + EXPECT_TRUE(info.contains(AppSettings::StoragePathKey)); + EXPECT_TRUE(info.contains(AppSettings::LinkDirectionKey)); +} + +TEST(CooperationExtraTest, DeviceInfoDefaultOsTypeIsOther) +{ + DeviceInfoPointer info(new DeviceInfo); + EXPECT_EQ(info->osType(), BaseUtils::OS_TYPE::kOther); +} + +TEST(CooperationExtraTest, DeviceInfoOsTypeRoundTripAllValues) +{ + DeviceInfoPointer info(new DeviceInfo("1.1.1.1", "host")); + + info->setOsType(BaseUtils::OS_TYPE::kLinux); + EXPECT_EQ(info->osType(), BaseUtils::OS_TYPE::kLinux); + + info->setOsType(BaseUtils::OS_TYPE::kWindows); + EXPECT_EQ(info->osType(), BaseUtils::OS_TYPE::kWindows); + + info->setOsType(BaseUtils::OS_TYPE::kMacOS); + EXPECT_EQ(info->osType(), BaseUtils::OS_TYPE::kMacOS); + + info->setOsType(BaseUtils::OS_TYPE::kOther); + EXPECT_EQ(info->osType(), BaseUtils::OS_TYPE::kOther); +} + +TEST(CooperationExtraTest, DeviceInfoOsTypePersistsViaVariantMap) +{ + DeviceInfoPointer original(new DeviceInfo("2.2.2.2", "host2")); + original->setOsType(BaseUtils::OS_TYPE::kWindows); + + QVariantMap map = original->toVariantMap(); + EXPECT_EQ(map.value(AppSettings::OSType).toInt(), static_cast(BaseUtils::OS_TYPE::kWindows)); + + DeviceInfoPointer restored = DeviceInfo::fromVariantMap(map); + ASSERT_NE(restored, nullptr); + EXPECT_EQ(restored->osType(), BaseUtils::OS_TYPE::kWindows); +} + +TEST(CooperationExtraTest, DeviceInfoDeviceTypeDirectSetterRoundTrip) +{ + DeviceInfoPointer pc(new DeviceInfo("3.3.3.3", "pc")); + pc->setDeviceType(DeviceInfo::DeviceType::PC); + EXPECT_EQ(pc->deviceType(), DeviceInfo::DeviceType::PC); + + DeviceInfoPointer mob(new DeviceInfo("4.4.4.4", "phone")); + mob->setDeviceType(DeviceInfo::DeviceType::Mobile); + EXPECT_EQ(mob->deviceType(), DeviceInfo::DeviceType::Mobile); +} + +TEST(CooperationExtraTest, ShareHelperInstanceNonNull) +{ + EXPECT_NE(ShareHelper::instance(), nullptr); +} + +TEST(CooperationExtraTest, ShareHelperInstanceSamePointer) +{ + auto *a = ShareHelper::instance(); + auto *b = ShareHelper::instance(); + EXPECT_EQ(a, b); +} + +TEST(CooperationExtraTest, ShareHelperRegistConnectBtnNoCrash) +{ + EXPECT_NO_FATAL_FAILURE(ShareHelper::instance()->registConnectBtn()); +} + +TEST(CooperationExtraTest, TransferHelperInstanceNonNull) +{ + EXPECT_NE(TransferHelper::instance(), nullptr); +} + +TEST(CooperationExtraTest, TransferHelperInstanceSamePointer) +{ + auto *a = TransferHelper::instance(); + auto *b = TransferHelper::instance(); + EXPECT_EQ(a, b); +} + +TEST(CooperationExtraTest, TransferHelperRegistBtnNoCrash) +{ + EXPECT_NO_FATAL_FAILURE(TransferHelper::instance()->registBtn()); +} + +TEST(CooperationExtraTest, TransferHelperStatusEnumValues) +{ + EXPECT_NE(TransferHelper::Idle, TransferHelper::Confirming); + EXPECT_NE(TransferHelper::Connecting, TransferHelper::Transfering); + EXPECT_EQ(TransferHelper::instance()->transferStatus(), TransferHelper::Idle); +} diff --git a/tests/coop/discover_safe_test.cpp b/tests/coop/discover_safe_test.cpp new file mode 100644 index 000000000..1407d765b --- /dev/null +++ b/tests/coop/discover_safe_test.cpp @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include +#include +#include +#include +#include + +#include "lib/cooperation/core/discover/discovercontroller.h" +#include "lib/cooperation/core/discover/discovercontroller_p.h" +#include "lib/cooperation/core/discover/deviceinfo.h" +#include "lib/cooperation/core/global_defines.h" + +using namespace cooperation_core; + +namespace { +QString makeValidDeviceJson(const QString &ip, const QString &name) +{ + QVariantMap map; + map.insert(AppSettings::IPAddress, ip); + map.insert(AppSettings::DeviceNameKey, name); + map.insert(AppSettings::DiscoveryModeKey, 0); + map.insert(AppSettings::TransferModeKey, 0); + map.insert(AppSettings::LinkDirectionKey, 0); + map.insert(AppSettings::ClipboardShareKey, false); + map.insert(AppSettings::PeripheralShareKey, false); + map.insert(AppSettings::CooperationEnabled, true); + map.insert(AppSettings::OSType, 0); + + QJsonObject obj = QJsonObject::fromVariantMap(map); + return QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); +} +} // namespace + +class DiscoverSafeTest : public ::testing::Test { +protected: + void SetUp() override + { + auto *c = DiscoverController::instance(); + c->_historyDevices.clear(); + c->d->onlineDeviceList.clear(); + c->d->historyDeviceMap.clear(); + c->d->searchDevice.reset(); + } +}; + +TEST_F(DiscoverSafeTest, UpdatePublishNoopBeforeZeroConfInit) +{ + EXPECT_NO_FATAL_FAILURE(DiscoverController::instance()->updatePublish()); +} + +TEST_F(DiscoverSafeTest, RefreshEmitsDiscoveryFinishedFalseBeforeInit) +{ + DiscoverController *c = DiscoverController::instance(); + int calls = 0; + bool lastOk = true; + QObject sink; + QObject::connect(c, &DiscoverController::discoveryFinished, &sink, [&](bool ok) { + ++calls; + lastOk = ok; + }); + + EXPECT_NO_FATAL_FAILURE(c->refresh()); + EXPECT_GE(calls, 1); + EXPECT_FALSE(lastOk); +} + +TEST_F(DiscoverSafeTest, StartDiscoverEmitsDiscoveryFinishedFalseBeforeInit) +{ + DiscoverController *c = DiscoverController::instance(); + int calls = 0; + bool lastOk = true; + QObject sink; + QObject::connect(c, &DiscoverController::discoveryFinished, &sink, [&](bool ok) { + ++calls; + lastOk = ok; + }); + + EXPECT_NO_FATAL_FAILURE(c->startDiscover()); + EXPECT_GE(calls, 1); + EXPECT_FALSE(lastOk); +} + +TEST_F(DiscoverSafeTest, UnpublishEmitsCompatUnregistration) +{ + DiscoverController *c = DiscoverController::instance(); + int calls = 0; + bool lastReg = true; + QString lastJson = "sentinel"; + QObject sink; + QObject::connect(c, &DiscoverController::registCompatAppInfo, &sink, [&](bool reg, const QString &json) { + ++calls; + lastReg = reg; + lastJson = json; + }); + + EXPECT_NO_FATAL_FAILURE(c->unpublish()); + EXPECT_GE(calls, 1); + EXPECT_FALSE(lastReg); + EXPECT_TRUE(lastJson.isEmpty()); +} + +TEST_F(DiscoverSafeTest, SelfInfoReturnsNonNullDeviceInfo) +{ + DeviceInfoPointer info = DiscoverController::selfInfo(); + ASSERT_NE(info, nullptr); +} + +TEST_F(DiscoverSafeTest, ParseDeviceJsonValidReturnsConnectableDevice) +{ + DiscoverController *c = DiscoverController::instance(); + QString json = makeValidDeviceJson("172.16.0.9", "HostZ"); + DeviceInfoPointer info = c->parseDeviceJson(json); + ASSERT_NE(info, nullptr); + EXPECT_EQ(info->ipAddress().toStdString(), "172.16.0.9"); + EXPECT_EQ(info->deviceName().toStdString(), "HostZ"); + EXPECT_EQ(info->connectStatus(), DeviceInfo::ConnectStatus::Connectable); +} + +TEST_F(DiscoverSafeTest, ParseDeviceJsonInvalidReturnsNull) +{ + DiscoverController *c = DiscoverController::instance(); + EXPECT_EQ(c->parseDeviceJson(QString("not-valid-json")), nullptr); +} + +TEST_F(DiscoverSafeTest, IsValidDeviceRejectsNullAndEmptyIp) +{ + DiscoverController *c = DiscoverController::instance(); + EXPECT_FALSE(c->isVaildDevice(nullptr)); + DeviceInfoPointer emptyIp(new DeviceInfo("", "NoIp")); + EXPECT_FALSE(c->isVaildDevice(emptyIp)); +} + +TEST_F(DiscoverSafeTest, IsValidDeviceAcceptsHistoryDeviceWithoutIpFilter) +{ + DiscoverController *c = DiscoverController::instance(); + c->_historyDevices.append("198.51.100.7"); + DeviceInfoPointer hist(new DeviceInfo("198.51.100.7", "Hist")); + EXPECT_TRUE(c->isVaildDevice(hist)); +} + +TEST_F(DiscoverSafeTest, OnlineDeviceListStartsEmpty) +{ + EXPECT_TRUE(DiscoverController::instance()->getOnlineDeviceList().isEmpty()); +} + +TEST_F(DiscoverSafeTest, FindDeviceByUnknownIPReturnsNull) +{ + EXPECT_EQ(DiscoverController::instance()->findDeviceByIP("0.0.0.0"), nullptr); +} diff --git a/tests/coop/filesystem_test.cpp b/tests/coop/filesystem_test.cpp new file mode 100644 index 000000000..9fb789b0b --- /dev/null +++ b/tests/coop/filesystem_test.cpp @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "common/filesystem.h" + +#include +#include +#include +#include +#include +#include + +using namespace deepin_cross; + +namespace { +QString tempFilePath(const QString &name) +{ + return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + name; +} +} + +TEST(FileSystemTest, OfstreamIfstreamRoundTrip) +{ + QString qpath = tempFilePath("coop_fs_roundtrip.txt"); + fs::path p(qpath.toStdString()); + const std::string payload = "roundtrip data 12345"; + + { + std::ofstream out; + open_utf8_path(out, p); + EXPECT_TRUE(out.is_open()); + out << payload; + out.close(); + } + + { + std::ifstream in; + open_utf8_path(in, p); + EXPECT_TRUE(in.is_open()); + std::string content((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + in.close(); + EXPECT_EQ(content, payload); + } + + QFile::remove(qpath); +} + +TEST(FileSystemTest, IfstreamMissingFileIsOpenFalse) +{ + QString qpath = tempFilePath("coop_fs_does_not_exist.txt"); + QFile::remove(qpath); + fs::path p(qpath.toStdString()); + + std::ifstream in; + open_utf8_path(in, p); + EXPECT_FALSE(in.is_open()); +} + +TEST(FileSystemTest, FstreamWriteAndRead) +{ + QString qpath = tempFilePath("coop_fs_fstream.txt"); + fs::path p(qpath.toStdString()); + const std::string payload = "fstream payload"; + + { + std::fstream fs_out; + open_utf8_path(fs_out, p, std::ios::out | std::ios::trunc); + EXPECT_TRUE(fs_out.is_open()); + fs_out << payload; + fs_out.close(); + } + + { + std::fstream fs_in; + open_utf8_path(fs_in, p, std::ios::in); + EXPECT_TRUE(fs_in.is_open()); + std::string content((std::istreambuf_iterator(fs_in)), + std::istreambuf_iterator()); + EXPECT_EQ(content, payload); + } + + QFile::remove(qpath); +} + +TEST(FileSystemTest, OfstreamOverwritesExisting) +{ + QString qpath = tempFilePath("coop_fs_overwrite.txt"); + fs::path p(qpath.toStdString()); + + { + std::ofstream out; + open_utf8_path(out, p); + out << "first content"; + out.close(); + } + + { + std::ofstream out; + open_utf8_path(out, p, std::ios::out | std::ios::trunc); + out << "second"; + out.close(); + } + + { + std::ifstream in; + open_utf8_path(in, p); + std::string content((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + EXPECT_EQ(content, "second"); + } + + QFile::remove(qpath); +} diff --git a/tests/coop/main.cpp b/tests/coop/main.cpp index 0f21c4cb4..467758f2d 100644 --- a/tests/coop/main.cpp +++ b/tests/coop/main.cpp @@ -23,6 +23,7 @@ #include #include +#include extern "C" void __gcov_dump() __attribute__((weak)); extern "C" void __gcov_flush() __attribute__((weak)); @@ -36,15 +37,23 @@ static void flushCoverage() } } +static void crash_handler(int sig) +{ + flushCoverage(); + _exit(128 + sig); +} + int main(int argc, char **argv) { + std::signal(SIGSEGV, crash_handler); + std::signal(SIGABRT, crash_handler); + std::signal(SIGFPE, crash_handler); + std::signal(SIGILL, crash_handler); + qputenv("QT_QPA_PLATFORM", "offscreen"); QApplication app(argc, argv); ::testing::InitGoogleTest(&argc, argv); int result = RUN_ALL_TESTS(); - // Flush coverage counters *before* static destructors run (the - // ConfigManager/QApplication teardown races cause the exit-134 abort that - // would otherwise skip gcov finalization). flushCoverage(); return result; } diff --git a/tests/coop/phone_smoke_test.cpp b/tests/coop/phone_smoke_test.cpp new file mode 100644 index 000000000..6376538fe --- /dev/null +++ b/tests/coop/phone_smoke_test.cpp @@ -0,0 +1,227 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/cooperation/core/gui/phone/vncviewer.h" +#include "lib/cooperation/core/gui/phone/vncrecvthread.h" +#include "lib/cooperation/core/gui/phone/vncsendworker.h" +#include "lib/cooperation/core/gui/phone/screenmirroringwindow.h" +#include "lib/cooperation/core/gui/phone/phonewidget.h" +#include "lib/cooperation/core/net/helper/phonehelper.h" +#include "lib/cooperation/core/discover/deviceinfo.h" + +using cooperation_core::VncViewer; +using ::VNCRecvThread; +using ::VNCSendWorker; +using cooperation_core::ScreenMirroringWindow; +using cooperation_core::LockScreenWidget; +using cooperation_core::PhoneWidget; +using cooperation_core::QRCodeWidget; +using cooperation_core::PhoneHelper; +using cooperation_core::DeviceInfo; +using ::DeviceInfoPointer; + +TEST(VncViewerSmoke, ConstructAndDestroy) +{ + EXPECT_NO_FATAL_FAILURE({ + VncViewer *v = new VncViewer(); + delete v; + }); +} + +TEST(VncViewerSmoke, BackgroundBrushRoundTrip) +{ + VncViewer v; + QBrush brush(Qt::red); + v.setBackgroundBrush(brush); + EXPECT_EQ(v.backgroundBrush(), brush); +} + +TEST(VncViewerSmoke, ScaledRoundTrip) +{ + VncViewer v; + EXPECT_TRUE(v.scaled()); + v.setScaled(false); + EXPECT_FALSE(v.scaled()); + v.setScaled(true); + EXPECT_TRUE(v.scaled()); +} + +TEST(VncViewerSmoke, FpsAndFrameCounterRoundTrip) +{ + VncViewer v; + v.setFrameCounter(0); + EXPECT_EQ(v.frameCounter(), 0); + v.incFrameCounter(); + EXPECT_EQ(v.frameCounter(), 1); + v.setFrameCounter(42); + EXPECT_EQ(v.frameCounter(), 42); + v.setCurrentFps(7); + EXPECT_EQ(v.currentFps(), 7); +} + +TEST(VncViewerSmoke, SetServesNoCrash) +{ + VncViewer v; + EXPECT_NO_FATAL_FAILURE(v.setServes(std::string("127.0.0.1"), 5900, std::string("pwd"))); +} + +TEST(VncViewerSmoke, SetMobileRealSizeNoCrash) +{ + VncViewer v; + EXPECT_NO_FATAL_FAILURE(v.setMobileRealSize(400, 800)); + EXPECT_NO_FATAL_FAILURE(v.setMobileRealSize(800, 400)); +} + +TEST(VncViewerSmoke, FrameTimerTimeoutResetsCounterAndSetsFps) +{ + VncViewer v; + v.setFrameCounter(5); + EXPECT_NO_FATAL_FAILURE(v.frameTimerTimeout()); + EXPECT_EQ(v.frameCounter(), 0); + EXPECT_EQ(v.currentFps(), 5); +} + +TEST(VncViewerSmoke, TranslateMouseButtonMapping) +{ + VncViewer v; + EXPECT_NE(v.translateMouseButton(Qt::LeftButton), 0); + EXPECT_NE(v.translateMouseButton(Qt::MiddleButton), 0); + EXPECT_NE(v.translateMouseButton(Qt::RightButton), 0); + EXPECT_EQ(v.translateMouseButton(Qt::NoButton), 0); +} + +TEST(VncViewerSmoke, StopWhenNotConnectedEarlyReturn) +{ + VncViewer v; + EXPECT_NO_FATAL_FAILURE(v.stop()); +} + +TEST(VncViewerSmoke, OnSizeChangeWhenNotConnectedIgnored) +{ + VncViewer v; + v.m_connected = false; + EXPECT_NO_FATAL_FAILURE(v.onSizeChange(100, 200)); +} + +TEST(VncViewerSmoke, OnShortcutActionWhenNotConnectedIgnored) +{ + VncViewer v; + v.m_connected = false; + EXPECT_NO_FATAL_FAILURE(v.onShortcutAction(0)); + EXPECT_NO_FATAL_FAILURE(v.onShortcutAction(1)); +} + +TEST(VncViewerSmoke, UpdateImageWithNullImageIgnored) +{ + VncViewer v; + QImage nullImg; + EXPECT_NO_FATAL_FAILURE(v.updateImage(nullImg)); +} + +TEST(VncViewerSmoke, UpdateSurfaceNoCrash) +{ + VncViewer v; + v.resize(200, 300); + EXPECT_NO_FATAL_FAILURE(v.updateSurface()); +} + +TEST(VncRecvThreadSmoke, ConstructAndStopRunIdle) +{ + VNCRecvThread t; + EXPECT_NO_FATAL_FAILURE(t.stopRun()); +} + +TEST(VncSendWorkerSmoke, Construct) +{ + EXPECT_NO_FATAL_FAILURE({ VNCSendWorker w; }); +} + +TEST(LockScreenWidgetSmoke, Construct) +{ + EXPECT_NO_FATAL_FAILURE({ LockScreenWidget w; }); +} + +TEST(ScreenMirroringWindowSmoke, ConstructAndDestroy) +{ + EXPECT_NO_FATAL_FAILURE({ + ScreenMirroringWindow *w = new ScreenMirroringWindow("device-A"); + delete w; + }); +} + +TEST(ScreenMirroringWindowSmoke, InitSizeByViewNoCrash) +{ + ScreenMirroringWindow *w = new ScreenMirroringWindow("device-B"); + QSize sz(360, 640); + EXPECT_NO_FATAL_FAILURE(w->initSizebyView(sz)); + delete w; +} + +TEST(ScreenMirroringWindowSmoke, HandleSizeChangeNoCrash) +{ + ScreenMirroringWindow *w = new ScreenMirroringWindow("device-C"); + EXPECT_NO_FATAL_FAILURE(w->handleSizeChange(QSize(360, 640))); + delete w; +} + +TEST(PhoneWidgetSmoke, ConstructAndSwitchPages) +{ + PhoneWidget pw; + EXPECT_NO_FATAL_FAILURE(pw.switchWidget(PhoneWidget::kDeviceListWidget)); + EXPECT_NO_FATAL_FAILURE(pw.switchWidget(PhoneWidget::kNoNetworkWidget)); + EXPECT_NO_FATAL_FAILURE(pw.switchWidget(PhoneWidget::kQRCodeWidget)); +} + +TEST(PhoneWidgetSmoke, SwitchToUnknownPageNoOp) +{ + PhoneWidget pw; + EXPECT_NO_FATAL_FAILURE(pw.switchWidget(PhoneWidget::kUnknownPage)); +} + +TEST(PhoneWidgetSmoke, OnSetQRcodeInfoNoCrash) +{ + PhoneWidget pw; + EXPECT_NO_FATAL_FAILURE(pw.onSetQRcodeInfo("some-info")); +} + +TEST(PhoneWidgetSmoke, SetDeviceInfoNoCrash) +{ + PhoneWidget pw; + DeviceInfoPointer info(new DeviceInfo("10.0.0.1", "phone")); + info->setConnectStatus(DeviceInfo::Connectable); + EXPECT_NO_FATAL_FAILURE(pw.setDeviceInfo(info)); +} + +TEST(QRCodeWidgetSmoke, GenerateQRCodeReturnsPixmap) +{ + QRCodeWidget w; + QPixmap pm = w.generateQRCode("hello", 4); + EXPECT_FALSE(pm.isNull()); +} + +TEST(QRCodeWidgetSmoke, GenerateQRCodeEmptyTextReturnsNull) +{ + QRCodeWidget w; + QPixmap pm = w.generateQRCode("", 4); + EXPECT_TRUE(pm.isNull()); +} + +TEST(PhoneHelperSmoke, InstanceNonNull) +{ + EXPECT_NE(PhoneHelper::instance(), nullptr); +} + +TEST(PhoneHelperSmoke, InstanceSamePointer) +{ + EXPECT_EQ(PhoneHelper::instance(), PhoneHelper::instance()); +} diff --git a/tests/coop/settings_test.cpp b/tests/coop/settings_test.cpp new file mode 100644 index 000000000..ec11cbe00 --- /dev/null +++ b/tests/coop/settings_test.cpp @@ -0,0 +1,276 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "configs/settings/settings.h" + +#include +#include +#include +#include + +namespace { +QString writeFile(const QString &dir, const QString &name, const char *content) +{ + QDir().mkpath(dir); + QString path = dir + "/" + name; + QFile f(path); + if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + f.write(content); + f.close(); + } + return path; +} + +QString tempDir(const QString &sub) +{ + return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + sub; +} +} + +TEST(SettingsTest, ConstructFromFilesAndReadValueChain) +{ + QString dir = tempDir("coop_sett_chain"); + QString df = writeFile(dir, "default.json", + R"({"group_a":{"key1":"def","key2":42}})"); + QString ff = writeFile(dir, "fallback.json", + R"({"group_b":{"x":1}})"); + QString sf = writeFile(dir, "setting.json", + R"({"group_a":{"key1":"wrote"}})"); + + Settings s(df, ff, sf); + EXPECT_EQ(s.value("group_a", "key1").toString().toStdString(), "wrote"); + EXPECT_EQ(s.value("group_b", "x").toInt(), 1); + EXPECT_EQ(s.value("group_a", "key2").toInt(), 42); + EXPECT_EQ(s.value("group_a", "nope", "dft").toString().toStdString(), "dft"); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, ContainsChecksAllLayers) +{ + QString dir = tempDir("coop_sett_contains"); + QString df = writeFile(dir, "default.json", R"({"g_d":{"k":"d"}})"); + QString ff = writeFile(dir, "fallback.json", R"({"g_f":{"k":"f"}})"); + QString sf = writeFile(dir, "setting.json", R"({"g_w":{"k":"w"}})"); + + Settings s(df, ff, sf); + EXPECT_TRUE(s.contains("g_w", "k")); + EXPECT_TRUE(s.contains("g_f", "k")); + EXPECT_TRUE(s.contains("g_d", "k")); + EXPECT_FALSE(s.contains("g_x", "k")); + EXPECT_TRUE(s.contains("g_w", "")); + EXPECT_FALSE(s.contains("nope", "")); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, GroupsKeysAndKeyList) +{ + QString dir = tempDir("coop_sett_keys"); + QString df = writeFile(dir, "default.json", + R"({"__metadata__":{"grp":{"keyOrdered":["a","b","c"]}},"grp":{"a":1,"b":2,"c":3}})"); + QString ff = writeFile(dir, "fallback.json", R"({})"); + QString sf = writeFile(dir, "setting.json", R"({"grp":{"extra":9}})"); + + Settings s(df, ff, sf); + EXPECT_TRUE(s.groups().contains("grp")); + auto ks = s.keys("grp"); + EXPECT_TRUE(ks.contains("a")); + EXPECT_TRUE(ks.contains("extra")); + + QStringList kl = s.keyList("grp"); + EXPECT_EQ(kl.first().toStdString(), "a"); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, SetValueAndSetValueNoNotifySemantics) +{ + Settings s(":/coop_nd.json", ":/coop_nf.json", ":/coop_ns.json"); + QSignalSpy changed(&s, &Settings::valueChanged); + + s.setValue("g", "k", "v"); + EXPECT_EQ(changed.size(), 1); + EXPECT_EQ(s.value("g", "k").toString().toStdString(), "v"); + + s.setValue("g", "k", "v"); + EXPECT_EQ(changed.size(), 1); + + s.setValue("g", "k", "v2"); + EXPECT_EQ(changed.size(), 2); + EXPECT_EQ(s.value("g", "k").toString().toStdString(), "v2"); + + changed.clear(); + EXPECT_TRUE(s.setValueNoNotify("g", "k", "v3")); + EXPECT_EQ(changed.size(), 0); + EXPECT_EQ(s.value("g", "k").toString().toStdString(), "v3"); + + EXPECT_FALSE(s.setValueNoNotify("g", "k", "v3")); + + EXPECT_TRUE(s.setValueNoNotify("g", "knew", "nv")); + EXPECT_EQ(s.value("g", "knew").toString().toStdString(), "nv"); +} + +TEST(SettingsTest, RemoveAndRemoveGroupAndClear) +{ + Settings s(":/coop_rmd.json", ":/coop_rmf.json", ":/coop_rms.json"); + s.setValue("g", "k1", 1); + s.setValue("g", "k2", 2); + + QSignalSpy spy(&s, &Settings::valueChanged); + s.remove("g", "absent"); + EXPECT_EQ(spy.size(), 0); + + s.remove("g", "k1"); + EXPECT_FALSE(s.isRemovable("g", "k1")); + + spy.clear(); + s.setValue("grp", "a", "A"); + s.removeGroup("grp"); + EXPECT_FALSE(s.isRemovable("grp", "a")); + + s.removeGroup("notexist"); + + s.setValue("c", "x", 1); + spy.clear(); + s.clear(); + s.clear(); + SUCCEED(); +} + +TEST(SettingsTest, SyncWritesToFileAndReload) +{ + QString dir = tempDir("coop_sett_sync"); + QString df = writeFile(dir, "default.json", R"({"g":{"k":"d"}})"); + QString ff = writeFile(dir, "fallback.json", R"({"g":{"fb":1}})"); + QString sf = writeFile(dir, "setting.json", R"({})"); + + Settings s(df, ff, sf); + s.setValue("g", "new", "val"); + EXPECT_TRUE(s.sync()); + + QFile f(sf); + ASSERT_TRUE(f.open(QIODevice::ReadOnly)); + QByteArray data = f.readAll(); + f.close(); + EXPECT_TRUE(data.contains("val")); + + EXPECT_TRUE(s.sync()); + + s.reload(); + SUCCEED(); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, AutoSyncAndWatchChangesToggles) +{ + QString dir = tempDir("coop_sett_auto"); + QString df = writeFile(dir, "default.json", R"({"g":{"k":1}})"); + QString ff = writeFile(dir, "fallback.json", R"({})"); + QString sf = writeFile(dir, "setting.json", R"({})"); + + Settings s(df, ff, sf); + EXPECT_FALSE(s.autoSync()); + EXPECT_FALSE(s.watchChanges()); + + s.setAutoSync(true); + EXPECT_TRUE(s.autoSync()); + s.setAutoSync(true); + EXPECT_TRUE(s.autoSync()); + s.setValue("g", "k", 2); + + s.setAutoSync(false); + EXPECT_FALSE(s.autoSync()); + + s.setWatchChanges(true); + EXPECT_TRUE(s.watchChanges()); + s.setWatchChanges(true); + EXPECT_TRUE(s.watchChanges()); + s.setWatchChanges(false); + EXPECT_FALSE(s.watchChanges()); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, WatchChangesCreatesMissingSettingFile) +{ + QString dir = tempDir("coop_sett_watch_missing"); + QDir().mkpath(dir); + QString df = writeFile(dir, "default.json", R"({"g":{"k":1}})"); + QString ff = writeFile(dir, "fallback.json", R"({})"); + QString sf = dir + "/sub/notexist.json"; + + Settings s(df, ff, sf); + s.setWatchChanges(true); + EXPECT_TRUE(s.watchChanges()); + s.setWatchChanges(false); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); + QDir(dir).removeRecursively(); +} + +TEST(SettingsTest, OnFileChangedPathMismatchNoop) +{ + QString dir = tempDir("coop_sett_ofc"); + QString df = writeFile(dir, "default.json", R"({"g":{"k":1}})"); + QString ff = writeFile(dir, "fallback.json", R"({})"); + QString sf = writeFile(dir, "setting.json", R"({"g":{"k":2}})"); + + Settings s(df, ff, sf); + s.onFileChanged("/some/other/path.json"); + SUCCEED(); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} + +TEST(SettingsTest, NameTypeConstructorAppConfig) +{ + Settings s("nonexistent_coop_app", Settings::AppConfig); + EXPECT_FALSE(s.contains("g", "k")); + SUCCEED(); +} + +TEST(SettingsTest, NameTypeConstructorGenericConfig) +{ + Settings s("nonexistent_coop_gen", Settings::GenericConfig); + EXPECT_FALSE(s.contains("g", "k")); + SUCCEED(); +} + +TEST(SettingsTest, InvalidJsonFallbackGraceful) +{ + QString dir = tempDir("coop_sett_json"); + QString df = writeFile(dir, "default.json", "{ not valid json"); + QString ff = writeFile(dir, "fallback.json", R"([1,2,3])"); + QString sf = writeFile(dir, "setting.json", ""); + + Settings s(df, ff, sf); + EXPECT_FALSE(s.contains("g", "k")); + EXPECT_EQ(s.groups().size(), 0); + + writeFile(dir, "default.json", R"({"good":{"a":1},"bad":5})"); + Settings s2(df, ":/coop_nf2.json", ":/coop_ns2.json"); + EXPECT_TRUE(s2.groups().contains("good")); + + QFile::remove(df); + QFile::remove(ff); + QFile::remove(sf); +} diff --git a/tests/session_core/session_integration_test.cpp b/tests/session_core/session_integration_test.cpp new file mode 100644 index 000000000..ef04e9c33 --- /dev/null +++ b/tests/session_core/session_integration_test.cpp @@ -0,0 +1,220 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "protoserver.h" +#include "protoclient.h" +#include "asioservice.h" +#include "manager/secureconfig.h" +#include "session.h" + +#include +#include +#include + +using namespace std::chrono_literals; + +class TestCallback : public SessionCallInterface +{ +public: + int lastState = -999; + std::string lastMsg; + int msgCount = 0; + + void onReceivedMessage(const proto::OriginMessage &req, proto::OriginMessage *resp) override + { + msgCount++; + resp->id = req.id; + resp->mask = req.mask; + resp->json_msg = R"({"reply":"ok"})"; + } + + bool onStateChanged(int state, std::string &msg) override + { + lastState = state; + lastMsg = msg; + return false; + } +}; + +class SessionIntegrationTest : public ::testing::Test { +protected: + std::shared_ptr service; + std::shared_ptr cb; + + void SetUp() override + { + service = std::make_shared(); + service->Start(); + cb = std::make_shared(); + } + void TearDown() override + { + if (service) service->Stop(); + } +}; + +// Test real server+client connection on localhost +TEST_F(SessionIntegrationTest, ServerStartStop) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15001); + server.setCallbacks(cb); + EXPECT_FALSE(server.hasConnected("127.0.0.1")); +} + +TEST_F(SessionIntegrationTest, ServerHasConnectedDirectMatch) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15002); + server.setCallbacks(cb); + BaseKit::UUID id; + server._session_ids.insert({"10.0.0.5", id}); + EXPECT_TRUE(server.hasConnected("10.0.0.5")); +} + +TEST_F(SessionIntegrationTest, ServerHandleRealIPMapping) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15003); + server.handleRealIPMapping("192.168.1.100", "10.0.0.1"); + EXPECT_EQ(server._real_to_remote_ip["192.168.1.100"], "10.0.0.1"); + EXPECT_EQ(server._remote_to_real_ip["10.0.0.1"], "192.168.1.100"); +} + +TEST_F(SessionIntegrationTest, ServerHandleRealIPMappingReplace) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15004); + server.handleRealIPMapping("192.168.1.100", "10.0.0.1"); + server.handleRealIPMapping("192.168.1.100", "10.0.0.2"); + EXPECT_EQ(server._real_to_remote_ip["192.168.1.100"], "10.0.0.2"); + EXPECT_EQ(server._remote_to_real_ip.find("10.0.0.1"), server._remote_to_real_ip.end()); +} + +TEST_F(SessionIntegrationTest, ServerHandlePingNew) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15005); + server.setCallbacks(cb); + server.handlePing("10.0.0.99"); + EXPECT_TRUE(server._ping_remotes.find("10.0.0.99") != server._ping_remotes.end()); +} + +TEST_F(SessionIntegrationTest, ServerHandlePingExisting) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15006); + server.setCallbacks(cb); + server.handlePing("10.0.0.99"); + server.handlePing("10.0.0.99"); + SUCCEED(); +} + +TEST_F(SessionIntegrationTest, ServerHasConnectedViaIPMapping) +{ + auto context = SecureConfig::serverContext(); + ProtoServer server(service, context, 15007); + server._real_to_remote_ip["100.100.100.100"] = "10.0.0.1"; + server._remote_to_real_ip["10.0.0.1"] = "100.100.100.100"; + BaseKit::UUID id; + server._session_ids.insert({"10.0.0.1", id}); + EXPECT_TRUE(server.hasConnected("100.100.100.100")); +} + +// ---- ProtoClient tests ---- + +TEST_F(SessionIntegrationTest, ClientHasConnectedDefault) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16001); + EXPECT_FALSE(client.hasConnected("192.168.1.1")); +} + +TEST_F(SessionIntegrationTest, ClientConnectReplyedDefault) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16002); + EXPECT_FALSE(client.connectReplyed()); +} + +TEST_F(SessionIntegrationTest, ClientSetRealIP) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16003); + client.setRealIP("10.0.0.99"); + SUCCEED(); +} + +TEST_F(SessionIntegrationTest, ClientStartHeartbeatNotConnected) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16004); + EXPECT_FALSE(client.startHeartbeat()); +} + +TEST_F(SessionIntegrationTest, ClientPingTimerStop) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16005); + client.pingTimerStop(); + SUCCEED(); +} + +TEST_F(SessionIntegrationTest, ClientDisconnectAndStop) +{ + auto context = SecureConfig::clientContext(); + auto *client = new ProtoClient(service, context, "127.0.0.1", 16006); + client->DisconnectAndStop(); + delete client; + SUCCEED(); +} + +// ---- ProtoEndpoint via client ---- + +TEST_F(SessionIntegrationTest, ClientSyncRequestNoConnectionReturnsEmpty) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16007); + client.setCallbacks(cb); + proto::OriginMessage msg; + msg.mask = 1000; + msg.json_msg = R"({"test":"data"})"; + auto result = client.syncRequest("127.0.0.1", msg); + EXPECT_TRUE(result.json_msg.empty()); +} + +TEST_F(SessionIntegrationTest, ClientSendDisRequestNoConnection) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16008); + client.setCallbacks(cb); + EXPECT_NO_FATAL_FAILURE(client.sendDisRequest()); +} + +TEST_F(SessionIntegrationTest, AsyncRequestWithHandlerTimeout) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16009); + client.setCallbacks(cb); + + proto::OriginMessage msg; + msg.mask = 1000; + msg.json_msg = R"({"test":"async"})"; + + bool called = false; + client.asyncRequestWithHandler("192.168.1.1", msg, [&](int32_t type, const std::string &) { + called = true; + }); + std::this_thread::sleep_for(std::chrono::milliseconds(3500)); + EXPECT_TRUE(called); +} + +TEST_F(SessionIntegrationTest, SelfRequestFlagDefault) +{ + auto context = SecureConfig::clientContext(); + ProtoClient client(service, context, "127.0.0.1", 16010); + EXPECT_FALSE(client._self_request.load()); +}