117 lines
3.7 KiB
C++
117 lines
3.7 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "fgc/Logger.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
using namespace fgc;
|
|
|
|
namespace {
|
|
|
|
// Run a callable with std::cout redirected to a buffer; return what was written.
|
|
template <typename F>
|
|
std::string captureCout(F&& f) {
|
|
std::ostringstream oss;
|
|
std::streambuf* old = std::cout.rdbuf(oss.rdbuf());
|
|
f();
|
|
std::cout.rdbuf(old);
|
|
return oss.str();
|
|
}
|
|
|
|
unsigned bits(LogCat c) { return static_cast<unsigned>(c); }
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("parseCategories maps names, all/none, and flags unknown tokens") {
|
|
bool ok = true;
|
|
|
|
CHECK(Logger::parseCategories("serial,mqtt", &ok) == (bits(LogCat::Serial) | bits(LogCat::Mqtt)));
|
|
CHECK(ok);
|
|
|
|
CHECK(Logger::parseCategories("all", &ok) == bits(LogCat::All));
|
|
CHECK(ok);
|
|
|
|
CHECK(Logger::parseCategories("none", &ok) == 0u);
|
|
CHECK(ok);
|
|
CHECK(Logger::parseCategories("", &ok) == 0u);
|
|
CHECK(ok);
|
|
|
|
// Case- and whitespace-insensitive.
|
|
CHECK(Logger::parseCategories(" SeRiAl , Camera ", &ok)
|
|
== (bits(LogCat::Serial) | bits(LogCat::Camera)));
|
|
CHECK(ok);
|
|
|
|
// Unknown token: ok=false, but recognized tokens still apply.
|
|
ok = true;
|
|
CHECK(Logger::parseCategories("serial,bogus", &ok) == bits(LogCat::Serial));
|
|
CHECK_FALSE(ok);
|
|
}
|
|
|
|
TEST_CASE("catFromString round-trips and flags unknown") {
|
|
bool ok = true;
|
|
CHECK(Logger::catFromString("control", &ok) == LogCat::Control);
|
|
CHECK(ok);
|
|
CHECK(Logger::catFromString("nope", &ok) == LogCat::None);
|
|
CHECK_FALSE(ok);
|
|
}
|
|
|
|
TEST_CASE("category enable/disable/set round-trip") {
|
|
Logger::setCategories(0);
|
|
CHECK(Logger::categories() == 0u);
|
|
CHECK_FALSE(Logger::categoryEnabled(LogCat::Serial));
|
|
|
|
Logger::enableCategory(LogCat::Serial);
|
|
Logger::enableCategory(LogCat::Mqtt);
|
|
CHECK(Logger::categoryEnabled(LogCat::Serial));
|
|
CHECK(Logger::categoryEnabled(LogCat::Mqtt));
|
|
CHECK_FALSE(Logger::categoryEnabled(LogCat::Camera));
|
|
|
|
Logger::disableCategory(LogCat::Serial);
|
|
CHECK_FALSE(Logger::categoryEnabled(LogCat::Serial));
|
|
CHECK(Logger::categoryEnabled(LogCat::Mqtt));
|
|
|
|
Logger::setCategories(bits(LogCat::All));
|
|
CHECK(Logger::categoryEnabled(LogCat::Camera));
|
|
CHECK(Logger::categoryEnabled(LogCat::Control));
|
|
|
|
Logger::setCategories(0); // leave global state clean for other tests
|
|
}
|
|
|
|
TEST_CASE("LOG_TRACE_CAT gating is independent of the linear level") {
|
|
Logger::setCategories(0);
|
|
|
|
SUBCASE("suppressed when category off (even at trace level)") {
|
|
Logger::setLevel(LogLevel::Trace);
|
|
std::string out = captureCout([] { LOG_TRACE_CAT(LogCat::Serial) << "TX kd180"; });
|
|
CHECK(out.empty());
|
|
}
|
|
|
|
SUBCASE("emitted with [SERIAL] tag when category on, at default info level") {
|
|
Logger::setLevel(LogLevel::Info); // NOT trace
|
|
Logger::enableCategory(LogCat::Serial);
|
|
std::string out = captureCout([] { LOG_TRACE_CAT(LogCat::Serial) << "TX kd180"; });
|
|
CHECK(out.find("[SERIAL]") != std::string::npos);
|
|
CHECK(out.find("TX kd180") != std::string::npos);
|
|
}
|
|
|
|
SUBCASE("a different category stays silent") {
|
|
Logger::setLevel(LogLevel::Info);
|
|
Logger::enableCategory(LogCat::Serial); // serial on, mqtt off
|
|
std::string out = captureCout([] { LOG_TRACE_CAT(LogCat::Mqtt) << "PUB t p"; });
|
|
CHECK(out.empty());
|
|
}
|
|
|
|
SUBCASE("level Off silences categories too") {
|
|
Logger::setLevel(LogLevel::Off);
|
|
Logger::enableCategory(LogCat::Serial);
|
|
std::string out = captureCout([] { LOG_TRACE_CAT(LogCat::Serial) << "TX x"; });
|
|
CHECK(out.empty());
|
|
}
|
|
|
|
// Restore defaults so test ordering can't leak state.
|
|
Logger::setCategories(0);
|
|
Logger::setLevel(LogLevel::Info);
|
|
}
|