Update .gitignore, CMakeLists.txt, CMakePresets.json, and 10 more files

This commit is contained in:
random committed 2026-09-15 08:32:55 +02:00
1 parent 88ff181bfd
commit 1f58c33bb7
13 files changed
+3380

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(SPDLOGConfigTests LANGUAGES CXX)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
include(CTest)
# ---- Dependencies ----
CPMAddPackage("gh:doctest/doctest@2.5.2")
file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp)
add_executable(${PROJECT_NAME} ${sources})
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20)
target_link_libraries(${PROJECT_NAME} PRIVATE doctest::doctest SPDLOGConfig::SPDLOGConfig)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wpedantic -Wextra)
elseif(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
target_compile_definitions(${PROJECT_NAME} PRIVATE DOCTEST_CONFIG_USE_STD_HEADERS)
endif()
# ---- Register doctest test cases with CTest ----
include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake)
doctest_discover_tests(${PROJECT_NAME})
+159
View File
@@ -0,0 +1,159 @@
{
"global_pattern": "[%Y-%m-%dT%T%z] [%L] <%n>: %v",
"logger": [
{
"level": "trace",
"name": "root",
"sinks": [
"console_st",
"console_mt",
"color_console_st",
"color_console_mt",
"daily_out",
"daily_err",
"file_out",
"file_err",
"rotate_out",
"rotate_err",
"null_sink_st",
"null_sink_mt",
"syslog_st",
"syslog_mt",
"msvc_sink_st",
"msvc_sink_mt"
]
},
{
"level": "trace",
"name": "console",
"pattern": "succient",
"sinks": [
"console_st",
"console_mt"
]
},
{
"level": "trace",
"name": "global_async",
"pattern": "succient",
"sinks": [
"console_mt"
],
"type": "async"
},
{
"level": "trace",
"name": "local_async",
"overflow_policy": "overrun_oldest",
"pattern": "succient",
"sinks": [
"console_mt"
],
"thread_pool": "tp",
"type": "async"
}
],
"pattern": [
{
"name": "succient",
"value": "%c-%L: %v"
}
],
"sink": [
{
"name": "console_st",
"type": "stdout_sink_st"
},
{
"name": "console_mt",
"type": "stdout_sink_mt"
},
{
"name": "color_console_st",
"type": "stdout_color_sink_st"
},
{
"name": "color_console_mt",
"type": "stdout_color_sink_mt"
},
{
"create_parent_dir": true,
"filename": "log/spdlog_setup.log",
"level": "info",
"name": "file_out",
"type": "basic_file_sink_st"
},
{
"filename": "log/spdlog_setup_err.log",
"level": "err",
"name": "file_err",
"truncate": true,
"type": "basic_file_sink_mt"
},
{
"base_filename": "log/rotate_spdlog_setup.log",
"level": "info",
"max_files": 10,
"max_size": "1M",
"name": "rotate_out",
"type": "rotating_file_sink_st"
},
{
"base_filename": "log/rotate_spdlog_setup_err.log",
"level": "err",
"max_files": 10,
"max_size": "1M",
"name": "rotate_err",
"type": "rotating_file_sink_mt"
},
{
"base_filename": "log/daily_spdlog_setup.log",
"level": "info",
"name": "daily_out",
"rotation_time": "17:30",
"type": "daily_file_sink_st"
},
{
"base_filename": "log/daily_spdlog_setup_err.log",
"level": "err",
"name": "daily_err",
"rotation_time": "17:30",
"type": "daily_file_sink_mt"
},
{
"name": "null_sink_st",
"type": "null_sink_st"
},
{
"name": "null_sink_mt",
"type": "null_sink_mt"
},
{
"name": "syslog_st",
"type": "syslog_sink_st"
},
{
"name": "syslog_mt",
"type": "syslog_sink_mt"
},
{
"name": "msvc_sink_st",
"type": "msvc_sink_st"
},
{
"name": "msvc_sink_mt",
"type": "msvc_sink_mt"
}
],
"thread_pools": [
{
"name": "tp",
"num_threads": 2,
"queue_size": 4096
}
],
"global_thread_pool": {
"num_threads": 1,
"queue_size": 8192
}
}
+3
View File
@@ -0,0 +1,3 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
+344
View File
@@ -0,0 +1,344 @@
#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));
}
}