345 lines
11 KiB
C++
345 lines
11 KiB
C++
#include "spdlogconfig/SPDLOGConfig.h"
|
|
#include "spdlog/spdlog.h"
|
|
|
|
#include <doctest/doctest.h>
|
|
|
|
#include <filesystem>
|
|
#include <memory>
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 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<spdlog::sinks::basic_file_sink_st> file_out_sink;
|
|
std::shared_ptr<spdlog::sinks::basic_file_sink_mt> file_err_sink;
|
|
|
|
for (auto &s : sinks) {
|
|
auto as_st =
|
|
std::dynamic_pointer_cast<spdlog::sinks::basic_file_sink_st>(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<spdlog::sinks::basic_file_sink_mt>(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<spdlog::sinks::rotating_file_sink_st> rotate_out_sink;
|
|
std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> rotate_err_sink;
|
|
|
|
for (auto &s : sinks) {
|
|
auto as_st =
|
|
std::dynamic_pointer_cast<spdlog::sinks::rotating_file_sink_st>(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<spdlog::sinks::rotating_file_sink_mt>(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<spdlog::sinks::daily_file_sink_st>(s)) {
|
|
has_daily_st = true;
|
|
}
|
|
if (std::dynamic_pointer_cast<spdlog::sinks::daily_file_sink_mt>(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<spdlog::sinks::null_sink_st>(s)) {
|
|
has_null_st = true;
|
|
}
|
|
if (std::dynamic_pointer_cast<spdlog::sinks::null_sink_mt>(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<spdlog::sinks::syslog_sink_st *>(s.get())) {
|
|
has_syslog_st = true;
|
|
}
|
|
if (dynamic_cast<spdlog::sinks::syslog_sink_mt *>(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<spdlog::sinks::msvc_sink_mt *>(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<spdlog::sinks::stdout_sink_st>(s) ||
|
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_sink_mt>(s) ||
|
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_color_sink_st>(s) ||
|
|
std::dynamic_pointer_cast<spdlog::sinks::stdout_color_sink_mt>(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<spdlog::async_logger>(global_async);
|
|
auto as_local_async =
|
|
std::dynamic_pointer_cast<spdlog::async_logger>(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<std::size_t>(0));
|
|
CHECK(GetMaxSizeInt("42") == static_cast<std::size_t>(42));
|
|
CHECK(GetMaxSizeInt("10485760") == static_cast<std::size_t>(10485760));
|
|
}
|
|
|
|
TEST_CASE("GetMaxSizeInt with K/M/G/T suffix") {
|
|
CHECK(GetMaxSizeInt("1K") == static_cast<std::size_t>(1024));
|
|
CHECK(GetMaxSizeInt("2M") == static_cast<std::size_t>(2 * 1024 * 1024));
|
|
CHECK(GetMaxSizeInt("3G") ==
|
|
static_cast<std::size_t>(3ull * 1024 * 1024 * 1024));
|
|
CHECK(GetMaxSizeInt("1T") ==
|
|
static_cast<std::size_t>(1ull * 1024 * 1024 * 1024 * 1024));
|
|
}
|
|
|
|
TEST_CASE("GetMaxSizeInt ignores whitespace around the number") {
|
|
CHECK(GetMaxSizeInt(" 1K") == static_cast<std::size_t>(1024));
|
|
CHECK(GetMaxSizeInt("2M ") == static_cast<std::size_t>(2 * 1024 * 1024));
|
|
}
|
|
}
|