#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <sstream>
#include <iostream>
#include <panda/log/file.h>
#include <panda/unievent/Fs.h>
using namespace panda;
using namespace panda::log;
using panda::unievent::Fs;
#define TEST(name) TEST_CASE("log-file: " name, "[log-file]")
#ifdef _WIN32
#define NL "\r\n"
#else
#define NL "\n"
#endif
struct StderrCapture {
std::stringstream ss;
std::streambuf* old;
StderrCapture () : ss(), old(std::cerr.rdbuf(ss.rdbuf())) {}
~StderrCapture () { std::cerr.rdbuf(old); }
};
struct Ctx {
string dir;
Ctx () : dir("t/var") {
Fs::mkpath(dir);
}
~Ctx () {
set_logger(nullptr);
Fs::remove_all(dir);
}
string readfile (string path) {
auto fd = Fs::open(path, Fs::OpenFlags::RDONLY).value();
auto content = Fs::read(fd, 999).value();
Fs::close(fd);
return content;
}
};
TEST("create") {
Ctx c;
FileLoggerSP logger;
FileLogger::Config cfg;
SECTION("no file") {
cfg.file = c.dir + "/file.log";
}
SECTION("no path") {
cfg.file = c.dir + "/mydir/file.log";
}
SECTION("file exists") {
cfg.file = c.dir + "/file.log";
Fs::touch(cfg.file);
}
logger = new FileLogger(cfg);
CHECK(true);
}
TEST("log") {
Ctx c;
FileLogger::Config cfg;
cfg.file = c.dir + "/file.log";
cfg.buffered = GENERATE(true, false);
set_logger(new FileLogger(cfg));
set_formatter("%m");
set_level(Level::Debug);
panda_log_debug("hello");
set_logger(nullptr);
set_logger(new FileLogger(cfg));
panda_log_debug("world");
set_logger(nullptr); // need to close file to flush it
CHECK(c.readfile(cfg.file) == "hello" NL "world" NL);
}
#ifndef _WIN32 // windows will not allow to change busy file
TEST("reopen log file if moved/deleted/etc") {
Ctx c;
FileLogger::Config cfg;
cfg.file = c.dir + "/file.log";
cfg.buffered = GENERATE(true, false);
cfg.check_freq = 0;
FileLogger* logger = new FileLogger(cfg);
set_logger(logger);
set_formatter("%m");
set_level(Level::Debug);
panda_log_debug("hello");
logger->flush();
CHECK(c.readfile(cfg.file) == "hello" NL);
SECTION("remove") { Fs::remove(cfg.file); }
SECTION("move") { Fs::rename(cfg.file, cfg.file + ".tmp"); }
panda_log_debug("world");
logger->flush();
CHECK(c.readfile(cfg.file) == "world" NL);
}
#endif
TEST("ignore logging if log file could not be created/written") {
Ctx c;
FileLogger::Config cfg;
cfg.check_freq = 0;
cfg.buffered = GENERATE(true, false);
string to_delete;
SECTION("mkpath fails") {
to_delete = c.dir + "/notdir";
Fs::touch(to_delete);
cfg.file = to_delete + "/file";
}
SECTION("open fails") {
cfg.file = c.dir + "/notfile";
Fs::mkpath(cfg.file);
to_delete = cfg.file;
}
StderrCapture cap;
set_logger(new FileLogger(cfg));
panda_log_error("this log should be ignored");
Fs::remove_all(to_delete); // remove the reason of failure
panda_log_error("this log should NOT be ignored");
auto fd = Fs::open(cfg.file, Fs::OpenFlags::RDONLY).value();
set_logger(nullptr); // need to close file to flush it
auto txt = Fs::read(fd, 999).value();
CHECK(txt == "this log should NOT be ignored" NL);
}