aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/spdlog/tests
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/spdlog/tests')
-rw-r--r--thirdparty/spdlog/tests/includes.h42
-rw-r--r--thirdparty/spdlog/tests/main.cpp10
-rw-r--r--thirdparty/spdlog/tests/test_async.cpp200
-rw-r--r--thirdparty/spdlog/tests/test_backtrace.cpp73
-rw-r--r--thirdparty/spdlog/tests/test_bin_to_hex.cpp97
-rw-r--r--thirdparty/spdlog/tests/test_cfg.cpp178
-rw-r--r--thirdparty/spdlog/tests/test_circular_q.cpp50
-rw-r--r--thirdparty/spdlog/tests/test_create_dir.cpp144
-rw-r--r--thirdparty/spdlog/tests/test_custom_callbacks.cpp37
-rw-r--r--thirdparty/spdlog/tests/test_daily_logger.cpp169
-rw-r--r--thirdparty/spdlog/tests/test_dup_filter.cpp83
-rw-r--r--thirdparty/spdlog/tests/test_errors.cpp112
-rw-r--r--thirdparty/spdlog/tests/test_eventlog.cpp75
-rw-r--r--thirdparty/spdlog/tests/test_file_helper.cpp169
-rw-r--r--thirdparty/spdlog/tests/test_file_logging.cpp187
-rw-r--r--thirdparty/spdlog/tests/test_fmt_helper.cpp82
-rw-r--r--thirdparty/spdlog/tests/test_macros.cpp53
-rw-r--r--thirdparty/spdlog/tests/test_misc.cpp224
-rw-r--r--thirdparty/spdlog/tests/test_mpmc_q.cpp114
-rw-r--r--thirdparty/spdlog/tests/test_pattern_formatter.cpp660
-rw-r--r--thirdparty/spdlog/tests/test_registry.cpp125
-rw-r--r--thirdparty/spdlog/tests/test_ringbuffer.cpp52
-rw-r--r--thirdparty/spdlog/tests/test_sink.h70
-rw-r--r--thirdparty/spdlog/tests/test_stdout_api.cpp90
-rw-r--r--thirdparty/spdlog/tests/test_stopwatch.cpp42
-rw-r--r--thirdparty/spdlog/tests/test_systemd.cpp14
-rw-r--r--thirdparty/spdlog/tests/test_time_point.cpp35
-rw-r--r--thirdparty/spdlog/tests/utils.cpp102
-rw-r--r--thirdparty/spdlog/tests/utils.h18
29 files changed, 3307 insertions, 0 deletions
diff --git a/thirdparty/spdlog/tests/includes.h b/thirdparty/spdlog/tests/includes.h
new file mode 100644
index 000000000..2e49a5cb7
--- /dev/null
+++ b/thirdparty/spdlog/tests/includes.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#if defined(__GNUC__) && __GNUC__ == 12
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12
+#endif
+#include <catch2/catch_all.hpp>
+#if defined(__GNUC__) && __GNUC__ == 12
+ #pragma GCC diagnostic pop
+#endif
+
+#include "utils.h"
+#include <chrono>
+#include <cstdio>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <iomanip>
+#include <stdlib.h>
+
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
+
+#include "spdlog/spdlog.h"
+#include "spdlog/async.h"
+#include "spdlog/details/fmt_helper.h"
+#include "spdlog/details/os.h"
+
+#ifndef SPDLOG_NO_TLS
+ #include "spdlog/mdc.h"
+#endif
+
+#include "spdlog/sinks/basic_file_sink.h"
+#include "spdlog/sinks/daily_file_sink.h"
+#include "spdlog/sinks/null_sink.h"
+#include "spdlog/sinks/ostream_sink.h"
+#include "spdlog/sinks/rotating_file_sink.h"
+#include "spdlog/sinks/stdout_color_sinks.h"
+#include "spdlog/sinks/msvc_sink.h"
+#include "spdlog/pattern_formatter.h"
diff --git a/thirdparty/spdlog/tests/main.cpp b/thirdparty/spdlog/tests/main.cpp
new file mode 100644
index 000000000..a4a4ff154
--- /dev/null
+++ b/thirdparty/spdlog/tests/main.cpp
@@ -0,0 +1,10 @@
+#if defined(__GNUC__) && __GNUC__ == 12
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12
+#endif
+
+#include <catch2/catch_all.hpp>
+
+#if defined(__GNUC__) && __GNUC__ == 12
+ #pragma GCC diagnostic pop
+#endif
diff --git a/thirdparty/spdlog/tests/test_async.cpp b/thirdparty/spdlog/tests/test_async.cpp
new file mode 100644
index 000000000..76fdd7c6b
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_async.cpp
@@ -0,0 +1,200 @@
+#include "includes.h"
+#include "spdlog/async.h"
+#include "spdlog/sinks/basic_file_sink.h"
+#include "test_sink.h"
+
+#define TEST_FILENAME "test_logs/async_test.log"
+
+TEST_CASE("basic async test ", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ size_t overrun_counter = 0;
+ size_t queue_size = 128;
+ size_t messages = 256;
+ {
+ auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
+ auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp,
+ spdlog::async_overflow_policy::block);
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message #{}", i);
+ }
+ logger->flush();
+ overrun_counter = tp->overrun_counter();
+ }
+ REQUIRE(test_sink->msg_counter() == messages);
+ REQUIRE(test_sink->flush_counter() == 1);
+ REQUIRE(overrun_counter == 0);
+}
+
+TEST_CASE("discard policy ", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ test_sink->set_delay(std::chrono::milliseconds(1));
+ size_t queue_size = 4;
+ size_t messages = 1024;
+
+ auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
+ auto logger = std::make_shared<spdlog::async_logger>(
+ "as", test_sink, tp, spdlog::async_overflow_policy::overrun_oldest);
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message");
+ }
+ REQUIRE(test_sink->msg_counter() < messages);
+ REQUIRE(tp->overrun_counter() > 0);
+}
+
+TEST_CASE("discard policy discard_new ", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ test_sink->set_delay(std::chrono::milliseconds(1));
+ size_t queue_size = 4;
+ size_t messages = 1024;
+
+ auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
+ auto logger = std::make_shared<spdlog::async_logger>(
+ "as", test_sink, tp, spdlog::async_overflow_policy::discard_new);
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message");
+ }
+ REQUIRE(test_sink->msg_counter() < messages);
+ REQUIRE(tp->discard_counter() > 0);
+}
+
+TEST_CASE("discard policy using factory ", "[async]") {
+ size_t queue_size = 4;
+ size_t messages = 1024;
+ spdlog::init_thread_pool(queue_size, 1);
+
+ auto logger = spdlog::create_async_nb<spdlog::sinks::test_sink_mt>("as2");
+ auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
+ test_sink->set_delay(std::chrono::milliseconds(3));
+
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message");
+ }
+
+ REQUIRE(test_sink->msg_counter() < messages);
+ spdlog::drop_all();
+}
+
+TEST_CASE("flush", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ size_t queue_size = 256;
+ size_t messages = 256;
+ {
+ auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
+ auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp,
+ spdlog::async_overflow_policy::block);
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message #{}", i);
+ }
+
+ logger->flush();
+ }
+ // std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ REQUIRE(test_sink->msg_counter() == messages);
+ REQUIRE(test_sink->flush_counter() == 1);
+}
+
+TEST_CASE("async periodic flush", "[async]") {
+ auto logger = spdlog::create_async<spdlog::sinks::test_sink_mt>("as");
+ auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
+
+ spdlog::flush_every(std::chrono::seconds(1));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1700));
+ REQUIRE(test_sink->flush_counter() == 1);
+ spdlog::flush_every(std::chrono::seconds(0));
+ spdlog::drop_all();
+}
+
+TEST_CASE("tp->wait_empty() ", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ test_sink->set_delay(std::chrono::milliseconds(5));
+ size_t messages = 100;
+
+ auto tp = std::make_shared<spdlog::details::thread_pool>(messages, 2);
+ auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp,
+ spdlog::async_overflow_policy::block);
+ for (size_t i = 0; i < messages; i++) {
+ logger->info("Hello message #{}", i);
+ }
+ logger->flush();
+ tp.reset();
+
+ REQUIRE(test_sink->msg_counter() == messages);
+ REQUIRE(test_sink->flush_counter() == 1);
+}
+
+TEST_CASE("multi threads", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ size_t queue_size = 128;
+ size_t messages = 256;
+ size_t n_threads = 10;
+ {
+ auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
+ auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp,
+ spdlog::async_overflow_policy::block);
+
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < n_threads; i++) {
+ threads.emplace_back([logger, messages] {
+ for (size_t j = 0; j < messages; j++) {
+ logger->info("Hello message #{}", j);
+ }
+ });
+ logger->flush();
+ }
+
+ for (auto &t : threads) {
+ t.join();
+ }
+ }
+
+ REQUIRE(test_sink->msg_counter() == messages * n_threads);
+ REQUIRE(test_sink->flush_counter() == n_threads);
+}
+
+TEST_CASE("to_file", "[async]") {
+ prepare_logdir();
+ size_t messages = 1024;
+ size_t tp_threads = 1;
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ {
+ auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
+ auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
+ auto logger =
+ std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));
+
+ for (size_t j = 0; j < messages; j++) {
+ logger->info("Hello message #{}", j);
+ }
+ }
+
+ require_message_count(TEST_FILENAME, messages);
+ auto contents = file_contents(TEST_FILENAME);
+ using spdlog::details::os::default_eol;
+ REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol)));
+}
+
+TEST_CASE("to_file multi-workers", "[async]") {
+ prepare_logdir();
+ size_t messages = 1024 * 10;
+ size_t tp_threads = 10;
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ {
+ auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
+ auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
+ auto logger =
+ std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));
+
+ for (size_t j = 0; j < messages; j++) {
+ logger->info("Hello message #{}", j);
+ }
+ }
+ require_message_count(TEST_FILENAME, messages);
+}
+
+TEST_CASE("bad_tp", "[async]") {
+ auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
+ std::shared_ptr<spdlog::details::thread_pool> const empty_tp;
+ auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, empty_tp);
+ logger->info("Please throw an exception");
+ REQUIRE(test_sink->msg_counter() == 0);
+}
diff --git a/thirdparty/spdlog/tests/test_backtrace.cpp b/thirdparty/spdlog/tests/test_backtrace.cpp
new file mode 100644
index 000000000..4d78c0c21
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_backtrace.cpp
@@ -0,0 +1,73 @@
+#include "includes.h"
+#include "test_sink.h"
+#include "spdlog/async.h"
+
+TEST_CASE("bactrace1", "[bactrace]") {
+ using spdlog::sinks::test_sink_st;
+ auto test_sink = std::make_shared<test_sink_st>();
+ size_t backtrace_size = 5;
+
+ spdlog::logger logger("test-backtrace", test_sink);
+ logger.set_pattern("%v");
+ logger.enable_backtrace(backtrace_size);
+
+ logger.info("info message");
+ for (int i = 0; i < 100; i++) logger.debug("debug message {}", i);
+
+ REQUIRE(test_sink->lines().size() == 1);
+ REQUIRE(test_sink->lines()[0] == "info message");
+
+ logger.dump_backtrace();
+ REQUIRE(test_sink->lines().size() == backtrace_size + 3);
+ REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************");
+ REQUIRE(test_sink->lines()[2] == "debug message 95");
+ REQUIRE(test_sink->lines()[3] == "debug message 96");
+ REQUIRE(test_sink->lines()[4] == "debug message 97");
+ REQUIRE(test_sink->lines()[5] == "debug message 98");
+ REQUIRE(test_sink->lines()[6] == "debug message 99");
+ REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************");
+}
+
+TEST_CASE("bactrace-empty", "[bactrace]") {
+ using spdlog::sinks::test_sink_st;
+ auto test_sink = std::make_shared<test_sink_st>();
+ size_t backtrace_size = 5;
+
+ spdlog::logger logger("test-backtrace", test_sink);
+ logger.set_pattern("%v");
+ logger.enable_backtrace(backtrace_size);
+ logger.dump_backtrace();
+ REQUIRE(test_sink->lines().size() == 0);
+}
+
+TEST_CASE("bactrace-async", "[bactrace]") {
+ using spdlog::sinks::test_sink_mt;
+ auto test_sink = std::make_shared<test_sink_mt>();
+ using spdlog::details::os::sleep_for_millis;
+
+ size_t backtrace_size = 5;
+
+ spdlog::init_thread_pool(120, 1);
+ auto logger = std::make_shared<spdlog::async_logger>("test-bactrace-async", test_sink,
+ spdlog::thread_pool());
+ logger->set_pattern("%v");
+ logger->enable_backtrace(backtrace_size);
+
+ logger->info("info message");
+ for (int i = 0; i < 100; i++) logger->debug("debug message {}", i);
+
+ sleep_for_millis(100);
+ REQUIRE(test_sink->lines().size() == 1);
+ REQUIRE(test_sink->lines()[0] == "info message");
+
+ logger->dump_backtrace();
+ sleep_for_millis(100); // give time for the async dump to complete
+ REQUIRE(test_sink->lines().size() == backtrace_size + 3);
+ REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************");
+ REQUIRE(test_sink->lines()[2] == "debug message 95");
+ REQUIRE(test_sink->lines()[3] == "debug message 96");
+ REQUIRE(test_sink->lines()[4] == "debug message 97");
+ REQUIRE(test_sink->lines()[5] == "debug message 98");
+ REQUIRE(test_sink->lines()[6] == "debug message 99");
+ REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************");
+}
diff --git a/thirdparty/spdlog/tests/test_bin_to_hex.cpp b/thirdparty/spdlog/tests/test_bin_to_hex.cpp
new file mode 100644
index 000000000..45fc9fa96
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_bin_to_hex.cpp
@@ -0,0 +1,97 @@
+#include "includes.h"
+#include "test_sink.h"
+#include "spdlog/fmt/bin_to_hex.h"
+
+TEST_CASE("to_hex", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
+ oss_logger.info("{}", spdlog::to_hex(v));
+
+ auto output = oss.str();
+ REQUIRE(ends_with(output,
+ "0000: 09 0a 0b 0c ff ff" + std::string(spdlog::details::os::default_eol)));
+}
+
+TEST_CASE("to_hex_upper", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
+ oss_logger.info("{:X}", spdlog::to_hex(v));
+
+ auto output = oss.str();
+ REQUIRE(ends_with(output,
+ "0000: 09 0A 0B 0C FF FF" + std::string(spdlog::details::os::default_eol)));
+}
+
+TEST_CASE("to_hex_no_delimiter", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
+ oss_logger.info("{:sX}", spdlog::to_hex(v));
+
+ auto output = oss.str();
+ REQUIRE(
+ ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol)));
+}
+
+TEST_CASE("to_hex_show_ascii", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff};
+ oss_logger.info("{:Xsa}", spdlog::to_hex(v, 8));
+
+ REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." +
+ std::string(spdlog::details::os::default_eol)));
+}
+
+TEST_CASE("to_hex_different_size_per_line", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff};
+
+ oss_logger.info("{:Xsa}", spdlog::to_hex(v, 10));
+ REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." +
+ std::string(spdlog::details::os::default_eol)));
+
+ oss_logger.info("{:Xs}", spdlog::to_hex(v, 10));
+ REQUIRE(ends_with(oss.str(),
+ "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol)));
+
+ oss_logger.info("{:Xsa}", spdlog::to_hex(v, 6));
+ REQUIRE(ends_with(
+ oss.str(), "0000: 090A0B410C4B ...A.K" + std::string(spdlog::details::os::default_eol) +
+ "0006: FFFF .." + std::string(spdlog::details::os::default_eol)));
+
+ oss_logger.info("{:Xs}", spdlog::to_hex(v, 6));
+ REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4B" +
+ std::string(spdlog::details::os::default_eol) + "0006: FFFF" +
+ std::string(spdlog::details::os::default_eol)));
+}
+
+TEST_CASE("to_hex_no_ascii", "[to_hex]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("oss", oss_sink);
+
+ std::vector<unsigned char> v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff};
+ oss_logger.info("{:Xs}", spdlog::to_hex(v, 8));
+
+ REQUIRE(ends_with(oss.str(),
+ "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol)));
+
+ oss_logger.info("{:Xsna}", spdlog::to_hex(v, 8));
+
+ REQUIRE(
+ ends_with(oss.str(), "090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol)));
+}
diff --git a/thirdparty/spdlog/tests/test_cfg.cpp b/thirdparty/spdlog/tests/test_cfg.cpp
new file mode 100644
index 000000000..7dec94b44
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_cfg.cpp
@@ -0,0 +1,178 @@
+
+#include "includes.h"
+#include "test_sink.h"
+
+#include <spdlog/cfg/env.h>
+#include <spdlog/cfg/argv.h>
+
+using spdlog::cfg::load_argv_levels;
+using spdlog::cfg::load_env_levels;
+using spdlog::sinks::test_sink_st;
+
+TEST_CASE("env", "[cfg]") {
+ spdlog::drop("l1");
+ auto l1 = spdlog::create<test_sink_st>("l1");
+#ifdef CATCH_PLATFORM_WINDOWS
+ _putenv_s("SPDLOG_LEVEL", "l1=warn");
+#else
+ setenv("SPDLOG_LEVEL", "l1=warn", 1);
+#endif
+ load_env_levels();
+ REQUIRE(l1->level() == spdlog::level::warn);
+
+#ifdef CATCH_PLATFORM_WINDOWS
+ _putenv_s("MYAPP_LEVEL", "l1=trace");
+#else
+ setenv("MYAPP_LEVEL", "l1=trace", 1);
+#endif
+ load_env_levels("MYAPP_LEVEL");
+ REQUIRE(l1->level() == spdlog::level::trace);
+
+ spdlog::set_default_logger(spdlog::create<test_sink_st>("cfg-default"));
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
+
+TEST_CASE("argv1", "[cfg]") {
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=warn"};
+ load_argv_levels(2, argv);
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ REQUIRE(l1->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
+
+TEST_CASE("argv2", "[cfg]") {
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=warn,trace"};
+ load_argv_levels(2, argv);
+ auto l1 = spdlog::create<test_sink_st>("l1");
+ REQUIRE(l1->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
+}
+
+TEST_CASE("argv3", "[cfg]") {
+ spdlog::set_level(spdlog::level::trace);
+
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"};
+ load_argv_levels(2, argv);
+ auto l1 = spdlog::create<test_sink_st>("l1");
+ REQUIRE(l1->level() == spdlog::level::trace);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
+}
+
+TEST_CASE("argv4", "[cfg]") {
+ spdlog::set_level(spdlog::level::info);
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
+ load_argv_levels(2, argv);
+ auto l1 = spdlog::create<test_sink_st>("l1");
+ REQUIRE(l1->level() == spdlog::level::info);
+}
+
+TEST_CASE("argv5", "[cfg]") {
+ spdlog::set_level(spdlog::level::info);
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
+ load_argv_levels(3, argv);
+ auto l1 = spdlog::create<test_sink_st>("l1");
+ REQUIRE(l1->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
+ spdlog::set_level(spdlog::level::info);
+}
+
+TEST_CASE("argv6", "[cfg]") {
+ spdlog::set_level(spdlog::level::err);
+ const char *argv[] = {""};
+ load_argv_levels(1, argv);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
+ spdlog::set_level(spdlog::level::info);
+}
+
+TEST_CASE("argv7", "[cfg]") {
+ spdlog::set_level(spdlog::level::err);
+ const char *argv[] = {""};
+ load_argv_levels(0, argv);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
+ spdlog::set_level(spdlog::level::info);
+}
+
+TEST_CASE("level-not-set-test1", "[cfg]") {
+ spdlog::drop("l1");
+ const char *argv[] = {"ignore", ""};
+ load_argv_levels(2, argv);
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ l1->set_level(spdlog::level::trace);
+ REQUIRE(l1->level() == spdlog::level::trace);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
+
+TEST_CASE("level-not-set-test2", "[cfg]") {
+ spdlog::drop("l1");
+ spdlog::drop("l2");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
+
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ l1->set_level(spdlog::level::warn);
+ auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
+ l2->set_level(spdlog::level::warn);
+
+ load_argv_levels(2, argv);
+
+ REQUIRE(l1->level() == spdlog::level::trace);
+ REQUIRE(l2->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
+
+TEST_CASE("level-not-set-test3", "[cfg]") {
+ spdlog::drop("l1");
+ spdlog::drop("l2");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
+
+ load_argv_levels(2, argv);
+
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
+
+ REQUIRE(l1->level() == spdlog::level::trace);
+ REQUIRE(l2->level() == spdlog::level::info);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
+
+TEST_CASE("level-not-set-test4", "[cfg]") {
+ spdlog::drop("l1");
+ spdlog::drop("l2");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"};
+
+ load_argv_levels(2, argv);
+
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
+
+ REQUIRE(l1->level() == spdlog::level::trace);
+ REQUIRE(l2->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
+}
+
+TEST_CASE("level-not-set-test5", "[cfg]") {
+ spdlog::drop("l1");
+ spdlog::drop("l2");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"};
+
+ load_argv_levels(2, argv);
+
+ auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
+ auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
+
+ REQUIRE(l1->level() == spdlog::level::warn);
+ REQUIRE(l2->level() == spdlog::level::warn);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
+}
+
+TEST_CASE("restore-to-default", "[cfg]") {
+ spdlog::drop("l1");
+ spdlog::drop("l2");
+ const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"};
+ load_argv_levels(2, argv);
+ REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
+}
diff --git a/thirdparty/spdlog/tests/test_circular_q.cpp b/thirdparty/spdlog/tests/test_circular_q.cpp
new file mode 100644
index 000000000..c8b02d363
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_circular_q.cpp
@@ -0,0 +1,50 @@
+#include "includes.h"
+#include "spdlog/details/circular_q.h"
+
+using q_type = spdlog::details::circular_q<size_t>;
+TEST_CASE("test_size", "[circular_q]") {
+ const size_t q_size = 4;
+ q_type q(q_size);
+ REQUIRE(q.size() == 0);
+ REQUIRE(q.empty() == true);
+ for (size_t i = 0; i < q_size; i++) {
+ q.push_back(std::move(i));
+ }
+ REQUIRE(q.size() == q_size);
+ q.push_back(999);
+ REQUIRE(q.size() == q_size);
+}
+
+TEST_CASE("test_rolling", "[circular_q]") {
+ const size_t q_size = 4;
+ q_type q(q_size);
+
+ for (size_t i = 0; i < q_size + 2; i++) {
+ q.push_back(std::move(i));
+ }
+
+ REQUIRE(q.size() == q_size);
+
+ REQUIRE(q.front() == 2);
+ q.pop_front();
+
+ REQUIRE(q.front() == 3);
+ q.pop_front();
+
+ REQUIRE(q.front() == 4);
+ q.pop_front();
+
+ REQUIRE(q.front() == 5);
+ q.pop_front();
+
+ REQUIRE(q.empty());
+
+ q.push_back(6);
+ REQUIRE(q.front() == 6);
+}
+
+TEST_CASE("test_empty", "[circular_q]") {
+ q_type q(0);
+ q.push_back(1);
+ REQUIRE(q.empty());
+} \ No newline at end of file
diff --git a/thirdparty/spdlog/tests/test_create_dir.cpp b/thirdparty/spdlog/tests/test_create_dir.cpp
new file mode 100644
index 000000000..fd040339c
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_create_dir.cpp
@@ -0,0 +1,144 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+
+using spdlog::details::os::create_dir;
+using spdlog::details::os::path_exists;
+
+bool try_create_dir(const spdlog::filename_t &path, const spdlog::filename_t &normalized_path) {
+ auto rv = create_dir(path);
+ REQUIRE(rv == true);
+ return path_exists(normalized_path);
+}
+
+TEST_CASE("create_dir", "[create_dir]") {
+ prepare_logdir();
+
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"),
+ SPDLOG_FILENAME_T("test_logs/dir1/dir1")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"),
+ SPDLOG_FILENAME_T("test_logs/dir1/dir1"))); // test existing
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1///dir2//"),
+ SPDLOG_FILENAME_T("test_logs/dir1/dir2")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("./test_logs/dir1/dir3"),
+ SPDLOG_FILENAME_T("test_logs/dir1/dir3")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/../test_logs/dir1/dir4"),
+ SPDLOG_FILENAME_T("test_logs/dir1/dir4")));
+
+#ifdef WIN32
+ // test backslash folder separator
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir222"),
+ SPDLOG_FILENAME_T("test_logs\\dir1\\dir222")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\"),
+ SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23"),
+ SPDLOG_FILENAME_T("test_logs\\dir1\\dir2\\dir23")));
+ REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\..\\test_logs\\dir1\\dir5"),
+ SPDLOG_FILENAME_T("test_logs\\dir1\\dir5")));
+#endif
+}
+
+TEST_CASE("create_invalid_dir", "[create_dir]") {
+ REQUIRE(create_dir(SPDLOG_FILENAME_T("")) == false);
+ REQUIRE(create_dir(spdlog::filename_t{}) == false);
+#ifdef __linux__
+ REQUIRE(create_dir("/proc/spdlog-utest") == false);
+#endif
+}
+
+TEST_CASE("dir_name", "[create_dir]") {
+ using spdlog::details::os::dir_name;
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty());
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty());
+
+#ifdef WIN32
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\)")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir\\)"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file)")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt)")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt\)")) ==
+ SPDLOG_FILENAME_T(R"(dir\file.txt)"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\dir)"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir)"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(..\file.txt)")) == SPDLOG_FILENAME_T(".."));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(.\file.txt)")) == SPDLOG_FILENAME_T("."));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c:\\a\b\c\d\file.txt)")) ==
+ SPDLOG_FILENAME_T(R"(c:\\a\b\c\d)"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c://a/b/c/d/file.txt)")) ==
+ SPDLOG_FILENAME_T(R"(c://a/b/c/d)"));
+#endif
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir//"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt")) == SPDLOG_FILENAME_T("dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt/")) == SPDLOG_FILENAME_T("dir/file.txt"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir"));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T(".."));
+ REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T("."));
+}
+
+#ifdef _WIN32
+
+ //
+ // test windows cases when drive letter is given e.g. C:\\some-folder
+ //
+ #include <windows.h>
+ #include <fileapi.h>
+
+std::string get_full_path(const std::string &relative_folder_path) {
+ char full_path[MAX_PATH];
+
+ DWORD result = ::GetFullPathNameA(relative_folder_path.c_str(), MAX_PATH, full_path, nullptr);
+ // Return an empty string if failed to get full path
+ return result > 0 && result < MAX_PATH ? std::string(full_path) : std::string();
+}
+
+std::wstring get_full_path(const std::wstring &relative_folder_path) {
+ wchar_t full_path[MAX_PATH];
+ DWORD result = ::GetFullPathNameW(relative_folder_path.c_str(), MAX_PATH, full_path, nullptr);
+ return result > 0 && result < MAX_PATH ? std::wstring(full_path) : std::wstring();
+}
+
+spdlog::filename_t::value_type find_non_existing_drive() {
+ for (char drive = 'A'; drive <= 'Z'; ++drive) {
+ std::string root_path = std::string(1, drive) + ":\\";
+ UINT drive_type = GetDriveTypeA(root_path.c_str());
+ if (drive_type == DRIVE_NO_ROOT_DIR) {
+ return static_cast<spdlog::filename_t::value_type>(drive);
+ }
+ }
+ return '\0'; // No available drive found
+}
+
+TEST_CASE("create_abs_path1", "[create_dir]") {
+ prepare_logdir();
+ auto abs_path = get_full_path(SPDLOG_FILENAME_T("test_logs\\logdir1"));
+ REQUIRE(!abs_path.empty());
+ REQUIRE(create_dir(abs_path) == true);
+}
+
+TEST_CASE("create_abs_path2", "[create_dir]") {
+ prepare_logdir();
+ auto abs_path = get_full_path(SPDLOG_FILENAME_T("test_logs/logdir2"));
+ REQUIRE(!abs_path.empty());
+ REQUIRE(create_dir(abs_path) == true);
+}
+
+TEST_CASE("non_existing_drive", "[create_dir]") {
+ prepare_logdir();
+ spdlog::filename_t path;
+
+ auto non_existing_drive = find_non_existing_drive();
+ path += non_existing_drive;
+ path += SPDLOG_FILENAME_T(":\\");
+ REQUIRE(create_dir(path) == false);
+ path += SPDLOG_FILENAME_T("subdir");
+ REQUIRE(create_dir(path) == false);
+}
+// #endif // SPDLOG_WCHAR_FILENAMES
+#endif // _WIN32
diff --git a/thirdparty/spdlog/tests/test_custom_callbacks.cpp b/thirdparty/spdlog/tests/test_custom_callbacks.cpp
new file mode 100644
index 000000000..f14572115
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_custom_callbacks.cpp
@@ -0,0 +1,37 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+#include "test_sink.h"
+#include "spdlog/sinks/callback_sink.h"
+#include "spdlog/async.h"
+#include "spdlog/common.h"
+
+TEST_CASE("custom_callback_logger", "[custom_callback_logger]") {
+ std::vector<std::string> lines;
+ spdlog::pattern_formatter formatter;
+ auto callback_logger =
+ std::make_shared<spdlog::sinks::callback_sink_st>([&](const spdlog::details::log_msg &msg) {
+ spdlog::memory_buf_t formatted;
+ formatter.format(msg, formatted);
+ auto eol_len = strlen(spdlog::details::os::default_eol);
+ using diff_t =
+ typename std::iterator_traits<decltype(formatted.end())>::difference_type;
+ lines.emplace_back(formatted.begin(), formatted.end() - static_cast<diff_t>(eol_len));
+ });
+ std::shared_ptr<spdlog::sinks::test_sink_st> test_sink(new spdlog::sinks::test_sink_st);
+
+ spdlog::logger logger("test-callback", {callback_logger, test_sink});
+
+ logger.info("test message 1");
+ logger.info("test message 2");
+ logger.info("test message 3");
+
+ std::vector<std::string> ref_lines = test_sink->lines();
+
+ REQUIRE(lines[0] == ref_lines[0]);
+ REQUIRE(lines[1] == ref_lines[1]);
+ REQUIRE(lines[2] == ref_lines[2]);
+ spdlog::drop_all();
+}
diff --git a/thirdparty/spdlog/tests/test_daily_logger.cpp b/thirdparty/spdlog/tests/test_daily_logger.cpp
new file mode 100644
index 000000000..8a00c024a
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_daily_logger.cpp
@@ -0,0 +1,169 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+
+#ifdef SPDLOG_USE_STD_FORMAT
+using filename_memory_buf_t = std::basic_string<spdlog::filename_t::value_type>;
+#else
+using filename_memory_buf_t = fmt::basic_memory_buffer<spdlog::filename_t::value_type, 250>;
+#endif
+
+#ifdef SPDLOG_WCHAR_FILENAMES
+std::string filename_buf_to_utf8string(const filename_memory_buf_t &w) {
+ spdlog::memory_buf_t buf;
+ spdlog::details::os::wstr_to_utf8buf(spdlog::wstring_view_t(w.data(), w.size()), buf);
+ return SPDLOG_BUF_TO_STRING(buf);
+}
+#else
+std::string filename_buf_to_utf8string(const filename_memory_buf_t &w) {
+ return SPDLOG_BUF_TO_STRING(w);
+}
+#endif
+
+TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") {
+ using sink_type =
+ spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::daily_filename_calculator>;
+
+ prepare_logdir();
+
+ // calculate filename (time based)
+ spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
+ std::tm tm = spdlog::details::os::localtime();
+ filename_memory_buf_t w;
+ spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"),
+ basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+
+ auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
+ for (int i = 0; i < 10; ++i) {
+ logger->info("Test message {}", i);
+ }
+ logger->flush();
+
+ require_message_count(filename_buf_to_utf8string(w), 10);
+}
+
+struct custom_daily_file_name_calculator {
+ static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) {
+ return spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename,
+ now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
+ }
+};
+
+TEST_CASE("daily_logger with custom calculator", "[daily_logger]") {
+ using sink_type = spdlog::sinks::daily_file_sink<std::mutex, custom_daily_file_name_calculator>;
+
+ prepare_logdir();
+
+ // calculate filename (time based)
+ spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
+ std::tm tm = spdlog::details::os::localtime();
+ filename_memory_buf_t w;
+ spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"),
+ basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+
+ auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
+ for (int i = 0; i < 10; ++i) {
+ logger->info("Test message {}", i);
+ }
+
+ logger->flush();
+
+ require_message_count(filename_buf_to_utf8string(w), 10);
+}
+
+/*
+ * File name calculations
+ */
+
+TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]") {
+ auto filename =
+ spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 3);
+ REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3.txt"));
+}
+
+TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]") {
+ auto filename =
+ spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated"), 3);
+ REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3"));
+}
+
+TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]") {
+ auto filename =
+ spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 0);
+ REQUIRE(filename == SPDLOG_FILENAME_T("rotated.txt"));
+}
+
+// regex supported only from gcc 4.9 and above
+#if defined(_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
+
+ #include <regex>
+
+TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]") {
+ // daily_YYYY-MM-DD_hh-mm.txt
+ auto filename = spdlog::sinks::daily_filename_calculator::calc_filename(
+ SPDLOG_FILENAME_T("daily.txt"), spdlog::details::os::localtime());
+ // date regex based on https://www.regular-expressions.info/dates.html
+ std::basic_regex<spdlog::filename_t::value_type> re(
+ SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"));
+ std::match_results<spdlog::filename_t::const_iterator> match;
+ REQUIRE(std::regex_match(filename, match, re));
+}
+#endif
+
+TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink]") {
+ std::tm tm = spdlog::details::os::localtime();
+ // example-YYYY-MM-DD.log
+ auto filename = spdlog::sinks::daily_filename_format_calculator::calc_filename(
+ SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm);
+
+ REQUIRE(filename ==
+ spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday));
+}
+
+/* Test removal of old files */
+static spdlog::details::log_msg create_msg(std::chrono::seconds offset) {
+ using spdlog::log_clock;
+ spdlog::details::log_msg msg{"test", spdlog::level::info, "Hello Message"};
+ msg.time = log_clock::now() + offset;
+ return msg;
+}
+
+static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_files) {
+ using spdlog::log_clock;
+ using spdlog::details::log_msg;
+ using spdlog::sinks::daily_file_sink_st;
+
+ prepare_logdir();
+
+ spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_rotate.txt");
+ daily_file_sink_st sink{basename, 2, 30, true, max_days};
+
+ // simulate messages with 24 intervals
+
+ for (int i = 0; i < days_to_run; i++) {
+ auto offset = std::chrono::seconds{24 * 3600 * i};
+ sink.log(create_msg(offset));
+ }
+
+ REQUIRE(count_files("test_logs") == static_cast<size_t>(expected_n_files));
+}
+
+TEST_CASE("daily_logger rotate", "[daily_file_sink]") {
+ int days_to_run = 1;
+ test_rotate(days_to_run, 0, 1);
+ test_rotate(days_to_run, 1, 1);
+ test_rotate(days_to_run, 3, 1);
+ test_rotate(days_to_run, 10, 1);
+
+ days_to_run = 10;
+ test_rotate(days_to_run, 0, 10);
+ test_rotate(days_to_run, 1, 1);
+ test_rotate(days_to_run, 3, 3);
+ test_rotate(days_to_run, 9, 9);
+ test_rotate(days_to_run, 10, 10);
+ test_rotate(days_to_run, 11, 10);
+ test_rotate(days_to_run, 20, 10);
+}
diff --git a/thirdparty/spdlog/tests/test_dup_filter.cpp b/thirdparty/spdlog/tests/test_dup_filter.cpp
new file mode 100644
index 000000000..78e22be3b
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_dup_filter.cpp
@@ -0,0 +1,83 @@
+#include "includes.h"
+#include "spdlog/sinks/dup_filter_sink.h"
+#include "test_sink.h"
+
+TEST_CASE("dup_filter_test1", "[dup_filter_sink]") {
+ using spdlog::sinks::dup_filter_sink_st;
+ using spdlog::sinks::test_sink_mt;
+
+ dup_filter_sink_st dup_sink{std::chrono::seconds{5}};
+ auto test_sink = std::make_shared<test_sink_mt>();
+ dup_sink.add_sink(test_sink);
+
+ for (int i = 0; i < 10; i++) {
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ }
+
+ REQUIRE(test_sink->msg_counter() == 1);
+}
+
+TEST_CASE("dup_filter_test2", "[dup_filter_sink]") {
+ using spdlog::sinks::dup_filter_sink_st;
+ using spdlog::sinks::test_sink_mt;
+
+ dup_filter_sink_st dup_sink{std::chrono::seconds{0}};
+ auto test_sink = std::make_shared<test_sink_mt>();
+ dup_sink.add_sink(test_sink);
+
+ for (int i = 0; i < 10; i++) {
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ }
+
+ REQUIRE(test_sink->msg_counter() == 10);
+}
+
+TEST_CASE("dup_filter_test3", "[dup_filter_sink]") {
+ using spdlog::sinks::dup_filter_sink_st;
+ using spdlog::sinks::test_sink_mt;
+
+ dup_filter_sink_st dup_sink{std::chrono::seconds{1}};
+ auto test_sink = std::make_shared<test_sink_mt>();
+ dup_sink.add_sink(test_sink);
+
+ for (int i = 0; i < 10; i++) {
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"});
+ }
+
+ REQUIRE(test_sink->msg_counter() == 20);
+}
+
+TEST_CASE("dup_filter_test4", "[dup_filter_sink]") {
+ using spdlog::sinks::dup_filter_sink_mt;
+ using spdlog::sinks::test_sink_mt;
+
+ dup_filter_sink_mt dup_sink{std::chrono::milliseconds{10}};
+ auto test_sink = std::make_shared<test_sink_mt>();
+ dup_sink.add_sink(test_sink);
+
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"});
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"});
+ REQUIRE(test_sink->msg_counter() == 2);
+}
+
+TEST_CASE("dup_filter_test5", "[dup_filter_sink]") {
+ using spdlog::sinks::dup_filter_sink_mt;
+ using spdlog::sinks::test_sink_mt;
+
+ dup_filter_sink_mt dup_sink{std::chrono::seconds{5}};
+ auto test_sink = std::make_shared<test_sink_mt>();
+ test_sink->set_pattern("%v");
+ dup_sink.add_sink(test_sink);
+
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"});
+ dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"});
+
+ REQUIRE(test_sink->msg_counter() ==
+ 3); // skip 2 messages but log the "skipped.." message before message2
+ REQUIRE(test_sink->lines()[1] == "Skipped 2 duplicate messages..");
+}
diff --git a/thirdparty/spdlog/tests/test_errors.cpp b/thirdparty/spdlog/tests/test_errors.cpp
new file mode 100644
index 000000000..1c24cabc2
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_errors.cpp
@@ -0,0 +1,112 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+
+#include <iostream>
+
+#define SIMPLE_LOG "test_logs/simple_log.txt"
+#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt"
+
+class failing_sink : public spdlog::sinks::base_sink<std::mutex> {
+protected:
+ void sink_it_(const spdlog::details::log_msg &) final {
+ throw std::runtime_error("some error happened during log");
+ }
+
+ void flush_() final { throw std::runtime_error("some error happened during flush"); }
+};
+struct custom_ex {};
+
+#if !defined(SPDLOG_USE_STD_FORMAT) // std format doesn't fully support runtime strings
+TEST_CASE("default_error_handler", "[errors]") {
+ prepare_logdir();
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
+
+ auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
+ logger->set_pattern("%v");
+ logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1);
+ logger->info("Test message {}", 2);
+ logger->flush();
+ using spdlog::details::os::default_eol;
+ REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol));
+ REQUIRE(count_lines(SIMPLE_LOG) == 1);
+}
+
+TEST_CASE("custom_error_handler", "[errors]") {
+ prepare_logdir();
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
+ auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
+ logger->flush_on(spdlog::level::info);
+ logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
+ logger->info("Good message #1");
+
+ REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex);
+ logger->info("Good message #2");
+ require_message_count(SIMPLE_LOG, 2);
+}
+#endif
+
+TEST_CASE("default_error_handler2", "[errors]") {
+ spdlog::drop_all();
+ auto logger = spdlog::create<failing_sink>("failed_logger");
+ logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
+ REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
+}
+
+TEST_CASE("flush_error_handler", "[errors]") {
+ spdlog::drop_all();
+ auto logger = spdlog::create<failing_sink>("failed_logger");
+ logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
+ REQUIRE_THROWS_AS(logger->flush(), custom_ex);
+}
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+TEST_CASE("async_error_handler", "[errors]") {
+ prepare_logdir();
+ std::string err_msg("log failed with some msg");
+
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
+ {
+ spdlog::init_thread_pool(128, 1);
+ auto logger =
+ spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
+ logger->set_error_handler([=](const std::string &) {
+ std::ofstream ofs("test_logs/custom_err.txt");
+ if (!ofs) {
+ throw std::runtime_error("Failed open test_logs/custom_err.txt");
+ }
+ ofs << err_msg;
+ });
+ logger->info("Good message #1");
+ logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx");
+ logger->info("Good message #2");
+ spdlog::drop("logger"); // force logger to drain the queue and shutdown
+ }
+ spdlog::init_thread_pool(128, 1);
+ require_message_count(SIMPLE_ASYNC_LOG, 2);
+ REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
+}
+#endif
+
+// Make sure async error handler is executed
+TEST_CASE("async_error_handler2", "[errors]") {
+ prepare_logdir();
+ std::string err_msg("This is async handler error message");
+ {
+ spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
+ spdlog::init_thread_pool(128, 1);
+ auto logger = spdlog::create_async<failing_sink>("failed_logger");
+ logger->set_error_handler([=](const std::string &) {
+ std::ofstream ofs("test_logs/custom_err2.txt");
+ if (!ofs) throw std::runtime_error("Failed open test_logs/custom_err2.txt");
+ ofs << err_msg;
+ });
+ logger->info("Hello failure");
+ spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown
+ }
+
+ spdlog::init_thread_pool(128, 1);
+ REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg);
+}
diff --git a/thirdparty/spdlog/tests/test_eventlog.cpp b/thirdparty/spdlog/tests/test_eventlog.cpp
new file mode 100644
index 000000000..702eabea1
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_eventlog.cpp
@@ -0,0 +1,75 @@
+#if _WIN32
+
+ #include "includes.h"
+ #include "test_sink.h"
+
+ #include "spdlog/sinks/win_eventlog_sink.h"
+
+static const LPCSTR TEST_SOURCE = "spdlog_test";
+
+static void test_single_print(std::function<void(std::string const &)> do_log,
+ std::string const &expected_contents,
+ WORD expected_ev_type) {
+ using namespace std::chrono;
+ do_log(expected_contents);
+ const auto expected_time_generated =
+ duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
+
+ struct handle_t {
+ HANDLE handle_;
+
+ ~handle_t() {
+ if (handle_) {
+ REQUIRE(CloseEventLog(handle_));
+ }
+ }
+ } event_log{::OpenEventLogA(nullptr, TEST_SOURCE)};
+
+ REQUIRE(event_log.handle_);
+
+ DWORD read_bytes{}, size_needed{};
+ auto ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
+ 0, &read_bytes, 0, &read_bytes, &size_needed);
+ REQUIRE(!ok);
+ REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+
+ std::vector<char> record_buffer(size_needed);
+ PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data();
+
+ ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0,
+ record, size_needed, &read_bytes, &size_needed);
+ REQUIRE(ok);
+
+ REQUIRE(record->NumStrings == 1);
+ REQUIRE(record->EventType == expected_ev_type);
+ REQUIRE((expected_time_generated - record->TimeGenerated) <= 3u);
+
+ std::string message_in_log(((char *)record + record->StringOffset));
+ REQUIRE(message_in_log == expected_contents + spdlog::details::os::default_eol);
+}
+
+TEST_CASE("eventlog", "[eventlog]") {
+ using namespace spdlog;
+
+ auto test_sink = std::make_shared<sinks::win_eventlog_sink_mt>(TEST_SOURCE);
+
+ spdlog::logger test_logger("eventlog", test_sink);
+ test_logger.set_level(level::trace);
+
+ test_sink->set_pattern("%v");
+
+ test_single_print([&test_logger](std::string const &msg) { test_logger.trace(msg); },
+ "my trace message", EVENTLOG_SUCCESS);
+ test_single_print([&test_logger](std::string const &msg) { test_logger.debug(msg); },
+ "my debug message", EVENTLOG_SUCCESS);
+ test_single_print([&test_logger](std::string const &msg) { test_logger.info(msg); },
+ "my info message", EVENTLOG_INFORMATION_TYPE);
+ test_single_print([&test_logger](std::string const &msg) { test_logger.warn(msg); },
+ "my warn message", EVENTLOG_WARNING_TYPE);
+ test_single_print([&test_logger](std::string const &msg) { test_logger.error(msg); },
+ "my error message", EVENTLOG_ERROR_TYPE);
+ test_single_print([&test_logger](std::string const &msg) { test_logger.critical(msg); },
+ "my critical message", EVENTLOG_ERROR_TYPE);
+}
+
+#endif //_WIN32
diff --git a/thirdparty/spdlog/tests/test_file_helper.cpp b/thirdparty/spdlog/tests/test_file_helper.cpp
new file mode 100644
index 000000000..56ee75e3e
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_file_helper.cpp
@@ -0,0 +1,169 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+
+#define TEST_FILENAME "test_logs/file_helper_test.txt"
+
+using spdlog::details::file_helper;
+
+static void write_with_helper(file_helper &helper, size_t howmany) {
+ spdlog::memory_buf_t formatted;
+ spdlog::fmt_lib::format_to(std::back_inserter(formatted), "{}", std::string(howmany, '1'));
+ helper.write(formatted);
+ helper.flush();
+}
+
+TEST_CASE("file_helper_filename", "[file_helper::filename()]") {
+ prepare_logdir();
+
+ file_helper helper;
+ spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ helper.open(target_filename);
+ REQUIRE(helper.filename() == target_filename);
+}
+
+TEST_CASE("file_helper_size", "[file_helper::size()]") {
+ prepare_logdir();
+ spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ size_t expected_size = 123;
+ {
+ file_helper helper;
+ helper.open(target_filename);
+ write_with_helper(helper, expected_size);
+ REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
+ }
+ REQUIRE(get_filesize(TEST_FILENAME) == expected_size);
+}
+
+TEST_CASE("file_helper_reopen", "[file_helper::reopen()]") {
+ prepare_logdir();
+ spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ file_helper helper;
+ helper.open(target_filename);
+ write_with_helper(helper, 12);
+ REQUIRE(helper.size() == 12);
+ helper.reopen(true);
+ REQUIRE(helper.size() == 0);
+}
+
+TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]") {
+ prepare_logdir();
+ spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ size_t expected_size = 14;
+ file_helper helper;
+ helper.open(target_filename);
+ write_with_helper(helper, expected_size);
+ REQUIRE(helper.size() == expected_size);
+ helper.reopen(false);
+ REQUIRE(helper.size() == expected_size);
+}
+
+static void test_split_ext(const spdlog::filename_t::value_type *fname,
+ const spdlog::filename_t::value_type *expect_base,
+ const spdlog::filename_t::value_type *expect_ext) {
+ spdlog::filename_t filename(fname);
+ spdlog::filename_t expected_base(expect_base);
+ spdlog::filename_t expected_ext(expect_ext);
+
+ spdlog::filename_t basename;
+ spdlog::filename_t ext;
+ std::tie(basename, ext) = file_helper::split_by_extension(filename);
+ REQUIRE(basename == expected_base);
+ REQUIRE(ext == expected_ext);
+}
+
+TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]") {
+ test_split_ext(SPDLOG_FILENAME_T("mylog.txt"), SPDLOG_FILENAME_T("mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T(".mylog.txt"), SPDLOG_FILENAME_T(".mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"),
+ SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog.txt"),
+ SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."),
+ SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog.txt"),
+ SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog.txt"),
+ SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"),
+ SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("../mylog.txt"), SPDLOG_FILENAME_T("../mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt"), SPDLOG_FILENAME_T(".././mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(".././mylog.txt/xxx"),
+ SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("/mylog.txt"), SPDLOG_FILENAME_T("/mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T("//mylog.txt"), SPDLOG_FILENAME_T("//mylog"),
+ SPDLOG_FILENAME_T(".txt"));
+ test_split_ext(SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(""));
+ test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt"));
+}
+
+TEST_CASE("file_event_handlers", "[file_helper]") {
+ enum class flags { before_open, after_open, before_close, after_close };
+ prepare_logdir();
+
+ spdlog::filename_t test_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ // define event handles that update vector of flags when called
+ std::vector<flags> events;
+ spdlog::file_event_handlers handlers;
+ handlers.before_open = [&](spdlog::filename_t filename) {
+ REQUIRE(filename == test_filename);
+ events.push_back(flags::before_open);
+ };
+ handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) {
+ REQUIRE(filename == test_filename);
+ REQUIRE(fstream);
+ fputs("after_open\n", fstream);
+ events.push_back(flags::after_open);
+ };
+ handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) {
+ REQUIRE(filename == test_filename);
+ REQUIRE(fstream);
+ fputs("before_close\n", fstream);
+ events.push_back(flags::before_close);
+ };
+ handlers.after_close = [&](spdlog::filename_t filename) {
+ REQUIRE(filename == test_filename);
+ events.push_back(flags::after_close);
+ };
+ {
+ spdlog::details::file_helper helper{handlers};
+ REQUIRE(events.empty());
+
+ helper.open(test_filename);
+ REQUIRE(events == std::vector<flags>{flags::before_open, flags::after_open});
+
+ events.clear();
+ helper.close();
+ REQUIRE(events == std::vector<flags>{flags::before_close, flags::after_close});
+ REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n");
+
+ helper.reopen(true);
+ events.clear();
+ }
+ // make sure that the file_helper destructor calls the close callbacks if needed
+ REQUIRE(events == std::vector<flags>{flags::before_close, flags::after_close});
+ REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n");
+}
+
+TEST_CASE("file_helper_open", "[file_helper]") {
+ prepare_logdir();
+ spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+ file_helper helper;
+ helper.open(target_filename);
+ helper.close();
+
+ target_filename += SPDLOG_FILENAME_T("/invalid");
+ REQUIRE_THROWS_AS(helper.open(target_filename), spdlog::spdlog_ex);
+}
diff --git a/thirdparty/spdlog/tests/test_file_logging.cpp b/thirdparty/spdlog/tests/test_file_logging.cpp
new file mode 100644
index 000000000..e3155effd
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_file_logging.cpp
@@ -0,0 +1,187 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+
+#define SIMPLE_LOG "test_logs/simple_log"
+#define ROTATING_LOG "test_logs/rotating_log"
+
+TEST_CASE("simple_file_logger", "[simple_logger]") {
+ prepare_logdir();
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
+
+ auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
+ logger->set_pattern("%v");
+
+ logger->info("Test message {}", 1);
+ logger->info("Test message {}", 2);
+
+ logger->flush();
+ require_message_count(SIMPLE_LOG, 2);
+ using spdlog::details::os::default_eol;
+ REQUIRE(file_contents(SIMPLE_LOG) ==
+ spdlog::fmt_lib::format("Test message 1{}Test message 2{}", default_eol, default_eol));
+}
+
+TEST_CASE("flush_on", "[flush_on]") {
+ prepare_logdir();
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
+
+ auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
+ logger->set_pattern("%v");
+ logger->set_level(spdlog::level::trace);
+ logger->flush_on(spdlog::level::info);
+ logger->trace("Should not be flushed");
+ REQUIRE(count_lines(SIMPLE_LOG) == 0);
+
+ logger->info("Test message {}", 1);
+ logger->info("Test message {}", 2);
+
+ require_message_count(SIMPLE_LOG, 3);
+ using spdlog::details::os::default_eol;
+ REQUIRE(file_contents(SIMPLE_LOG) ==
+ spdlog::fmt_lib::format("Should not be flushed{}Test message 1{}Test message 2{}",
+ default_eol, default_eol, default_eol));
+}
+
+TEST_CASE("simple_file_logger", "[truncate]") {
+ prepare_logdir();
+ const spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
+ const bool truncate = true;
+ const auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, truncate);
+ const auto logger = std::make_shared<spdlog::logger>("simple_file_logger", sink);
+
+ logger->info("Test message {}", 3.14);
+ logger->info("Test message {}", 2.71);
+ logger->flush();
+ REQUIRE(count_lines(SIMPLE_LOG) == 2);
+
+ sink->truncate();
+ REQUIRE(count_lines(SIMPLE_LOG) == 0);
+
+ logger->info("Test message {}", 6.28);
+ logger->flush();
+ REQUIRE(count_lines(SIMPLE_LOG) == 1);
+}
+
+TEST_CASE("rotating_file_logger1", "[rotating_logger]") {
+ prepare_logdir();
+ size_t max_size = 1024 * 10;
+ spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
+ auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0);
+
+ for (int i = 0; i < 10; ++i) {
+ logger->info("Test message {}", i);
+ }
+
+ logger->flush();
+ require_message_count(ROTATING_LOG, 10);
+}
+
+TEST_CASE("rotating_file_logger2", "[rotating_logger]") {
+ prepare_logdir();
+ size_t max_size = 1024 * 10;
+ spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
+
+ {
+ // make an initial logger to create the first output file
+ auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
+ for (int i = 0; i < 10; ++i) {
+ logger->info("Test message {}", i);
+ }
+ // drop causes the logger destructor to be called, which is required so the
+ // next logger can rename the first output file.
+ spdlog::drop(logger->name());
+ }
+ auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
+ for (int i = 0; i < 10; ++i) {
+ logger->info("Test message {}", i);
+ }
+ logger->flush();
+ require_message_count(ROTATING_LOG, 10);
+
+ for (int i = 0; i < 1000; i++) {
+ logger->info("Test message {}", i);
+ }
+
+ logger->flush();
+ REQUIRE(get_filesize(ROTATING_LOG) <= max_size);
+ REQUIRE(get_filesize(ROTATING_LOG ".1") <= max_size);
+}
+
+// test that passing max_size=0 throws
+TEST_CASE("rotating_file_logger3", "[rotating_logger]") {
+ prepare_logdir();
+ size_t max_size = 0;
+ spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
+ REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0),
+ spdlog::spdlog_ex);
+}
+
+// test on-demand rotation of logs
+TEST_CASE("rotating_file_logger4", "[rotating_logger]") {
+ prepare_logdir();
+ size_t max_size = 1024 * 10;
+ spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
+ auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_st>(basename, max_size, 2);
+ auto logger = std::make_shared<spdlog::logger>("rotating_sink_logger", sink);
+
+ logger->info("Test message - pre-rotation");
+ logger->flush();
+
+ sink->rotate_now();
+
+ logger->info("Test message - post-rotation");
+ logger->flush();
+
+ REQUIRE(get_filesize(ROTATING_LOG) > 0);
+ REQUIRE(get_filesize(ROTATING_LOG ".1") > 0);
+}
+
+// test changing the max size of the rotating file sink
+TEST_CASE("rotating_file_logger5", "[rotating_logger]") {
+ prepare_logdir();
+ size_t max_size = 5 * 1024;
+ size_t max_files = 2;
+ spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
+ auto sink =
+ std::make_shared<spdlog::sinks::rotating_file_sink_st>(basename, max_size, max_files);
+ auto logger = std::make_shared<spdlog::logger>("rotating_sink_logger", sink);
+ logger->set_pattern("%v");
+
+ REQUIRE(sink->get_max_size() == max_size);
+ REQUIRE(sink->get_max_files() == max_files);
+ max_size = 7 * 1024;
+ max_files = 3;
+
+ sink->set_max_size(max_size);
+ sink->set_max_files(max_files);
+ REQUIRE(sink->get_max_size() == max_size);
+ REQUIRE(sink->get_max_files() == max_files);
+
+ const auto message = std::string(200, 'x');
+ assert(message.size() < max_size);
+ const auto n_messages = max_files * max_size / message.size();
+ for (size_t i = 0; i < n_messages; ++i) {
+ logger->info(message);
+ }
+ logger.reset(); // force flush and close the file
+
+ // validate that the files were rotated correctly with the new max size and max files
+ for (size_t i = 0; i <= max_files; i++) {
+ // calc filenames
+ // e.g. rotating_log, rotating_log.0 rotating_log.1, rotating_log.2, etc.
+ std::ostringstream oss;
+ oss << ROTATING_LOG;
+ if (i > 0) {
+ oss << '.' << i;
+ }
+ const auto filename = oss.str();
+ const auto filesize = get_filesize(filename);
+ REQUIRE(filesize <= max_size);
+ if (i > 0) {
+ REQUIRE(filesize >= max_size - message.size() - 2);
+ }
+ }
+}
diff --git a/thirdparty/spdlog/tests/test_fmt_helper.cpp b/thirdparty/spdlog/tests/test_fmt_helper.cpp
new file mode 100644
index 000000000..31b930672
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_fmt_helper.cpp
@@ -0,0 +1,82 @@
+
+#include "includes.h"
+
+using spdlog::memory_buf_t;
+using spdlog::details::to_string_view;
+
+void test_pad2(int n, const char *expected) {
+ memory_buf_t buf;
+ spdlog::details::fmt_helper::pad2(n, buf);
+
+ REQUIRE(to_string_view(buf) == expected);
+}
+
+void test_pad3(uint32_t n, const char *expected) {
+ memory_buf_t buf;
+ spdlog::details::fmt_helper::pad3(n, buf);
+
+ REQUIRE(to_string_view(buf) == expected);
+}
+
+void test_pad6(std::size_t n, const char *expected) {
+ memory_buf_t buf;
+ spdlog::details::fmt_helper::pad6(n, buf);
+
+ REQUIRE(to_string_view(buf) == expected);
+}
+
+void test_pad9(std::size_t n, const char *expected) {
+ memory_buf_t buf;
+ spdlog::details::fmt_helper::pad9(n, buf);
+
+ REQUIRE(to_string_view(buf) == expected);
+}
+
+TEST_CASE("pad2", "[fmt_helper]") {
+ test_pad2(0, "00");
+ test_pad2(3, "03");
+ test_pad2(10, "10");
+ test_pad2(23, "23");
+ test_pad2(99, "99");
+ test_pad2(100, "100");
+ test_pad2(123, "123");
+ test_pad2(1234, "1234");
+ test_pad2(-5, "-5");
+}
+
+TEST_CASE("pad3", "[fmt_helper]") {
+ test_pad3(0, "000");
+ test_pad3(3, "003");
+ test_pad3(10, "010");
+ test_pad3(23, "023");
+ test_pad3(99, "099");
+ test_pad3(100, "100");
+ test_pad3(123, "123");
+ test_pad3(999, "999");
+ test_pad3(1000, "1000");
+ test_pad3(1234, "1234");
+}
+
+TEST_CASE("pad6", "[fmt_helper]") {
+ test_pad6(0, "000000");
+ test_pad6(3, "000003");
+ test_pad6(23, "000023");
+ test_pad6(123, "000123");
+ test_pad6(1234, "001234");
+ test_pad6(12345, "012345");
+ test_pad6(123456, "123456");
+}
+
+TEST_CASE("pad9", "[fmt_helper]") {
+ test_pad9(0, "000000000");
+ test_pad9(3, "000000003");
+ test_pad9(23, "000000023");
+ test_pad9(123, "000000123");
+ test_pad9(1234, "000001234");
+ test_pad9(12345, "000012345");
+ test_pad9(123456, "000123456");
+ test_pad9(1234567, "001234567");
+ test_pad9(12345678, "012345678");
+ test_pad9(123456789, "123456789");
+ test_pad9(1234567891, "1234567891");
+}
diff --git a/thirdparty/spdlog/tests/test_macros.cpp b/thirdparty/spdlog/tests/test_macros.cpp
new file mode 100644
index 000000000..132706f13
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_macros.cpp
@@ -0,0 +1,53 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+
+#include "includes.h"
+
+#if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG
+ #error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
+#endif
+
+#define TEST_FILENAME "test_logs/simple_log"
+
+TEST_CASE("debug and trace w/o format string", "[macros]") {
+ prepare_logdir();
+ spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
+
+ auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
+ logger->set_pattern("%v");
+ logger->set_level(spdlog::level::trace);
+
+ SPDLOG_LOGGER_TRACE(logger, "Test message 1");
+ SPDLOG_LOGGER_DEBUG(logger, "Test message 2");
+ logger->flush();
+
+ using spdlog::details::os::default_eol;
+ REQUIRE(ends_with(file_contents(TEST_FILENAME),
+ spdlog::fmt_lib::format("Test message 2{}", default_eol)));
+ REQUIRE(count_lines(TEST_FILENAME) == 1);
+
+ auto orig_default_logger = spdlog::default_logger();
+ spdlog::set_default_logger(logger);
+
+ SPDLOG_TRACE("Test message 3");
+ SPDLOG_DEBUG("Test message {}", 4);
+ logger->flush();
+
+ require_message_count(TEST_FILENAME, 2);
+ REQUIRE(ends_with(file_contents(TEST_FILENAME),
+ spdlog::fmt_lib::format("Test message 4{}", default_eol)));
+ spdlog::set_default_logger(std::move(orig_default_logger));
+}
+
+TEST_CASE("disable param evaluation", "[macros]") {
+ SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated"));
+}
+
+TEST_CASE("pass logger pointer", "[macros]") {
+ auto logger = spdlog::create<spdlog::sinks::null_sink_mt>("refmacro");
+ auto &ref = *logger;
+ SPDLOG_LOGGER_TRACE(&ref, "Test message 1");
+ SPDLOG_LOGGER_DEBUG(&ref, "Test message 2");
+}
diff --git a/thirdparty/spdlog/tests/test_misc.cpp b/thirdparty/spdlog/tests/test_misc.cpp
new file mode 100644
index 000000000..deb18e745
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_misc.cpp
@@ -0,0 +1,224 @@
+#ifdef _WIN32 // to prevent fopen warning on windows
+ #define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "includes.h"
+#include "test_sink.h"
+
+template <class T>
+std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info) {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+
+ spdlog::logger oss_logger("oss", oss_sink);
+ oss_logger.set_level(logger_level);
+ oss_logger.set_pattern("%v");
+ oss_logger.info(what);
+
+ return oss.str().substr(0, oss.str().length() - strlen(spdlog::details::os::default_eol));
+}
+
+TEST_CASE("basic_logging ", "[basic_logging]") {
+ // const char
+ REQUIRE(log_info("Hello") == "Hello");
+ REQUIRE(log_info("").empty());
+
+ // std::string
+ REQUIRE(log_info(std::string("Hello")) == "Hello");
+ REQUIRE(log_info(std::string()).empty());
+
+ // Numbers
+ REQUIRE(log_info(5) == "5");
+ REQUIRE(log_info(5.6) == "5.6");
+
+ // User defined class
+ // REQUIRE(log_info(some_logged_class("some_val")) == "some_val");
+}
+
+TEST_CASE("log_levels", "[log_levels]") {
+ REQUIRE(log_info("Hello", spdlog::level::err).empty());
+ REQUIRE(log_info("Hello", spdlog::level::critical).empty());
+ REQUIRE(log_info("Hello", spdlog::level::info) == "Hello");
+ REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello");
+ REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
+}
+
+TEST_CASE("level_to_string_view", "[convert_to_string_view]") {
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::trace) == "trace");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::debug) == "debug");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "info");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::warn) == "warning");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::err) == "error");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::critical) == "critical");
+ REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off");
+}
+
+TEST_CASE("to_short_c_str", "[convert_to_short_c_str]") {
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::trace)) == "T");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::debug)) == "D");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::info)) == "I");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::warn)) == "W");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::err)) == "E");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::critical)) == "C");
+ REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::off)) == "O");
+}
+
+TEST_CASE("to_level_enum", "[convert_to_level_enum]") {
+ REQUIRE(spdlog::level::from_str("trace") == spdlog::level::trace);
+ REQUIRE(spdlog::level::from_str("debug") == spdlog::level::debug);
+ REQUIRE(spdlog::level::from_str("info") == spdlog::level::info);
+ REQUIRE(spdlog::level::from_str("warning") == spdlog::level::warn);
+ REQUIRE(spdlog::level::from_str("warn") == spdlog::level::warn);
+ REQUIRE(spdlog::level::from_str("error") == spdlog::level::err);
+ REQUIRE(spdlog::level::from_str("critical") == spdlog::level::critical);
+ REQUIRE(spdlog::level::from_str("off") == spdlog::level::off);
+ REQUIRE(spdlog::level::from_str("null") == spdlog::level::off);
+}
+
+TEST_CASE("periodic flush", "[periodic_flush]") {
+ using spdlog::sinks::test_sink_mt;
+ auto logger = spdlog::create<test_sink_mt>("periodic_flush");
+ auto test_sink = std::static_pointer_cast<test_sink_mt>(logger->sinks()[0]);
+
+ spdlog::flush_every(std::chrono::seconds(1));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1250));
+ REQUIRE(test_sink->flush_counter() == 1);
+ spdlog::flush_every(std::chrono::seconds(0));
+ spdlog::drop_all();
+}
+
+TEST_CASE("clone-logger", "[clone]") {
+ using spdlog::sinks::test_sink_mt;
+ auto test_sink = std::make_shared<test_sink_mt>();
+ auto logger = std::make_shared<spdlog::logger>("orig", test_sink);
+ logger->set_pattern("%v");
+ auto cloned = logger->clone("clone");
+
+ REQUIRE(cloned->name() == "clone");
+ REQUIRE(logger->sinks() == cloned->sinks());
+ REQUIRE(logger->level() == cloned->level());
+ REQUIRE(logger->flush_level() == cloned->flush_level());
+ logger->info("Some message 1");
+ cloned->info("Some message 2");
+
+ REQUIRE(test_sink->lines().size() == 2);
+ REQUIRE(test_sink->lines()[0] == "Some message 1");
+ REQUIRE(test_sink->lines()[1] == "Some message 2");
+
+ spdlog::drop_all();
+}
+
+TEST_CASE("clone async", "[clone]") {
+ using spdlog::sinks::test_sink_mt;
+ spdlog::init_thread_pool(4, 1);
+ auto test_sink = std::make_shared<test_sink_mt>();
+ auto logger = std::make_shared<spdlog::async_logger>("orig", test_sink, spdlog::thread_pool());
+ logger->set_pattern("%v");
+ auto cloned = logger->clone("clone");
+
+ REQUIRE(cloned->name() == "clone");
+ REQUIRE(logger->sinks() == cloned->sinks());
+ REQUIRE(logger->level() == cloned->level());
+ REQUIRE(logger->flush_level() == cloned->flush_level());
+
+ logger->info("Some message 1");
+ cloned->info("Some message 2");
+
+ spdlog::details::os::sleep_for_millis(100);
+
+ REQUIRE(test_sink->lines().size() == 2);
+ REQUIRE(test_sink->lines()[0] == "Some message 1");
+ REQUIRE(test_sink->lines()[1] == "Some message 2");
+
+ spdlog::drop_all();
+}
+
+TEST_CASE("default logger API", "[default logger]") {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+
+ spdlog::set_default_logger(std::make_shared<spdlog::logger>("oss", oss_sink));
+ spdlog::set_pattern("*** %v");
+
+ spdlog::default_logger()->set_level(spdlog::level::trace);
+ spdlog::trace("hello trace");
+ REQUIRE(oss.str() == "*** hello trace" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::debug("hello debug");
+ REQUIRE(oss.str() == "*** hello debug" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::info("Hello");
+ REQUIRE(oss.str() == "*** Hello" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::warn("Hello again {}", 2);
+ REQUIRE(oss.str() == "*** Hello again 2" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::error(123);
+ REQUIRE(oss.str() == "*** 123" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::critical(std::string("some string"));
+ REQUIRE(oss.str() == "*** some string" + std::string(spdlog::details::os::default_eol));
+
+ oss.str("");
+ spdlog::set_level(spdlog::level::info);
+ spdlog::debug("should not be logged");
+ REQUIRE(oss.str().empty());
+ spdlog::drop_all();
+ spdlog::set_pattern("%v");
+}
+
+#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+TEST_CASE("utf8 to utf16 conversion using windows api", "[windows utf]") {
+ spdlog::wmemory_buf_t buffer;
+
+ spdlog::details::os::utf8_to_wstrbuf("", buffer);
+ REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L""));
+
+ spdlog::details::os::utf8_to_wstrbuf("abc", buffer);
+ REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"abc"));
+
+ spdlog::details::os::utf8_to_wstrbuf("\xc3\x28", buffer); // Invalid UTF-8 sequence.
+ REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\xfffd("));
+
+ spdlog::details::os::utf8_to_wstrbuf("\xe3\x81\xad\xe3\x81\x93",
+ buffer); // "Neko" in hiragana.
+ REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\x306d\x3053"));
+}
+#endif
+
+struct auto_closer {
+ FILE* fp = nullptr;
+ explicit auto_closer(FILE* f)
+ : fp(f) {}
+ auto_closer(const auto_closer&) = delete;
+ auto_closer& operator=(const auto_closer&) = delete;
+ ~auto_closer() {
+ if (fp != nullptr) (void)std::fclose(fp);
+ }
+};
+
+TEST_CASE("os::fwrite_bytes", "[os]") {
+ using spdlog::details::os::create_dir;
+ using spdlog::details::os::fwrite_bytes;
+ const char* filename = "log_tests/test_fwrite_bytes.txt";
+ const char* msg = "hello";
+ prepare_logdir();
+ REQUIRE(create_dir(SPDLOG_FILENAME_T("log_tests")) == true);
+ {
+ auto_closer closer(std::fopen(filename, "wb"));
+ REQUIRE(closer.fp != nullptr);
+ REQUIRE(fwrite_bytes(msg, std::strlen(msg), closer.fp) == true);
+ REQUIRE(fwrite_bytes(msg, 0, closer.fp) == true);
+ std::fflush(closer.fp);
+ REQUIRE(spdlog::details::os::filesize(closer.fp) == 5);
+ }
+ // fwrite_bytes should return false on write failure
+ auto_closer closer(std::fopen(filename, "r"));
+ REQUIRE(closer.fp != nullptr);
+ REQUIRE_FALSE(fwrite_bytes("Hello", 5, closer.fp));
+}
diff --git a/thirdparty/spdlog/tests/test_mpmc_q.cpp b/thirdparty/spdlog/tests/test_mpmc_q.cpp
new file mode 100644
index 000000000..bc7a37d9c
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_mpmc_q.cpp
@@ -0,0 +1,114 @@
+#include "includes.h"
+
+using std::chrono::milliseconds;
+using test_clock = std::chrono::high_resolution_clock;
+
+static milliseconds millis_from(const test_clock::time_point &tp0) {
+ return std::chrono::duration_cast<milliseconds>(test_clock::now() - tp0);
+}
+TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]") {
+ size_t q_size = 100;
+ milliseconds tolerance_wait(20);
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ int popped_item = 0;
+
+ auto start = test_clock::now();
+ auto rv = q.dequeue_for(popped_item, milliseconds::zero());
+ auto delta_ms = millis_from(start);
+
+ REQUIRE(rv == false);
+ INFO("Delta " << delta_ms.count() << " millis");
+ REQUIRE(delta_ms <= tolerance_wait);
+}
+
+TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") {
+ size_t q_size = 100;
+ milliseconds wait_ms(250);
+ milliseconds tolerance_wait(250);
+
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ int popped_item = 0;
+ auto start = test_clock::now();
+ auto rv = q.dequeue_for(popped_item, wait_ms);
+ auto delta_ms = millis_from(start);
+
+ REQUIRE(rv == false);
+
+ INFO("Delta " << delta_ms.count() << " millis");
+ REQUIRE(delta_ms >= wait_ms - tolerance_wait);
+ REQUIRE(delta_ms <= wait_ms + tolerance_wait);
+}
+
+TEST_CASE("dequeue-full-nowait", "[mpmc_blocking_q]") {
+ spdlog::details::mpmc_blocking_queue<int> q(1);
+ q.enqueue(42);
+
+ int item = 0;
+ q.dequeue_for(item, milliseconds::zero());
+ REQUIRE(item == 42);
+}
+
+TEST_CASE("dequeue-full-wait", "[mpmc_blocking_q]") {
+ spdlog::details::mpmc_blocking_queue<int> q(1);
+ q.enqueue(42);
+
+ int item = 0;
+ q.dequeue(item);
+ REQUIRE(item == 42);
+}
+
+TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]") {
+ size_t q_size = 1;
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ milliseconds tolerance_wait(10);
+
+ q.enqueue(1);
+ REQUIRE(q.overrun_counter() == 0);
+
+ auto start = test_clock::now();
+ q.enqueue_nowait(2);
+ auto delta_ms = millis_from(start);
+
+ INFO("Delta " << delta_ms.count() << " millis");
+ REQUIRE(delta_ms <= tolerance_wait);
+ REQUIRE(q.overrun_counter() == 1);
+}
+
+TEST_CASE("bad_queue", "[mpmc_blocking_q]") {
+ size_t q_size = 0;
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ q.enqueue_nowait(1);
+ REQUIRE(q.overrun_counter() == 1);
+ int i = 0;
+ REQUIRE(q.dequeue_for(i, milliseconds(0)) == false);
+}
+
+TEST_CASE("empty_queue", "[mpmc_blocking_q]") {
+ size_t q_size = 10;
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ int i = 0;
+ REQUIRE(q.dequeue_for(i, milliseconds(10)) == false);
+}
+
+TEST_CASE("full_queue", "[mpmc_blocking_q]") {
+ size_t q_size = 100;
+ spdlog::details::mpmc_blocking_queue<int> q(q_size);
+ for (int i = 0; i < static_cast<int>(q_size); i++) {
+ q.enqueue(i + 0); // i+0 to force rvalue and avoid tidy warnings on the same time if we
+ // std::move(i) instead
+ }
+
+ q.enqueue_nowait(123456);
+ REQUIRE(q.overrun_counter() == 1);
+
+ for (int i = 1; i < static_cast<int>(q_size); i++) {
+ int item = -1;
+ q.dequeue(item);
+ REQUIRE(item == i);
+ }
+
+ // last item pushed has overridden the oldest.
+ int item = -1;
+ q.dequeue(item);
+ REQUIRE(item == 123456);
+}
diff --git a/thirdparty/spdlog/tests/test_pattern_formatter.cpp b/thirdparty/spdlog/tests/test_pattern_formatter.cpp
new file mode 100644
index 000000000..17a1bbcb5
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_pattern_formatter.cpp
@@ -0,0 +1,660 @@
+#include "includes.h"
+#include "test_sink.h"
+
+#include <chrono>
+
+using spdlog::memory_buf_t;
+using spdlog::details::to_string_view;
+
+// log to str and return it
+template <typename... Args>
+static std::string log_to_str(const std::string &msg, const Args &...args) {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("pattern_tester", oss_sink);
+ oss_logger.set_level(spdlog::level::info);
+
+ oss_logger.set_formatter(
+ std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(args...)));
+
+ oss_logger.info(msg);
+ return oss.str();
+}
+
+// log to str and return it with time
+template <typename... Args>
+static std::string log_to_str_with_time(spdlog::log_clock::time_point log_time,
+ const std::string &msg,
+ const Args &...args) {
+ std::ostringstream oss;
+ auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
+ spdlog::logger oss_logger("pattern_tester", oss_sink);
+ oss_logger.set_level(spdlog::level::info);
+
+ oss_logger.set_formatter(
+ std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(args...)));
+
+ oss_logger.log(log_time, {}, spdlog::level::info, msg);
+ return oss.str();
+}
+
+TEST_CASE("custom eol", "[pattern_formatter]") {
+ std::string msg = "Hello custom eol test";
+ std::string eol = ";)";
+ REQUIRE(log_to_str(msg, "%v", spdlog::pattern_time_type::local, ";)") == msg + eol);
+}
+
+TEST_CASE("empty format", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "", spdlog::pattern_time_type::local, "").empty());
+}
+
+TEST_CASE("empty format2", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "", spdlog::pattern_time_type::local, "\n") == "\n");
+}
+
+TEST_CASE("level", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[info] Some message\n");
+}
+
+TEST_CASE("short level", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[I] Some message\n");
+}
+
+TEST_CASE("name", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester] Some message\n");
+}
+
+TEST_CASE("date MM/DD/YY ", "[pattern_formatter]") {
+ auto now_tm = spdlog::details::os::localtime();
+ std::stringstream oss;
+ oss << std::setfill('0') << std::setw(2) << now_tm.tm_mon + 1 << "/" << std::setw(2)
+ << now_tm.tm_mday << "/" << std::setw(2) << (now_tm.tm_year + 1900) % 1000
+ << " Some message\n";
+ REQUIRE(log_to_str("Some message", "%D %v", spdlog::pattern_time_type::local, "\n") ==
+ oss.str());
+}
+
+TEST_CASE("GMT offset ", "[pattern_formatter]") {
+ using namespace std::chrono_literals;
+ const auto now = std::chrono::system_clock::now();
+ const auto yesterday = now - 24h;
+
+ REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc,
+ "\n") == "+00:00\n");
+}
+
+TEST_CASE("color range test1", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>(
+ "%^%v%$", spdlog::pattern_time_type::local, "\n");
+
+ memory_buf_t buf;
+ spdlog::fmt_lib::format_to(std::back_inserter(buf), "Hello");
+ memory_buf_t formatted;
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info,
+ spdlog::string_view_t(buf.data(), buf.size()));
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 0);
+ REQUIRE(msg.color_range_end == 5);
+ REQUIRE(log_to_str("hello", "%^%v%$", spdlog::pattern_time_type::local, "\n") == "hello\n");
+}
+
+TEST_CASE("color range test2", "[pattern_formatter]") {
+ auto formatter =
+ std::make_shared<spdlog::pattern_formatter>("%^%$", spdlog::pattern_time_type::local, "\n");
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 0);
+ REQUIRE(msg.color_range_end == 0);
+ REQUIRE(log_to_str("", "%^%$", spdlog::pattern_time_type::local, "\n") == "\n");
+}
+
+TEST_CASE("color range test3", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>("%^***%$");
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 0);
+ REQUIRE(msg.color_range_end == 3);
+}
+
+TEST_CASE("color range test4", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>(
+ "XX%^YYY%$", spdlog::pattern_time_type::local, "\n");
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored");
+
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 2);
+ REQUIRE(msg.color_range_end == 5);
+ REQUIRE(log_to_str("ignored", "XX%^YYY%$", spdlog::pattern_time_type::local, "\n") ==
+ "XXYYY\n");
+}
+
+TEST_CASE("color range test5", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>("**%^");
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 2);
+ REQUIRE(msg.color_range_end == 0);
+}
+
+TEST_CASE("color range test6", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>("**%$");
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+ REQUIRE(msg.color_range_start == 0);
+ REQUIRE(msg.color_range_end == 2);
+}
+
+//
+// Test padding
+//
+
+TEST_CASE("level_left_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%8l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ info] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%8!l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ info] Some message\n");
+}
+
+TEST_CASE("level_right_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%-8l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[info ] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%-8!l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[info ] Some message\n");
+}
+
+TEST_CASE("level_center_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%=8l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ info ] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%=8!l] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ info ] Some message\n");
+}
+
+TEST_CASE("short level_left_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%3L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ I] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%3!L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ I] Some message\n");
+}
+
+TEST_CASE("short level_right_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%-3L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[I ] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%-3!L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[I ] Some message\n");
+}
+
+TEST_CASE("short level_center_padded", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%=3L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ I ] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%=3!L] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[ I ] Some message\n");
+}
+
+TEST_CASE("left_padded_short", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%3n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%3!n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pat] Some message\n");
+}
+
+TEST_CASE("right_padded_short", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%-3n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%-3!n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pat] Some message\n");
+}
+
+TEST_CASE("center_padded_short", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%=3n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester] Some message\n");
+ REQUIRE(log_to_str("Some message", "[%=3!n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pat] Some message\n");
+}
+
+TEST_CASE("left_padded_huge", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%-300n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester ] Some message\n");
+
+ REQUIRE(log_to_str("Some message", "[%-300!n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester ] Some message\n");
+}
+
+TEST_CASE("left_padded_max", "[pattern_formatter]") {
+ REQUIRE(log_to_str("Some message", "[%-64n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester ] Some message\n");
+
+ REQUIRE(log_to_str("Some message", "[%-64!n] %v", spdlog::pattern_time_type::local, "\n") ==
+ "[pattern_tester ] Some message\n");
+}
+
+// Test padding + truncate flag
+
+TEST_CASE("paddinng_truncate", "[pattern_formatter]") {
+ REQUIRE(log_to_str("123456", "%6!v", spdlog::pattern_time_type::local, "\n") == "123456\n");
+ REQUIRE(log_to_str("123456", "%5!v", spdlog::pattern_time_type::local, "\n") == "12345\n");
+ REQUIRE(log_to_str("123456", "%7!v", spdlog::pattern_time_type::local, "\n") == " 123456\n");
+
+ REQUIRE(log_to_str("123456", "%-6!v", spdlog::pattern_time_type::local, "\n") == "123456\n");
+ REQUIRE(log_to_str("123456", "%-5!v", spdlog::pattern_time_type::local, "\n") == "12345\n");
+ REQUIRE(log_to_str("123456", "%-7!v", spdlog::pattern_time_type::local, "\n") == "123456 \n");
+
+ REQUIRE(log_to_str("123456", "%=6!v", spdlog::pattern_time_type::local, "\n") == "123456\n");
+ REQUIRE(log_to_str("123456", "%=5!v", spdlog::pattern_time_type::local, "\n") == "12345\n");
+ REQUIRE(log_to_str("123456", "%=7!v", spdlog::pattern_time_type::local, "\n") == "123456 \n");
+
+ REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n");
+}
+
+TEST_CASE("padding_truncate_funcname", "[pattern_formatter]") {
+ spdlog::sinks::test_sink_st test_sink;
+
+ const char *pattern = "%v [%5!!]";
+ auto formatter = std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(pattern));
+ test_sink.set_formatter(std::move(formatter));
+
+ spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger",
+ spdlog::level::info, "message"};
+ test_sink.log(msg1);
+ REQUIRE(test_sink.lines()[0] == "message [ func]");
+
+ spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger",
+ spdlog::level::info, "message"};
+ test_sink.log(msg2);
+ REQUIRE(test_sink.lines()[1] == "message [funct]");
+}
+
+TEST_CASE("padding_funcname", "[pattern_formatter]") {
+ spdlog::sinks::test_sink_st test_sink;
+
+ const char *pattern = "%v [%10!]";
+ auto formatter = std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(pattern));
+ test_sink.set_formatter(std::move(formatter));
+
+ spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger",
+ spdlog::level::info, "message"};
+ test_sink.log(msg1);
+ REQUIRE(test_sink.lines()[0] == "message [ func]");
+
+ spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "func567890123"}, "test_logger",
+ spdlog::level::info, "message"};
+ test_sink.log(msg2);
+ REQUIRE(test_sink.lines()[1] == "message [func567890123]");
+}
+
+TEST_CASE("clone-default-formatter", "[pattern_formatter]") {
+ auto formatter_1 = std::make_shared<spdlog::pattern_formatter>();
+ auto formatter_2 = formatter_1->clone();
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message");
+
+ memory_buf_t formatted_1;
+ memory_buf_t formatted_2;
+ formatter_1->format(msg, formatted_1);
+ formatter_2->format(msg, formatted_2);
+
+ REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2));
+}
+
+TEST_CASE("clone-default-formatter2", "[pattern_formatter]") {
+ auto formatter_1 = std::make_shared<spdlog::pattern_formatter>("%+");
+ auto formatter_2 = formatter_1->clone();
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message");
+
+ memory_buf_t formatted_1;
+ memory_buf_t formatted_2;
+ formatter_1->format(msg, formatted_1);
+ formatter_2->format(msg, formatted_2);
+
+ REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2));
+}
+
+TEST_CASE("clone-formatter", "[pattern_formatter]") {
+ auto formatter_1 = std::make_shared<spdlog::pattern_formatter>("%D %X [%] [%n] %v");
+ auto formatter_2 = formatter_1->clone();
+ std::string logger_name = "test";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message");
+
+ memory_buf_t formatted_1;
+ memory_buf_t formatted_2;
+ formatter_1->format(msg, formatted_1);
+ formatter_2->format(msg, formatted_2);
+
+ REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2));
+}
+
+TEST_CASE("clone-formatter-2", "[pattern_formatter]") {
+ using spdlog::pattern_time_type;
+ auto formatter_1 = std::make_shared<spdlog::pattern_formatter>(
+ "%D %X [%] [%n] %v", pattern_time_type::utc, "xxxxxx\n");
+ auto formatter_2 = formatter_1->clone();
+ std::string logger_name = "test2";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message");
+
+ memory_buf_t formatted_1;
+ memory_buf_t formatted_2;
+ formatter_1->format(msg, formatted_1);
+ formatter_2->format(msg, formatted_2);
+
+ REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2));
+}
+
+class custom_test_flag : public spdlog::custom_flag_formatter {
+public:
+ explicit custom_test_flag(std::string txt)
+ : some_txt{std::move(txt)} {}
+
+ void format(const spdlog::details::log_msg &,
+ const std::tm &tm,
+ spdlog::memory_buf_t &dest) override {
+ if (some_txt == "throw_me") {
+ throw spdlog::spdlog_ex("custom_flag_exception_test");
+ } else if (some_txt == "time") {
+ auto formatted = spdlog::fmt_lib::format("{:d}:{:02d}{:s}", tm.tm_hour % 12, tm.tm_min,
+ tm.tm_hour / 12 ? "PM" : "AM");
+ dest.append(formatted.data(), formatted.data() + formatted.size());
+ return;
+ }
+ some_txt = std::string(padinfo_.width_, ' ') + some_txt;
+ dest.append(some_txt.data(), some_txt.data() + some_txt.size());
+ }
+ spdlog::details::padding_info get_padding_info() { return padinfo_; }
+
+ std::string some_txt;
+
+ std::unique_ptr<custom_flag_formatter> clone() const override {
+ return spdlog::details::make_unique<custom_test_flag>(some_txt);
+ }
+};
+// test clone with custom flag formatters
+TEST_CASE("clone-custom_formatter", "[pattern_formatter]") {
+ auto formatter_1 = std::make_shared<spdlog::pattern_formatter>();
+ formatter_1->add_flag<custom_test_flag>('t', "custom_output").set_pattern("[%n] [%t] %v");
+ auto formatter_2 = formatter_1->clone();
+ std::string logger_name = "logger-name";
+ spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message");
+
+ memory_buf_t formatted_1;
+ memory_buf_t formatted_2;
+ formatter_1->format(msg, formatted_1);
+ formatter_2->format(msg, formatted_2);
+
+ auto expected = spdlog::fmt_lib::format("[logger-name] [custom_output] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted_1) == expected);
+ REQUIRE(to_string_view(formatted_2) == expected);
+}
+
+//
+// Test source location formatting
+//
+
+#ifdef _WIN32
+static const char *const test_path = "\\a\\b\\c/myfile.cpp";
+#else
+static const char *const test_path = "/a/b//myfile.cpp";
+#endif
+
+TEST_CASE("short filename formatter-1", "[pattern_formatter]") {
+ spdlog::pattern_formatter formatter("%s", spdlog::pattern_time_type::local, "");
+ memory_buf_t formatted;
+ std::string logger_name = "logger-name";
+ spdlog::source_loc source_loc{test_path, 123, "some_func()"};
+ spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello");
+ formatter.format(msg, formatted);
+
+ REQUIRE(to_string_view(formatted) == "myfile.cpp");
+}
+
+TEST_CASE("short filename formatter-2", "[pattern_formatter]") {
+ spdlog::pattern_formatter formatter("%s:%#", spdlog::pattern_time_type::local, "");
+ memory_buf_t formatted;
+ std::string logger_name = "logger-name";
+ spdlog::source_loc source_loc{"myfile.cpp", 123, "some_func()"};
+ spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello");
+ formatter.format(msg, formatted);
+
+ REQUIRE(to_string_view(formatted) == "myfile.cpp:123");
+}
+
+TEST_CASE("short filename formatter-3", "[pattern_formatter]") {
+ spdlog::pattern_formatter formatter("%s %v", spdlog::pattern_time_type::local, "");
+ memory_buf_t formatted;
+ std::string logger_name = "logger-name";
+ spdlog::source_loc source_loc{"", 123, "some_func()"};
+ spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello");
+ formatter.format(msg, formatted);
+
+ REQUIRE(to_string_view(formatted) == " Hello");
+}
+
+TEST_CASE("full filename formatter", "[pattern_formatter]") {
+ spdlog::pattern_formatter formatter("%g", spdlog::pattern_time_type::local, "");
+ memory_buf_t formatted;
+ std::string logger_name = "logger-name";
+ spdlog::source_loc source_loc{test_path, 123, "some_func()"};
+ spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello");
+ formatter.format(msg, formatted);
+
+ REQUIRE(to_string_view(formatted) == test_path);
+}
+
+TEST_CASE("custom flags", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->add_flag<custom_test_flag>('t', "custom1")
+ .add_flag<custom_test_flag>('u', "custom2")
+ .set_pattern("[%n] [%t] [%u] %v");
+
+ memory_buf_t formatted;
+
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+ auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [custom2] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted) == expected);
+}
+
+TEST_CASE("custom flags-padding", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->add_flag<custom_test_flag>('t', "custom1")
+ .add_flag<custom_test_flag>('u', "custom2")
+ .set_pattern("[%n] [%t] [%5u] %v");
+
+ memory_buf_t formatted;
+
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+ auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [ custom2] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted) == expected);
+}
+
+TEST_CASE("custom flags-exception", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->add_flag<custom_test_flag>('t', "throw_me")
+ .add_flag<custom_test_flag>('u', "custom2")
+ .set_pattern("[%n] [%t] [%u] %v");
+
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ CHECK_THROWS_AS(formatter->format(msg, formatted), spdlog::spdlog_ex);
+}
+
+TEST_CASE("override need_localtime", "[pattern_formatter]") {
+ auto formatter =
+ std::make_shared<spdlog::pattern_formatter>(spdlog::pattern_time_type::local, "\n");
+ formatter->add_flag<custom_test_flag>('t', "time").set_pattern("%t> %v");
+
+ {
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+ REQUIRE(to_string_view(formatted) == "0:00AM> some message\n");
+ }
+
+ {
+ formatter->need_localtime();
+
+ auto now_tm = spdlog::details::os::localtime();
+ std::stringstream oss;
+ oss << (now_tm.tm_hour % 12) << ":" << std::setfill('0') << std::setw(2) << now_tm.tm_min
+ << (now_tm.tm_hour / 12 ? "PM" : "AM") << "> some message\n";
+
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+ REQUIRE(to_string_view(formatted) == oss.str());
+ }
+}
+
+#ifndef SPDLOG_NO_TLS
+TEST_CASE("mdc formatter test-1", "[pattern_formatter]") {
+ spdlog::mdc::put("mdc_key_1", "mdc_value_1");
+ spdlog::mdc::put("mdc_key_2", "mdc_value_2");
+
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->set_pattern("[%n] [%l] [%&] %v");
+
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+
+ auto expected = spdlog::fmt_lib::format(
+ "[logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
+ spdlog::details::os::default_eol);
+ REQUIRE(to_string_view(formatted) == expected);
+
+ SECTION("Tear down") { spdlog::mdc::clear(); }
+}
+
+TEST_CASE("mdc formatter value update", "[pattern_formatter]") {
+ spdlog::mdc::put("mdc_key_1", "mdc_value_1");
+ spdlog::mdc::put("mdc_key_2", "mdc_value_2");
+
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->set_pattern("[%n] [%l] [%&] %v");
+
+ memory_buf_t formatted_1;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted_1);
+
+ auto expected = spdlog::fmt_lib::format(
+ "[logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted_1) == expected);
+
+ spdlog::mdc::put("mdc_key_1", "new_mdc_value_1");
+ memory_buf_t formatted_2;
+ formatter->format(msg, formatted_2);
+ expected = spdlog::fmt_lib::format(
+ "[logger-name] [info] [mdc_key_1:new_mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted_2) == expected);
+
+ SECTION("Tear down") { spdlog::mdc::clear(); }
+}
+
+TEST_CASE("mdc different threads", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->set_pattern("[%n] [%l] [%&] %v");
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+
+ memory_buf_t formatted_2;
+
+ auto lambda_1 = [formatter, msg]() {
+ spdlog::mdc::put("mdc_key", "thread_1_id");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+
+ auto expected =
+ spdlog::fmt_lib::format("[logger-name] [info] [mdc_key:thread_1_id] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted) == expected);
+ };
+
+ auto lambda_2 = [formatter, msg]() {
+ spdlog::mdc::put("mdc_key", "thread_2_id");
+ memory_buf_t formatted;
+ formatter->format(msg, formatted);
+
+ auto expected =
+ spdlog::fmt_lib::format("[logger-name] [info] [mdc_key:thread_2_id] some message{}",
+ spdlog::details::os::default_eol);
+
+ REQUIRE(to_string_view(formatted) == expected);
+ };
+
+ std::thread thread_1(lambda_1);
+ std::thread thread_2(lambda_2);
+
+ thread_1.join();
+ thread_2.join();
+
+ SECTION("Tear down") { spdlog::mdc::clear(); }
+}
+
+TEST_CASE("mdc remove key", "[pattern_formatter]") {
+ spdlog::mdc::put("mdc_key_1", "mdc_value_1");
+ spdlog::mdc::put("mdc_key_2", "mdc_value_2");
+ spdlog::mdc::remove("mdc_key_1");
+
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->set_pattern("[%n] [%l] [%&] %v");
+
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+
+ auto expected =
+ spdlog::fmt_lib::format("[logger-name] [info] [mdc_key_2:mdc_value_2] some message{}",
+ spdlog::details::os::default_eol);
+ REQUIRE(to_string_view(formatted) == expected);
+
+ SECTION("Tear down") { spdlog::mdc::clear(); }
+}
+
+TEST_CASE("mdc empty", "[pattern_formatter]") {
+ auto formatter = std::make_shared<spdlog::pattern_formatter>();
+ formatter->set_pattern("[%n] [%l] [%&] %v");
+
+ memory_buf_t formatted;
+ spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
+ "some message");
+ formatter->format(msg, formatted);
+
+ auto expected = spdlog::fmt_lib::format("[logger-name] [info] [] some message{}",
+ spdlog::details::os::default_eol);
+ REQUIRE(to_string_view(formatted) == expected);
+
+ SECTION("Tear down") { spdlog::mdc::clear(); }
+}
+#endif
diff --git a/thirdparty/spdlog/tests/test_registry.cpp b/thirdparty/spdlog/tests/test_registry.cpp
new file mode 100644
index 000000000..1805ae7f8
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_registry.cpp
@@ -0,0 +1,125 @@
+#include "includes.h"
+
+static const char *const tested_logger_name = "null_logger";
+static const char *const tested_logger_name2 = "null_logger2";
+
+#ifndef SPDLOG_NO_EXCEPTIONS
+TEST_CASE("register_drop", "[registry]") {
+ spdlog::drop_all();
+ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
+ REQUIRE(spdlog::get(tested_logger_name) != nullptr);
+ // Throw if registering existing name
+ REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name),
+ spdlog::spdlog_ex);
+}
+
+TEST_CASE("explicit register", "[registry]") {
+ spdlog::drop_all();
+ auto logger = std::make_shared<spdlog::logger>(tested_logger_name,
+ std::make_shared<spdlog::sinks::null_sink_st>());
+ spdlog::register_logger(logger);
+ REQUIRE(spdlog::get(tested_logger_name) != nullptr);
+ // Throw if registering existing name
+ REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name),
+ spdlog::spdlog_ex);
+}
+#endif
+
+TEST_CASE("register_or_replace", "[registry]") {
+ spdlog::drop_all();
+ auto logger1 = std::make_shared<spdlog::logger>(
+ tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
+ spdlog::register_logger(logger1);
+ REQUIRE(spdlog::get(tested_logger_name) == logger1);
+
+ auto logger2 = std::make_shared<spdlog::logger>(
+ tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
+ spdlog::register_or_replace(logger2);
+ REQUIRE(spdlog::get(tested_logger_name) == logger2);
+}
+
+TEST_CASE("apply_all", "[registry]") {
+ spdlog::drop_all();
+ auto logger = std::make_shared<spdlog::logger>(tested_logger_name,
+ std::make_shared<spdlog::sinks::null_sink_st>());
+ spdlog::register_logger(logger);
+ auto logger2 = std::make_shared<spdlog::logger>(
+ tested_logger_name2, std::make_shared<spdlog::sinks::null_sink_st>());
+ spdlog::register_logger(logger2);
+
+ int counter = 0;
+ spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger>) { counter++; });
+ REQUIRE(counter == 2);
+
+ counter = 0;
+ spdlog::drop(tested_logger_name2);
+ spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l) {
+ REQUIRE(l->name() == tested_logger_name);
+ counter++;
+ });
+ REQUIRE(counter == 1);
+}
+
+TEST_CASE("drop", "[registry]") {
+ spdlog::drop_all();
+ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
+ spdlog::drop(tested_logger_name);
+ REQUIRE_FALSE(spdlog::get(tested_logger_name));
+}
+
+TEST_CASE("drop-default", "[registry]") {
+ spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name));
+ spdlog::drop(tested_logger_name);
+ REQUIRE_FALSE(spdlog::default_logger());
+ REQUIRE_FALSE(spdlog::get(tested_logger_name));
+}
+
+TEST_CASE("drop_all", "[registry]") {
+ spdlog::drop_all();
+ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
+ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2);
+ spdlog::drop_all();
+ REQUIRE_FALSE(spdlog::get(tested_logger_name));
+ REQUIRE_FALSE(spdlog::get(tested_logger_name2));
+ REQUIRE_FALSE(spdlog::default_logger());
+}
+
+TEST_CASE("drop non existing", "[registry]") {
+ spdlog::drop_all();
+ spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
+ spdlog::drop("some_name");
+ REQUIRE_FALSE(spdlog::get("some_name"));
+ REQUIRE(spdlog::get(tested_logger_name));
+ spdlog::drop_all();
+}
+
+TEST_CASE("default logger", "[registry]") {
+ spdlog::drop_all();
+ spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name));
+ REQUIRE(spdlog::get(tested_logger_name) == spdlog::default_logger());
+ spdlog::drop_all();
+}
+
+TEST_CASE("set_default_logger(nullptr)", "[registry]") {
+ spdlog::set_default_logger(nullptr);
+ REQUIRE_FALSE(spdlog::default_logger());
+}
+
+TEST_CASE("disable automatic registration", "[registry]") {
+ // set some global parameters
+ spdlog::level::level_enum log_level = spdlog::level::level_enum::warn;
+ spdlog::set_level(log_level);
+ // but disable automatic registration
+ spdlog::set_automatic_registration(false);
+ auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(
+ tested_logger_name, SPDLOG_FILENAME_T("filename"), 11, 59);
+ auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
+ // loggers should not be part of the registry
+ REQUIRE_FALSE(spdlog::get(tested_logger_name));
+ REQUIRE_FALSE(spdlog::get(tested_logger_name2));
+ // but make sure they are still initialized according to global defaults
+ REQUIRE(logger1->level() == log_level);
+ REQUIRE(logger2->level() == log_level);
+ spdlog::set_level(spdlog::level::info);
+ spdlog::set_automatic_registration(true);
+}
diff --git a/thirdparty/spdlog/tests/test_ringbuffer.cpp b/thirdparty/spdlog/tests/test_ringbuffer.cpp
new file mode 100644
index 000000000..81d791656
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_ringbuffer.cpp
@@ -0,0 +1,52 @@
+#include "includes.h"
+#include "spdlog/sinks/ringbuffer_sink.h"
+
+TEST_CASE("ringbuffer invalid size", "[ringbuffer]") {
+ REQUIRE_THROWS_AS(spdlog::sinks::ringbuffer_sink_mt(0), spdlog::spdlog_ex);
+}
+
+TEST_CASE("ringbuffer stores formatted messages", "[ringbuffer]") {
+ spdlog::sinks::ringbuffer_sink_st sink(3);
+ sink.set_pattern("%v");
+
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg1"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg2"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg3"});
+
+ auto formatted = sink.last_formatted();
+ REQUIRE(formatted.size() == 3);
+ using spdlog::details::os::default_eol;
+ REQUIRE(formatted[0] == spdlog::fmt_lib::format("msg1{}", default_eol));
+ REQUIRE(formatted[1] == spdlog::fmt_lib::format("msg2{}", default_eol));
+ REQUIRE(formatted[2] == spdlog::fmt_lib::format("msg3{}", default_eol));
+}
+
+TEST_CASE("ringbuffer overrun keeps last items", "[ringbuffer]") {
+ spdlog::sinks::ringbuffer_sink_st sink(2);
+ sink.set_pattern("%v");
+
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "first"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "second"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "third"});
+
+ auto formatted = sink.last_formatted();
+ REQUIRE(formatted.size() == 2);
+ using spdlog::details::os::default_eol;
+ REQUIRE(formatted[0] == spdlog::fmt_lib::format("second{}", default_eol));
+ REQUIRE(formatted[1] == spdlog::fmt_lib::format("third{}", default_eol));
+}
+
+TEST_CASE("ringbuffer retrieval limit", "[ringbuffer]") {
+ spdlog::sinks::ringbuffer_sink_st sink(3);
+ sink.set_pattern("%v");
+
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "A"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "B"});
+ sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "C"});
+
+ auto formatted = sink.last_formatted(2);
+ REQUIRE(formatted.size() == 2);
+ using spdlog::details::os::default_eol;
+ REQUIRE(formatted[0] == spdlog::fmt_lib::format("B{}", default_eol));
+ REQUIRE(formatted[1] == spdlog::fmt_lib::format("C{}", default_eol));
+}
diff --git a/thirdparty/spdlog/tests/test_sink.h b/thirdparty/spdlog/tests/test_sink.h
new file mode 100644
index 000000000..9c0945232
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_sink.h
@@ -0,0 +1,70 @@
+//
+// Copyright(c) 2018 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/fmt/fmt.h"
+#include <chrono>
+#include <mutex>
+#include <thread>
+
+namespace spdlog {
+namespace sinks {
+
+template <class Mutex>
+class test_sink : public base_sink<Mutex> {
+ const size_t lines_to_save = 100;
+
+public:
+ size_t msg_counter() {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+ return msg_counter_;
+ }
+
+ size_t flush_counter() {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+ return flush_counter_;
+ }
+
+ void set_delay(std::chrono::milliseconds delay) {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+ delay_ = delay;
+ }
+
+ // return last output without the eol
+ std::vector<std::string> lines() {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+ return lines_;
+ }
+
+protected:
+ void sink_it_(const details::log_msg &msg) override {
+ memory_buf_t formatted;
+ base_sink<Mutex>::formatter_->format(msg, formatted);
+ // save the line without the eol
+ auto eol_len = strlen(details::os::default_eol);
+ using diff_t = typename std::iterator_traits<decltype(formatted.end())>::difference_type;
+ if (lines_.size() < lines_to_save) {
+ lines_.emplace_back(formatted.begin(), formatted.end() - static_cast<diff_t>(eol_len));
+ }
+ msg_counter_++;
+ std::this_thread::sleep_for(delay_);
+ }
+
+ void flush_() override { flush_counter_++; }
+
+ size_t msg_counter_{0};
+ size_t flush_counter_{0};
+ std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()};
+ std::vector<std::string> lines_;
+};
+
+using test_sink_mt = test_sink<std::mutex>;
+using test_sink_st = test_sink<details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/thirdparty/spdlog/tests/test_stdout_api.cpp b/thirdparty/spdlog/tests/test_stdout_api.cpp
new file mode 100644
index 000000000..67659b84d
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_stdout_api.cpp
@@ -0,0 +1,90 @@
+/*
+ * This content is released under the MIT License as specified in
+ * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
+ */
+#include "includes.h"
+#include "spdlog/sinks/stdout_sinks.h"
+#include "spdlog/sinks/stdout_color_sinks.h"
+TEST_CASE("stdout_st", "[stdout]") {
+ auto l = spdlog::stdout_logger_st("test");
+ l->set_pattern("%+");
+ l->set_level(spdlog::level::trace);
+ l->trace("Test stdout_st");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stdout_mt", "[stdout]") {
+ auto l = spdlog::stdout_logger_mt("test");
+ l->set_pattern("%+");
+ l->set_level(spdlog::level::debug);
+ l->debug("Test stdout_mt");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stderr_st", "[stderr]") {
+ auto l = spdlog::stderr_logger_st("test");
+ l->set_pattern("%+");
+ l->info("Test stderr_st");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stderr_mt", "[stderr]") {
+ auto l = spdlog::stderr_logger_mt("test");
+ l->set_pattern("%+");
+ l->info("Test stderr_mt");
+ l->warn("Test stderr_mt");
+ l->error("Test stderr_mt");
+ l->critical("Test stderr_mt");
+ spdlog::drop_all();
+}
+
+// color loggers
+TEST_CASE("stdout_color_st", "[stdout]") {
+ auto l = spdlog::stdout_color_st("test");
+ l->set_pattern("%+");
+ l->info("Test stdout_color_st");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stdout_color_mt", "[stdout]") {
+ auto l = spdlog::stdout_color_mt("test");
+ l->set_pattern("%+");
+ l->set_level(spdlog::level::trace);
+ l->trace("Test stdout_color_mt");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stderr_color_st", "[stderr]") {
+ auto l = spdlog::stderr_color_st("test");
+ l->set_pattern("%+");
+ l->set_level(spdlog::level::debug);
+ l->debug("Test stderr_color_st");
+ spdlog::drop_all();
+}
+
+TEST_CASE("stderr_color_mt", "[stderr]") {
+ auto l = spdlog::stderr_color_mt("test");
+ l->set_pattern("%+");
+ l->info("Test stderr_color_mt");
+ l->warn("Test stderr_color_mt");
+ l->error("Test stderr_color_mt");
+ l->critical("Test stderr_color_mt");
+ spdlog::drop_all();
+}
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+TEST_CASE("wchar_api", "[stdout]") {
+ auto l = spdlog::stdout_logger_st("wchar_logger");
+ l->set_pattern("%+");
+ l->set_level(spdlog::level::trace);
+ l->trace(L"Test wchar_api");
+ l->trace(L"Test wchar_api {}", L"param");
+ l->trace(L"Test wchar_api {}", 1);
+ l->trace(L"Test wchar_api {}", std::wstring{L"wstring param"});
+ l->trace(std::wstring{L"Test wchar_api wstring"});
+ SPDLOG_LOGGER_DEBUG(l, L"Test SPDLOG_LOGGER_DEBUG {}", L"param");
+ spdlog::drop_all();
+}
+
+#endif
diff --git a/thirdparty/spdlog/tests/test_stopwatch.cpp b/thirdparty/spdlog/tests/test_stopwatch.cpp
new file mode 100644
index 000000000..b1b4b191c
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_stopwatch.cpp
@@ -0,0 +1,42 @@
+#include "includes.h"
+#include "test_sink.h"
+#include "spdlog/stopwatch.h"
+
+TEST_CASE("stopwatch1", "[stopwatch]") {
+ using std::chrono::milliseconds;
+ using clock = std::chrono::steady_clock;
+ milliseconds wait_ms(500);
+ milliseconds tolerance_ms(250);
+ auto start = clock::now();
+ spdlog::stopwatch sw;
+ std::this_thread::sleep_for(wait_ms);
+ auto stop = clock::now();
+ auto diff_ms = std::chrono::duration_cast<milliseconds>(stop - start);
+ REQUIRE(sw.elapsed() >= diff_ms);
+ REQUIRE(sw.elapsed() <= diff_ms + tolerance_ms);
+}
+
+TEST_CASE("stopwatch2", "[stopwatch]") {
+ using spdlog::sinks::test_sink_st;
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using clock = std::chrono::steady_clock;
+
+ clock::duration wait_duration(milliseconds(500));
+ clock::duration tolerance_duration(milliseconds(250));
+
+ auto test_sink = std::make_shared<test_sink_st>();
+
+ auto start = clock::now();
+ spdlog::stopwatch sw;
+ spdlog::logger logger("test-stopwatch", test_sink);
+ logger.set_pattern("%v");
+ std::this_thread::sleep_for(wait_duration);
+ auto stop = clock::now();
+ logger.info("{}", sw);
+ auto val = std::stod(test_sink->lines()[0]);
+ auto diff_duration = duration_cast<std::chrono::duration<double>>(stop - start);
+
+ REQUIRE(val >= (diff_duration).count() - 0.001);
+ REQUIRE(val <= (diff_duration + tolerance_duration).count());
+}
diff --git a/thirdparty/spdlog/tests/test_systemd.cpp b/thirdparty/spdlog/tests/test_systemd.cpp
new file mode 100644
index 000000000..e36365741
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_systemd.cpp
@@ -0,0 +1,14 @@
+#include "includes.h"
+#include "spdlog/sinks/systemd_sink.h"
+
+TEST_CASE("systemd", "[all]") {
+ auto systemd_sink = std::make_shared<spdlog::sinks::systemd_sink_st>();
+ spdlog::logger logger("spdlog_systemd_test", systemd_sink);
+ logger.set_level(spdlog::level::trace);
+ logger.trace("test spdlog trace");
+ logger.debug("test spdlog debug");
+ SPDLOG_LOGGER_INFO((&logger), "test spdlog info");
+ SPDLOG_LOGGER_WARN((&logger), "test spdlog warn");
+ SPDLOG_LOGGER_ERROR((&logger), "test spdlog error");
+ SPDLOG_LOGGER_CRITICAL((&logger), "test spdlog critical");
+}
diff --git a/thirdparty/spdlog/tests/test_time_point.cpp b/thirdparty/spdlog/tests/test_time_point.cpp
new file mode 100644
index 000000000..b7b1b2323
--- /dev/null
+++ b/thirdparty/spdlog/tests/test_time_point.cpp
@@ -0,0 +1,35 @@
+#include "includes.h"
+#include "test_sink.h"
+#include "spdlog/async.h"
+
+TEST_CASE("time_point1", "[time_point log_msg]") {
+ std::shared_ptr<spdlog::sinks::test_sink_st> test_sink(new spdlog::sinks::test_sink_st);
+ spdlog::logger logger("test-time_point", test_sink);
+
+ spdlog::source_loc source{};
+ std::chrono::system_clock::time_point tp{std::chrono::system_clock::now()};
+ test_sink->set_pattern("%T.%F"); // interested in the time_point
+
+ // all the following should have the same time
+ test_sink->set_delay(std::chrono::milliseconds(10));
+ for (int i = 0; i < 5; i++) {
+ spdlog::details::log_msg msg{tp, source, "test_logger", spdlog::level::info, "message"};
+ test_sink->log(msg);
+ }
+
+ logger.log(tp, source, spdlog::level::info, "formatted message");
+ logger.log(tp, source, spdlog::level::info, "formatted message");
+ logger.log(tp, source, spdlog::level::info, "formatted message");
+ logger.log(tp, source, spdlog::level::info, "formatted message");
+ logger.log(source, spdlog::level::info,
+ "formatted message"); // last line has different time_point
+
+ // now the real test... that the times are the same.
+ std::vector<std::string> lines = test_sink->lines();
+ REQUIRE(lines[0] == lines[1]);
+ REQUIRE(lines[2] == lines[3]);
+ REQUIRE(lines[4] == lines[5]);
+ REQUIRE(lines[6] == lines[7]);
+ REQUIRE(lines[8] != lines[9]);
+ spdlog::drop_all();
+}
diff --git a/thirdparty/spdlog/tests/utils.cpp b/thirdparty/spdlog/tests/utils.cpp
new file mode 100644
index 000000000..405b5e5a2
--- /dev/null
+++ b/thirdparty/spdlog/tests/utils.cpp
@@ -0,0 +1,102 @@
+#include "includes.h"
+
+#ifdef _WIN32
+ #include <windows.h>
+#else
+ #include <sys/types.h>
+ #include <dirent.h>
+#endif
+
+void prepare_logdir() {
+ spdlog::drop_all();
+#ifdef _WIN32
+ system("rmdir /S /Q test_logs");
+#else
+ auto rv = system("rm -rf test_logs");
+ if (rv != 0) {
+ throw std::runtime_error("Failed to rm -rf test_logs");
+ }
+#endif
+}
+
+std::string file_contents(const std::string &filename) {
+ std::ifstream ifs(filename, std::ios_base::binary);
+ if (!ifs) {
+ throw std::runtime_error("Failed open file ");
+ }
+ return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+}
+
+std::size_t count_lines(const std::string &filename) {
+ std::ifstream ifs(filename);
+ if (!ifs) {
+ throw std::runtime_error("Failed open file ");
+ }
+
+ std::string line;
+ size_t counter = 0;
+ while (std::getline(ifs, line)) counter++;
+ return counter;
+}
+
+void require_message_count(const std::string &filename, const std::size_t messages) {
+ if (strlen(spdlog::details::os::default_eol) == 0) {
+ REQUIRE(count_lines(filename) == 1);
+ } else {
+ REQUIRE(count_lines(filename) == messages);
+ }
+}
+
+std::size_t get_filesize(const std::string &filename) {
+ std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
+ if (!ifs) {
+ throw std::runtime_error("Failed open file " + filename);
+ }
+ return static_cast<std::size_t>(ifs.tellg());
+}
+
+// source: https://stackoverflow.com/a/2072890/192001
+bool ends_with(std::string const &value, std::string const &ending) {
+ if (ending.size() > value.size()) {
+ return false;
+ }
+ return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
+}
+
+#ifdef _WIN32
+// Based on: https://stackoverflow.com/a/37416569/192001
+std::size_t count_files(const std::string &folder) {
+ size_t counter = 0;
+ WIN32_FIND_DATAA ffd;
+
+ // Start iterating over the files in the folder directory.
+ HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do // Managed to locate and create an handle to that folder.
+ {
+ if (ffd.cFileName[0] != '.') counter++;
+ } while (::FindNextFileA(hFind, &ffd) != 0);
+ ::FindClose(hFind);
+ } else {
+ throw std::runtime_error("Failed open folder " + folder);
+ }
+
+ return counter;
+}
+#else
+// Based on: https://stackoverflow.com/a/2802255/192001
+std::size_t count_files(const std::string &folder) {
+ size_t counter = 0;
+ DIR *dp = opendir(folder.c_str());
+ if (dp == nullptr) {
+ throw std::runtime_error("Failed open folder " + folder);
+ }
+
+ struct dirent *ep = nullptr;
+ while ((ep = readdir(dp)) != nullptr) {
+ if (ep->d_name[0] != '.') counter++;
+ }
+ (void)closedir(dp);
+ return counter;
+}
+#endif
diff --git a/thirdparty/spdlog/tests/utils.h b/thirdparty/spdlog/tests/utils.h
new file mode 100644
index 000000000..53c09b469
--- /dev/null
+++ b/thirdparty/spdlog/tests/utils.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+std::size_t count_files(const std::string &folder);
+
+void prepare_logdir();
+
+std::string file_contents(const std::string &filename);
+
+std::size_t count_lines(const std::string &filename);
+
+void require_message_count(const std::string &filename, const std::size_t messages);
+
+std::size_t get_filesize(const std::string &filename);
+
+bool ends_with(std::string const &value, std::string const &ending); \ No newline at end of file