From b34c8e0472fc324592a21b35c990f719004341a1 Mon Sep 17 00:00:00 2001 From: Broseki Date: Sat, 4 Jan 2025 16:43:13 -0500 Subject: [PATCH 1/3] Switched logger to be a singleton class --- .gitignore | 2 + Session/Session.cpp | 1 + Session/Sessions.cpp | 1 + Tests/Session/Session_test.cpp | 13 ++- Tests/Session/Sessions_test.cpp | 45 +++++++-- Tests/Utils/Logger/Logger_test.cpp | 155 ++++++++++++++++++++--------- Utils/Logger/Logger.cpp | 27 +++++ Utils/Logger/Logger.h | 17 +++- main.cpp | 23 +++-- 9 files changed, 211 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index a64bac2..a88e4a5 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,5 @@ libs/ .vscode/ **/.DS_Store + +vcpkg_installed diff --git a/Session/Session.cpp b/Session/Session.cpp index 5363daf..87fedf3 100644 --- a/Session/Session.cpp +++ b/Session/Session.cpp @@ -8,6 +8,7 @@ #include "KV_Keys.h" Session::Session(int socketfd) { + std::cout << "Creating session with FD: " << socketfd << std::endl; this->socketfd = socketfd; } diff --git a/Session/Sessions.cpp b/Session/Sessions.cpp index 4cafd4a..5efc4f6 100644 --- a/Session/Sessions.cpp +++ b/Session/Sessions.cpp @@ -5,6 +5,7 @@ #include "Sessions.h" #include #include +#include Sessions::Sessions(Configuration *config, Logger *logger) { this->config = config; diff --git a/Tests/Session/Session_test.cpp b/Tests/Session/Session_test.cpp index b6e520a..7fa9bfe 100644 --- a/Tests/Session/Session_test.cpp +++ b/Tests/Session/Session_test.cpp @@ -72,22 +72,25 @@ TEST_F(SessionTest, ClearOutputBuffer) { TEST_F(SessionTest, AddRemoveStateMachine) { // Setup auto state_machine = std::make_shared(); - Logger logger(&std::cout, Logger::FATAL); + Logger* logger = Logger::initialize(&std::cout, Logger::FATAL); // Test to make sure the session doesn't have any state machines EXPECT_EQ(session_uut.get_state_machines().size(), 0); // Add the state machine - session_uut.add_state_machine(&logger, state_machine); + session_uut.add_state_machine(logger, state_machine); // Test to make sure the session has the state machine EXPECT_EQ(session_uut.get_state_machines().size(), 1); // Remove the state machine - session_uut.remove_state_machine(&logger, state_machine); + session_uut.remove_state_machine(logger, state_machine); // Test to make sure the session doesn't have any state machines EXPECT_EQ(session_uut.get_state_machines().size(), 0); + + // Tear down logger + Logger::destroy(); } TEST_F(SessionTest, GetSetKeyValue) { @@ -123,14 +126,14 @@ TEST_F(SessionTest, RemoveKeyValue) { TEST_F(SessionTest, GetSetView) { - Logger logger(&std::cout, Logger::FATAL); + Logger *logger = Logger::initialize(&std::cout, Logger::FATAL); auto view = std::make_shared(); // Test to make sure the session doesn't have a view EXPECT_EQ(session_uut.get_view(), nullptr); // Set the view - session_uut.set_view(&logger, view); + session_uut.set_view(logger, view); // Test to make sure the session has the view EXPECT_EQ(session_uut.get_view(), view); diff --git a/Tests/Session/Sessions_test.cpp b/Tests/Session/Sessions_test.cpp index cb1e898..06b0982 100644 --- a/Tests/Session/Sessions_test.cpp +++ b/Tests/Session/Sessions_test.cpp @@ -6,19 +6,53 @@ #include "../../Session/Sessions.h" #include + class MockLogger : public Logger { public: - MockLogger(std::ostream* output_stream, LogLevel level) : Logger(output_stream, level) {} + static MockLogger* get_instance() { + if (m_instance == nullptr) { + m_instance = new MockLogger(&std::cout, Logger::DEBUG); + } + return m_instance; + } + + static void reset() { + if (m_instance != nullptr) { + delete m_instance; + m_instance = nullptr; + } + } + MOCK_METHOD(void, log, (LogLevel level, std::string message), (override)); + +protected: + MockLogger(std::ostream* output_stream, LogLevel level) : Logger(output_stream, level) {} + +private: + static MockLogger* m_instance; }; +// Initialize the static member +MockLogger* MockLogger::m_instance = nullptr; + class SessionsTest : public ::testing::Test { protected: - SessionsTest() : configuration("test_config.json"), logger(&std::cout, Logger::FATAL), sessions(&configuration, &logger) {} + SessionsTest() : configuration("test_config.json"), logger(MockLogger::get_instance()), sessions(&configuration, logger) {} Configuration configuration; - Logger logger; + MockLogger* logger; Sessions sessions; + +public: + + void TearDown() override { + testing::Mock::VerifyAndClearExpectations(logger); + } + + static void TearDownTestSuite() { + MockLogger::reset(); + } + }; TEST_F(SessionsTest, AddSession) { @@ -115,10 +149,9 @@ TEST_F(SessionsTest, RemoveNonExistentSession) { TEST_F(SessionsTest, Destructor) { // Expect the logger to be called with the correct message - MockLogger logger_tmp(&std::cout, Logger::INFO); - EXPECT_CALL(logger_tmp, log(Logger::INFO, "Destroying Sessions object")).Times(1); + EXPECT_CALL(*logger, log(Logger::INFO, "Destroying Sessions object")).Times(1); - Sessions *tmp_sessions = new Sessions(&configuration, &logger_tmp); + Sessions *tmp_sessions = new Sessions(&configuration, logger); tmp_sessions->addSession(std::make_shared(0)); tmp_sessions->addSession(std::make_shared(0)); diff --git a/Tests/Utils/Logger/Logger_test.cpp b/Tests/Utils/Logger/Logger_test.cpp index 1af18df..91e7fbf 100644 --- a/Tests/Utils/Logger/Logger_test.cpp +++ b/Tests/Utils/Logger/Logger_test.cpp @@ -7,7 +7,7 @@ using namespace testing; class LoggerTest : public Test { public: std::ostringstream output_stream; - Logger logger{&output_stream, Logger::DEBUG}; + }; TEST_F(LoggerTest, LogLevelToString) { @@ -32,210 +32,267 @@ TEST_F(LoggerTest, StringToLogLevel) { TEST_F(LoggerTest, LogDebug) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Debug message"; - logger.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); // Test EXPECT_EQ(output_stream.str().find("[DEBUG - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogInfo) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Info message"; - logger.log(Logger::INFO, message); + logger->log(Logger::INFO, message); // Test EXPECT_EQ(output_stream.str().find("[INFO - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogWarning) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Warning message"; - logger.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); // Test EXPECT_EQ(output_stream.str().find("[WARNING - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogError) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Error message"; - logger.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); // Test EXPECT_EQ(output_stream.str().find("[ERROR - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogFatal) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Fatal message"; - logger.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); // Test EXPECT_EQ(output_stream.str().find("[FATAL - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogUnknownLogLevel) { // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Unknown log level message"; - logger.log(static_cast(100), message); + logger->log(static_cast(100), message); // Test EXPECT_EQ(output_stream.str().find("[UNKNOWN - "), 0); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelDebug) { // Setup - Logger logger_tmp{&output_stream, Logger::DEBUG}; + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelInfo) { // Setup - Logger logger_tmp{&output_stream, Logger::INFO}; + Logger* logger = Logger::initialize(&output_stream, Logger::INFO); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelWarning) { // Setup - Logger logger_tmp{&output_stream, Logger::WARNING}; + Logger* logger = Logger::initialize(&output_stream, Logger::WARNING); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelError) { // Setup - Logger logger_tmp{&output_stream, Logger::ERROR}; + Logger* logger = Logger::initialize(&output_stream, Logger::ERROR); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelFatal) { // Setup - Logger logger_tmp{&output_stream, Logger::FATAL}; + Logger *logger = Logger::initialize(&output_stream, Logger::FATAL); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_NE(output_stream.str().find(message), std::string::npos); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); } TEST_F(LoggerTest, LogLevelUnknown) { // Setup - Logger logger_tmp{&output_stream, static_cast(100)}; + Logger *logger = Logger::initialize(&output_stream, static_cast(100)); std::string message = "Test message"; // Test - logger_tmp.log(Logger::DEBUG, message); + logger->log(Logger::DEBUG, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::INFO, message); + logger->log(Logger::INFO, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::WARNING, message); + logger->log(Logger::WARNING, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::ERROR, message); + logger->log(Logger::ERROR, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(Logger::FATAL, message); + logger->log(Logger::FATAL, message); EXPECT_EQ(output_stream.str(), ""); - logger_tmp.log(static_cast(100), message); + logger->log(static_cast(100), message); EXPECT_NE(output_stream.str().find(message), std::string::npos); + + // Tear down + Logger::destroy(); +} + +TEST_F(LoggerTest, DuplicateInitializeCall) { + // Call initialize twice + Logger::initialize(&output_stream, Logger::DEBUG); + // The first call should be successful with no warning message + EXPECT_EQ(output_stream.str(), ""); + + // Call initialize again + Logger::initialize(&output_stream, Logger::DEBUG); + // We should get a warning message + EXPECT_EQ(output_stream.str().find("[WARNING - "), 0); + + // Tear down + Logger::destroy(); } \ No newline at end of file diff --git a/Utils/Logger/Logger.cpp b/Utils/Logger/Logger.cpp index 9e5388f..4d2aaf1 100644 --- a/Utils/Logger/Logger.cpp +++ b/Utils/Logger/Logger.cpp @@ -8,6 +8,33 @@ #include #include +Logger* Logger::m_instance = nullptr; + +Logger* Logger::get_instance() { + if (m_instance == nullptr) { + // The logger has not been initialized yet + // throw an exception + throw std::runtime_error("Attempted to get the logger instance before it was initialized"); + } + return m_instance; +} + +Logger* Logger::initialize(std::ostream *output_stream, LogLevel level) { + if (m_instance == nullptr) { + m_instance = new Logger(output_stream, level); + } else { + m_instance->log(LogLevel::WARNING, "The logger has already been initialized"); + } + return m_instance; +} + +void Logger::destroy() { + if (m_instance != nullptr) { + delete m_instance; + m_instance = nullptr; + } +} + Logger::Logger(std::ostream *output_stream, LogLevel level) { this->m_log_level = level; this->m_output_stream = output_stream; diff --git a/Utils/Logger/Logger.h b/Utils/Logger/Logger.h index bdee1fb..637b641 100644 --- a/Utils/Logger/Logger.h +++ b/Utils/Logger/Logger.h @@ -9,8 +9,6 @@ #include class Logger { -private: - std::ostream *m_output_stream; public: enum LogLevel { @@ -21,11 +19,24 @@ class Logger { FATAL }; - Logger(std::ostream* output_stream, LogLevel log_level); + Logger(const Logger&) = delete; + virtual ~Logger() = default; + Logger& operator=(const Logger&) = delete; + + static Logger* initialize(std::ostream *output_stream, LogLevel level); + static Logger* get_instance(); + static void destroy(); + virtual void log(LogLevel log_level, std::string message); +protected: + Logger(std::ostream *output_stream, LogLevel level); + static Logger* m_instance; + private: + LogLevel m_log_level; + std::ostream *m_output_stream; std::mutex m_log_mutex; public: diff --git a/main.cpp b/main.cpp index 8cc73bc..821b59a 100644 --- a/main.cpp +++ b/main.cpp @@ -25,45 +25,48 @@ int main(int argc, char** argv) { Configuration configuration = Configuration(argc == 2 ? argv[1] : "config.json"); // Create a log controller - Logger logger = Logger(&std::cout, logger.string_to_log_level(configuration.log_level)); - logger.log(logger.INFO, "Logger initialized... Starting server..."); + Logger* logger = Logger::initialize(&std::cout, Logger::string_to_log_level(configuration.log_level)); + logger->log(Logger::INFO, "Logger initialized... Starting server..."); // Print the configuration model - logger.log(logger.DEBUG, configuration.toString()); + logger->log(Logger::DEBUG, configuration.toString()); // Initialize a vector of threads to track all the threads we create std::vector threads; // Initialize the main server socket - ServerSocket server_socket = ServerSocket(&configuration, &logger); + ServerSocket server_socket = ServerSocket(&configuration, logger); // Start the client socket handler threads - Sessions sessions = Sessions(&configuration, &logger); + Sessions sessions = Sessions(&configuration, logger); // Create server listener threads for (uint32_t i = 0; i < configuration.connection_establishment_handler_thread_count; i++) { - threads.emplace_back(std::thread(&ServerListener::start, new ServerListener(&logger, &server_socket, &configuration, &sessions))); + threads.emplace_back(std::thread(&ServerListener::start, new ServerListener(logger, &server_socket, &configuration, &sessions))); } // Setup Master Clock - MasterClock master_clock = MasterClock(&logger, configuration.player_session_socket_handler_thread_count, configuration.game_loop_thread_count); + MasterClock master_clock = MasterClock(logger, configuration.player_session_socket_handler_thread_count, configuration.game_loop_thread_count); // Start session handler threads for (uint32_t i = 0; i < configuration.player_session_socket_handler_thread_count; i++) { - threads.emplace_back(std::thread(&SessionHandler::start, new SessionHandler(&logger, &configuration, &master_clock, &sessions, i))); + threads.emplace_back(std::thread(&SessionHandler::start, new SessionHandler(logger, &configuration, &master_clock, &sessions, i))); } // Start game loop threads for (uint32_t i = 0; i < configuration.game_loop_thread_count; i++) { - threads.emplace_back(std::thread(&GameLoop::start, new GameLoop(&logger, &configuration, &master_clock, &sessions, i))); + threads.emplace_back(std::thread(&GameLoop::start, new GameLoop(logger, &configuration, &master_clock, &sessions, i))); } - logger.log(logger.INFO, "Server started successfully!"); + logger->log(Logger::INFO, "Server started successfully!"); // Join threads for (auto& thread : threads) { thread.join(); } + // Destroy the logger + Logger::destroy(); + return 0; } From a4110942c090716dafe7f9d7526845046704c94d Mon Sep 17 00:00:00 2001 From: Broseki Date: Sun, 5 Jan 2025 14:23:34 -0500 Subject: [PATCH 2/3] Switched configuration to be a singleton class --- Session/Session.cpp | 1 - Tests/Session/Sessions_test.cpp | 68 ++++++++----------- .../Configuration/Configuration_test.cpp | 39 +++++++---- Tests/Utils/Logger/Logger_test.cpp | 12 +++- Utils/Configuration/Configuration.cpp | 27 ++++++++ Utils/Configuration/Configuration.h | 10 ++- main.cpp | 24 +++---- 7 files changed, 112 insertions(+), 69 deletions(-) diff --git a/Session/Session.cpp b/Session/Session.cpp index 87fedf3..5363daf 100644 --- a/Session/Session.cpp +++ b/Session/Session.cpp @@ -8,7 +8,6 @@ #include "KV_Keys.h" Session::Session(int socketfd) { - std::cout << "Creating session with FD: " << socketfd << std::endl; this->socketfd = socketfd; } diff --git a/Tests/Session/Sessions_test.cpp b/Tests/Session/Sessions_test.cpp index 06b0982..f9d3cb7 100644 --- a/Tests/Session/Sessions_test.cpp +++ b/Tests/Session/Sessions_test.cpp @@ -6,41 +6,12 @@ #include "../../Session/Sessions.h" #include - -class MockLogger : public Logger { -public: - static MockLogger* get_instance() { - if (m_instance == nullptr) { - m_instance = new MockLogger(&std::cout, Logger::DEBUG); - } - return m_instance; - } - - static void reset() { - if (m_instance != nullptr) { - delete m_instance; - m_instance = nullptr; - } - } - - MOCK_METHOD(void, log, (LogLevel level, std::string message), (override)); - -protected: - MockLogger(std::ostream* output_stream, LogLevel level) : Logger(output_stream, level) {} - -private: - static MockLogger* m_instance; -}; - -// Initialize the static member -MockLogger* MockLogger::m_instance = nullptr; - class SessionsTest : public ::testing::Test { protected: - SessionsTest() : configuration("test_config.json"), logger(MockLogger::get_instance()), sessions(&configuration, logger) {} + SessionsTest() : logger(Logger::initialize(&std::cout, Logger::INFO)), configuration(Configuration::initialize("test_config.json")), sessions(configuration, logger) {} - Configuration configuration; - MockLogger* logger; + Logger* logger; + Configuration* configuration; Sessions sessions; public: @@ -50,7 +21,7 @@ class SessionsTest : public ::testing::Test { } static void TearDownTestSuite() { - MockLogger::reset(); + Logger::destroy(); } }; @@ -147,21 +118,36 @@ TEST_F(SessionsTest, RemoveNonExistentSession) { EXPECT_EQ(sessions.getSessions(0, 1)[0], session); } + TEST_F(SessionsTest, Destructor) { - // Expect the logger to be called with the correct message - EXPECT_CALL(*logger, log(Logger::INFO, "Destroying Sessions object")).Times(1); + std::vector> weak_refs; + { + Sessions *tmp_sessions = new Sessions(configuration, logger); - Sessions *tmp_sessions = new Sessions(&configuration, logger); + // Store weak references to track Session lifetime + auto session1 = std::make_shared(0); + auto session2 = std::make_shared(0); + auto session3 = std::make_shared(0); - tmp_sessions->addSession(std::make_shared(0)); - tmp_sessions->addSession(std::make_shared(0)); - tmp_sessions->addSession(std::make_shared(0)); + weak_refs.push_back(session1); + weak_refs.push_back(session2); + weak_refs.push_back(session3); - delete tmp_sessions; + tmp_sessions->addSession(session1); + tmp_sessions->addSession(session2); + tmp_sessions->addSession(session3); + + delete tmp_sessions; + } + + // Verify all sessions were destroyed + for (const auto& weak_ref : weak_refs) { + EXPECT_TRUE(weak_ref.expired()) << "Session was not properly destroyed"; + } } TEST_F(SessionsTest, AddSessionFull) { - for (uint32_t i = 0; i < configuration.max_players; i++) { + for (uint32_t i = 0; i < configuration->max_players; i++) { sessions.addSession(std::make_shared(i)); } diff --git a/Tests/Utils/Configuration/Configuration_test.cpp b/Tests/Utils/Configuration/Configuration_test.cpp index 42909ea..f1f9170 100644 --- a/Tests/Utils/Configuration/Configuration_test.cpp +++ b/Tests/Utils/Configuration/Configuration_test.cpp @@ -1,32 +1,40 @@ #include "gtest/gtest.h" #include "../../../Utils/Configuration/Configuration.h" +#include "../../../Utils/Logger/Logger.h" + class ConfigurationTest : public ::testing::Test { protected: - ConfigurationTest() : configuration("test_config.json") {} - Configuration configuration; + ConfigurationTest() : logger(Logger::initialize(&std::cout, Logger::ERROR)), configuration(Configuration::initialize("test_config.json")) {} + Logger* logger; + Configuration* configuration; + + static void TearDownTestSuite() { + Configuration::destroy(); + Logger::destroy(); + } }; TEST_F(ConfigurationTest, ThreadSettings) { - EXPECT_EQ(configuration.connection_establishment_handler_thread_count, 43432); - EXPECT_EQ(configuration.player_session_socket_handler_thread_count, 5452); - EXPECT_EQ(configuration.game_loop_thread_count, 3425); + EXPECT_EQ(configuration->connection_establishment_handler_thread_count, 43432); + EXPECT_EQ(configuration->player_session_socket_handler_thread_count, 5452); + EXPECT_EQ(configuration->game_loop_thread_count, 3425); } TEST_F(ConfigurationTest, NetworkingSettings) { - EXPECT_EQ(configuration.game_port, 11111); - EXPECT_EQ(configuration.connection_queue_size, 5); - EXPECT_EQ(configuration.socket_buffer_size, 1000); + EXPECT_EQ(configuration->game_port, 11111); + EXPECT_EQ(configuration->connection_queue_size, 5); + EXPECT_EQ(configuration->socket_buffer_size, 1000); } TEST_F(ConfigurationTest, GameSettings) { - EXPECT_EQ(configuration.max_players, 10); - EXPECT_EQ(configuration.world_id, 112); - EXPECT_EQ(configuration.tick_rate, 12); + EXPECT_EQ(configuration->max_players, 10); + EXPECT_EQ(configuration->world_id, 112); + EXPECT_EQ(configuration->tick_rate, 12); } TEST_F(ConfigurationTest, SystemSettings) { - EXPECT_EQ(configuration.log_level, "debug"); + EXPECT_EQ(configuration->log_level, "debug"); } TEST_F(ConfigurationTest, ToString) { @@ -47,5 +55,10 @@ TEST_F(ConfigurationTest, ToString) { "\n System Settings:" "\n log_level: debug"; - EXPECT_EQ(configuration.toString(), expected_output); + EXPECT_EQ(configuration->toString(), expected_output); +} + +TEST_F(ConfigurationTest, GetInstance) { + Configuration* instance = Configuration::get_instance(); + EXPECT_EQ(instance, configuration); } \ No newline at end of file diff --git a/Tests/Utils/Logger/Logger_test.cpp b/Tests/Utils/Logger/Logger_test.cpp index 91e7fbf..deed870 100644 --- a/Tests/Utils/Logger/Logger_test.cpp +++ b/Tests/Utils/Logger/Logger_test.cpp @@ -7,7 +7,6 @@ using namespace testing; class LoggerTest : public Test { public: std::ostringstream output_stream; - }; TEST_F(LoggerTest, LogLevelToString) { @@ -293,6 +292,17 @@ TEST_F(LoggerTest, DuplicateInitializeCall) { // We should get a warning message EXPECT_EQ(output_stream.str().find("[WARNING - "), 0); + // Tear down + Logger::destroy(); +} + +TEST_F(LoggerTest, GetInstance) { + // Setup + Logger *logger = Logger::initialize(&output_stream, Logger::DEBUG); + + // Test + EXPECT_EQ(Logger::get_instance(), logger); + // Tear down Logger::destroy(); } \ No newline at end of file diff --git a/Utils/Configuration/Configuration.cpp b/Utils/Configuration/Configuration.cpp index 47aeb47..f153999 100644 --- a/Utils/Configuration/Configuration.cpp +++ b/Utils/Configuration/Configuration.cpp @@ -5,9 +5,13 @@ #include "Configuration.h" #include + +#include "../Logger/Logger.h" #include "nlohmann/json.hpp" using json = nlohmann::json; +Configuration* Configuration::m_instance = nullptr; + Configuration::Configuration(std::string configuration_file_path) { // Get the configuration data from the file std::ifstream f(configuration_file_path); @@ -60,3 +64,26 @@ std::string Configuration::toString() const { std::string("\n System Settings:") + std::string("\n log_level: ") + log_level; } + +Configuration* Configuration::initialize(std::string configuration_file_path) { + if (m_instance == nullptr) { + m_instance = new Configuration(configuration_file_path); + } else { + Logger::get_instance()->log(Logger::WARNING, "Configuration already initialized"); + } + return m_instance; +} + +Configuration* Configuration::get_instance() { + if (m_instance == nullptr) { + throw std::runtime_error("Attempted to get the configuration instance before it was initialized"); + } + return m_instance; +} + +void Configuration::destroy() { + if (m_instance != nullptr) { + delete m_instance; + m_instance = nullptr; + } +} \ No newline at end of file diff --git a/Utils/Configuration/Configuration.h b/Utils/Configuration/Configuration.h index c5e19f6..6642c4b 100644 --- a/Utils/Configuration/Configuration.h +++ b/Utils/Configuration/Configuration.h @@ -11,11 +11,19 @@ class Configuration { public: - Configuration(std::string configuration_file_path); + static Configuration* initialize(std::string configuration_file_path); + static Configuration* get_instance(); + static void destroy(); // String representation of the configuration model std::string toString() const; +private: + static Configuration* m_instance; + +protected: + Configuration(std::string configuration_file_path); + public: // Thread settings uint32_t connection_establishment_handler_thread_count; diff --git a/main.cpp b/main.cpp index 821b59a..fb5c934 100644 --- a/main.cpp +++ b/main.cpp @@ -22,40 +22,40 @@ int main(int argc, char** argv) { std::cout << "Usage: " << argv[0] << " [config_file]" << std::endl; return 1; } - Configuration configuration = Configuration(argc == 2 ? argv[1] : "config.json"); + Configuration* configuration = Configuration::initialize(argc == 2 ? argv[1] : "config.json"); // Create a log controller - Logger* logger = Logger::initialize(&std::cout, Logger::string_to_log_level(configuration.log_level)); + Logger* logger = Logger::initialize(&std::cout, Logger::string_to_log_level(configuration->log_level)); logger->log(Logger::INFO, "Logger initialized... Starting server..."); // Print the configuration model - logger->log(Logger::DEBUG, configuration.toString()); + logger->log(Logger::DEBUG, configuration->toString()); // Initialize a vector of threads to track all the threads we create std::vector threads; // Initialize the main server socket - ServerSocket server_socket = ServerSocket(&configuration, logger); + ServerSocket server_socket = ServerSocket(configuration, logger); // Start the client socket handler threads - Sessions sessions = Sessions(&configuration, logger); + Sessions sessions = Sessions(configuration, logger); // Create server listener threads - for (uint32_t i = 0; i < configuration.connection_establishment_handler_thread_count; i++) { - threads.emplace_back(std::thread(&ServerListener::start, new ServerListener(logger, &server_socket, &configuration, &sessions))); + for (uint32_t i = 0; i < configuration->connection_establishment_handler_thread_count; i++) { + threads.emplace_back(std::thread(&ServerListener::start, new ServerListener(logger, &server_socket, configuration, &sessions))); } // Setup Master Clock - MasterClock master_clock = MasterClock(logger, configuration.player_session_socket_handler_thread_count, configuration.game_loop_thread_count); + MasterClock master_clock = MasterClock(logger, configuration->player_session_socket_handler_thread_count, configuration->game_loop_thread_count); // Start session handler threads - for (uint32_t i = 0; i < configuration.player_session_socket_handler_thread_count; i++) { - threads.emplace_back(std::thread(&SessionHandler::start, new SessionHandler(logger, &configuration, &master_clock, &sessions, i))); + for (uint32_t i = 0; i < configuration->player_session_socket_handler_thread_count; i++) { + threads.emplace_back(std::thread(&SessionHandler::start, new SessionHandler(logger, configuration, &master_clock, &sessions, i))); } // Start game loop threads - for (uint32_t i = 0; i < configuration.game_loop_thread_count; i++) { - threads.emplace_back(std::thread(&GameLoop::start, new GameLoop(logger, &configuration, &master_clock, &sessions, i))); + for (uint32_t i = 0; i < configuration->game_loop_thread_count; i++) { + threads.emplace_back(std::thread(&GameLoop::start, new GameLoop(logger, configuration, &master_clock, &sessions, i))); } logger->log(Logger::INFO, "Server started successfully!"); From 5a5ccba5be7df052806f698ec2f2151cc03f97f9 Mon Sep 17 00:00:00 2001 From: Broseki Date: Sun, 5 Jan 2025 14:25:42 -0500 Subject: [PATCH 3/3] Cleaned up changes for singletons --- Session/Sessions.cpp | 1 - main.cpp | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Session/Sessions.cpp b/Session/Sessions.cpp index 5efc4f6..4cafd4a 100644 --- a/Session/Sessions.cpp +++ b/Session/Sessions.cpp @@ -5,7 +5,6 @@ #include "Sessions.h" #include #include -#include Sessions::Sessions(Configuration *config, Logger *logger) { this->config = config; diff --git a/main.cpp b/main.cpp index fb5c934..1bca099 100644 --- a/main.cpp +++ b/main.cpp @@ -65,7 +65,10 @@ int main(int argc, char** argv) { thread.join(); } - // Destroy the logger + // Destroy the configuration + Configuration::destroy(); + + // Destroy the logger (last) Logger::destroy(); return 0;