Update .gitignore, CMakeLists.txt, CMakePresets.json, and 10 more files
This commit is contained in:
1 parent
88ff181bfd
commit
1f58c33bb7
13 files changed
+3380
No files matched your search
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user