#include "spdlogconfig/SPDLOGConfig.h" #include "spdlog/spdlog.h" #include #include #include // ----------------------------------------------------------------------------- // Test helpers // ----------------------------------------------------------------------------- namespace { /** * Build the path to `full.json` relative to this source file. * * Layout assumption: * - This file lives in: .../test/source/spdlogconfig.cpp * - `full.json` lives in: .../test/full.json * * @return Absolute/relative path string to `full.json`. */ inline std::string full_json_path() { namespace fs = std::filesystem; static const fs::path base = fs::path(__FILE__).parent_path().parent_path(); // .../test return (base / "full.json").string(); } /** * Common setup for tests that require a fully initialized spdlog configuration. * * Drops all existing loggers first to keep test cases independent. */ inline void setup_spdlog_from_full_json() { spdlog::drop_all(); spdlogconfig::setupFrom(full_json_path()); } } // namespace // ----------------------------------------------------------------------------- // Tests for full.json // ----------------------------------------------------------------------------- TEST_SUITE("Tests for full.json") { TEST_CASE("full Setup") { // Ensure this test is independent from others. spdlog::drop_all(); SUBCASE("No loggers should be created yet.") { CHECK(spdlog::get("root") == nullptr); CHECK(spdlog::get("console") == nullptr); CHECK(spdlog::get("global_async") == nullptr); CHECK(spdlog::get("local_async") == nullptr); } // Now load configuration. spdlogconfig::setupFrom(full_json_path()); SUBCASE("All loggers should be created.") { auto root = spdlog::get("root"); auto console = spdlog::get("console"); auto global_async = spdlog::get("global_async"); auto local_async = spdlog::get("local_async"); CHECK(root != nullptr); CHECK(console != nullptr); CHECK(global_async != nullptr); CHECK(local_async != nullptr); } } TEST_CASE("full Root logger") { setup_spdlog_from_full_json(); auto logger = spdlog::get("root"); REQUIRE(logger != nullptr); // avoid segfault SUBCASE("Root logger should have expected sinks") { // JSON defines 16 sinks, but 2 are platform-specific (syslog or msvc) // and may not be created -> effectively 14 sinks. CHECK(logger->sinks().size() == 14); } SUBCASE("Root logger's level should be trace.") { CHECK(logger->level() == spdlog::level::level_enum::trace); } } TEST_CASE("Console logger") { setup_spdlog_from_full_json(); auto logger_console = spdlog::get("console"); REQUIRE(logger_console != nullptr); SUBCASE("Console logger should have two sinks.") { CHECK(logger_console->sinks().size() == 2); } } TEST_CASE("Global async logger") { setup_spdlog_from_full_json(); auto logger_global_async = spdlog::get("global_async"); REQUIRE(logger_global_async != nullptr); SUBCASE("Global async logger should have only one sink.") { CHECK(logger_global_async->sinks().size() == 1); } } TEST_CASE("Local async logger") { setup_spdlog_from_full_json(); auto logger_local_async = spdlog::get("local_async"); REQUIRE(logger_local_async != nullptr); SUBCASE("Local async logger should have only one sink.") { CHECK(logger_local_async->sinks().size() == 1); } } // --------------------------------------------------------------------------- // Additional tests: root logger sinks / file sinks / platform sinks // --------------------------------------------------------------------------- TEST_CASE("Root logger sink configuration") { setup_spdlog_from_full_json(); auto logger = spdlog::get("root"); REQUIRE(logger != nullptr); auto &sinks = logger->sinks(); SUBCASE("basic_file_sinks have correct filenames and levels") { std::shared_ptr file_out_sink; std::shared_ptr file_err_sink; for (auto &s : sinks) { auto as_st = std::dynamic_pointer_cast(s); if (as_st && as_st->filename() == SPDLOG_FILENAME_T("log/spdlog_setup.log")) { file_out_sink = as_st; } auto as_mt = std::dynamic_pointer_cast(s); if (as_mt && as_mt->filename() == SPDLOG_FILENAME_T("log/spdlog_setup_err.log")) { file_err_sink = as_mt; } } CHECK(file_out_sink != nullptr); CHECK(file_err_sink != nullptr); if (file_out_sink) { CHECK(file_out_sink->level() == spdlog::level::info); } if (file_err_sink) { CHECK(file_err_sink->level() == spdlog::level::err); } // create_parent_dir=true -> "log" should exist CHECK(std::filesystem::exists("log")); CHECK(std::filesystem::is_directory("log")); } SUBCASE("rotating_file_sinks have correct configuration") { std::shared_ptr rotate_out_sink; std::shared_ptr rotate_err_sink; for (auto &s : sinks) { auto as_st = std::dynamic_pointer_cast(s); if (as_st && as_st->filename() == SPDLOG_FILENAME_T("log/rotate_spdlog_setup.log")) { rotate_out_sink = as_st; } auto as_mt = std::dynamic_pointer_cast(s); if (as_mt && as_mt->filename() == SPDLOG_FILENAME_T("log/rotate_spdlog_setup_err.log")) { rotate_err_sink = as_mt; } } CHECK(rotate_out_sink != nullptr); CHECK(rotate_err_sink != nullptr); const auto expected_size = spdlogconfig::utils::GetMaxSizeInt("1M"); // from JSON const std::size_t expected_files = 10; if (rotate_out_sink) { CHECK(rotate_out_sink->get_max_size() == expected_size); CHECK(rotate_out_sink->get_max_files() == expected_files); CHECK(rotate_out_sink->level() == spdlog::level::info); } if (rotate_err_sink) { CHECK(rotate_err_sink->get_max_size() == expected_size); CHECK(rotate_err_sink->get_max_files() == expected_files); CHECK(rotate_err_sink->level() == spdlog::level::err); } } SUBCASE("Root logger has daily-file sinks") { bool has_daily_st = false; bool has_daily_mt = false; for (auto &s : sinks) { if (std::dynamic_pointer_cast(s)) { has_daily_st = true; } if (std::dynamic_pointer_cast(s)) { has_daily_mt = true; } } CHECK(has_daily_st); CHECK(has_daily_mt); } SUBCASE("Null sinks exist") { bool has_null_st = false; bool has_null_mt = false; for (auto &s : sinks) { if (std::dynamic_pointer_cast(s)) { has_null_st = true; } if (std::dynamic_pointer_cast(s)) { has_null_mt = true; } } CHECK(has_null_st); CHECK(has_null_mt); } SUBCASE("Platform-specific sinks exist") { #ifndef _WIN32 bool has_syslog_st = false; bool has_syslog_mt = false; for (auto &s : sinks) { if (dynamic_cast(s.get())) { has_syslog_st = true; } if (dynamic_cast(s.get())) { has_syslog_mt = true; } } CHECK(has_syslog_st); CHECK(has_syslog_mt); #else bool has_msvc_mt = false; for (auto &s : sinks) { if (dynamic_cast(s.get())) { has_msvc_mt = true; } } CHECK(has_msvc_mt); #endif } SUBCASE("Console sinks inherit the logger level trace") { for (auto &s : sinks) { if (std::dynamic_pointer_cast(s) || std::dynamic_pointer_cast(s) || std::dynamic_pointer_cast(s) || std::dynamic_pointer_cast(s)) { CHECK(s->level() == spdlog::level::trace); } } } } // --------------------------------------------------------------------------- // Additional tests: async logger & thread pool // --------------------------------------------------------------------------- TEST_CASE("Async logger configuration and thread pool") { setup_spdlog_from_full_json(); SUBCASE("Global thread pool is initialized") { auto tp = spdlog::thread_pool(); CHECK(tp != nullptr); } SUBCASE("global_async and local_async are async_logger") { auto global_async = spdlog::get("global_async"); auto local_async = spdlog::get("local_async"); REQUIRE(global_async != nullptr); REQUIRE(local_async != nullptr); auto as_global_async = std::dynamic_pointer_cast(global_async); auto as_local_async = std::dynamic_pointer_cast(local_async); CHECK(as_global_async != nullptr); CHECK(as_local_async != nullptr); } } } // ----------------------------------------------------------------------------- // Additional tests: utility functions // ----------------------------------------------------------------------------- TEST_SUITE("spdlogconfig::utils") { using spdlogconfig::utils::GetMaxSizeInt; TEST_CASE("GetMaxSizeInt without suffix") { CHECK(GetMaxSizeInt("0") == static_cast(0)); CHECK(GetMaxSizeInt("42") == static_cast(42)); CHECK(GetMaxSizeInt("10485760") == static_cast(10485760)); } TEST_CASE("GetMaxSizeInt with K/M/G/T suffix") { CHECK(GetMaxSizeInt("1K") == static_cast(1024)); CHECK(GetMaxSizeInt("2M") == static_cast(2 * 1024 * 1024)); CHECK(GetMaxSizeInt("3G") == static_cast(3ull * 1024 * 1024 * 1024)); CHECK(GetMaxSizeInt("1T") == static_cast(1ull * 1024 * 1024 * 1024 * 1024)); } TEST_CASE("GetMaxSizeInt ignores whitespace around the number") { CHECK(GetMaxSizeInt(" 1K") == static_cast(1024)); CHECK(GetMaxSizeInt("2M ") == static_cast(2 * 1024 * 1024)); } }