//===-- DAPSessionManagerTest.cpp ----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "DAPSessionManager.h" #include "TestBase.h" #include "lldb/API/SBDebugger.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace lldb_dap; using namespace lldb; using namespace lldb_dap_tests; class DAPSessionManagerTest : public DAPTestBase {}; TEST_F(DAPSessionManagerTest, GetInstanceReturnsSameSingleton) { DAPSessionManager &instance1 = DAPSessionManager::GetInstance(); DAPSessionManager &instance2 = DAPSessionManager::GetInstance(); EXPECT_EQ(&instance1, &instance2); } // UnregisterSession uses std::notify_all_at_thread_exit, so it must be called // from a separate thread to properly release the mutex on thread exit. TEST_F(DAPSessionManagerTest, RegisterAndUnregisterSession) { DAPSessionManager &manager = DAPSessionManager::GetInstance(); // Initially not registered. std::vector sessions_before = manager.GetActiveSessions(); EXPECT_EQ( std::count(sessions_before.begin(), sessions_before.end(), dap.get()), 0); manager.RegisterSession(&loop, dap.get()); // Should be in active sessions after registration. std::vector sessions_after = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions_after.begin(), sessions_after.end(), dap.get()), 1); // Unregister. std::thread unregister_thread([&]() { manager.UnregisterSession(&loop); }); unregister_thread.join(); // There should no longer be active sessions. std::vector sessions_final = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions_final.begin(), sessions_final.end(), dap.get()), 0); } TEST_F(DAPSessionManagerTest, DisconnectAllSessions) { DAPSessionManager &manager = DAPSessionManager::GetInstance(); manager.RegisterSession(&loop, dap.get()); std::vector sessions = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1); manager.DisconnectAllSessions(); // DisconnectAllSessions shutdown but doesn't wait for // sessions to complete or remove them from the active sessions map. sessions = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1); std::thread unregister_thread([&]() { manager.UnregisterSession(&loop); }); unregister_thread.join(); } TEST_F(DAPSessionManagerTest, WaitForAllSessionsToDisconnect) { DAPSessionManager &manager = DAPSessionManager::GetInstance(); manager.RegisterSession(&loop, dap.get()); std::vector sessions = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1); // Unregister after a delay to test blocking behavior. std::thread unregister_thread([&]() { std::this_thread::sleep_for(std::chrono::milliseconds(100)); manager.UnregisterSession(&loop); }); // WaitForAllSessionsToDisconnect should block until unregistered. auto start = std::chrono::steady_clock::now(); llvm::Error err = manager.WaitForAllSessionsToDisconnect(); EXPECT_FALSE(err); auto duration = std::chrono::steady_clock::now() - start; // Verify it waited at least 100ms. EXPECT_GE(duration, std::chrono::milliseconds(100)); // Session should be unregistered now. sessions = manager.GetActiveSessions(); EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 0); unregister_thread.join(); }