148 lines
4.3 KiB
C++
148 lines
4.3 KiB
C++
/**
|
|
* @file tests/unit/test_logging.cpp
|
|
* @brief Test src/logging.*.
|
|
*/
|
|
#include "../tests_common.h"
|
|
#include "../tests_log_checker.h"
|
|
|
|
#include <filesystem>
|
|
#include <format>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <random>
|
|
#include <src/logging.h>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
namespace {
|
|
std::array log_levels = {
|
|
std::tuple("verbose", &verbose),
|
|
std::tuple("debug", &debug),
|
|
std::tuple("info", &info),
|
|
std::tuple("warning", &warning),
|
|
std::tuple("error", &error),
|
|
std::tuple("fatal", &fatal),
|
|
};
|
|
|
|
constexpr auto log_file = "test_sunshine.log";
|
|
|
|
/**
|
|
* @brief Write test content to a log file.
|
|
*
|
|
* @param path Path to write.
|
|
* @param content Content to write.
|
|
*/
|
|
void write_log_file(const std::filesystem::path &path, std::string_view content) {
|
|
std::ofstream output {path};
|
|
output << content;
|
|
}
|
|
|
|
/**
|
|
* @brief Read all content from a test log file.
|
|
*
|
|
* @param path Path to read.
|
|
* @return File content.
|
|
*/
|
|
std::string read_log_file(const std::filesystem::path &path) {
|
|
std::ifstream input {path};
|
|
return {std::istreambuf_iterator<char> {input}, std::istreambuf_iterator<char> {}};
|
|
}
|
|
} // namespace
|
|
|
|
/**
|
|
* @brief Test fixture for startup log rotation.
|
|
*/
|
|
class LogRotationTest: public BaseTest {
|
|
protected:
|
|
/**
|
|
* @brief Create an empty directory for the current test.
|
|
*/
|
|
void SetUp() override {
|
|
BaseTest::SetUp();
|
|
std::filesystem::remove_all(test_directory);
|
|
std::filesystem::create_directories(test_directory);
|
|
}
|
|
|
|
/**
|
|
* @brief Remove files created by the current test.
|
|
*/
|
|
void TearDown() override {
|
|
std::filesystem::remove_all(test_directory);
|
|
BaseTest::TearDown();
|
|
}
|
|
|
|
/**
|
|
* @brief Build the path for a rotated test log.
|
|
*
|
|
* @param generation Rotated log generation number.
|
|
* @return Path with the generation suffix appended.
|
|
*/
|
|
std::filesystem::path rotated_log_path(std::size_t generation) const {
|
|
auto path = log_path;
|
|
path += std::format(".{}", generation);
|
|
return path;
|
|
}
|
|
|
|
const std::filesystem::path test_directory {std::filesystem::path {SUNSHINE_TEST_BIN_DIR} / "log_rotation_tests"}; ///< Directory containing log rotation test files.
|
|
const std::filesystem::path log_path {test_directory / "custom.log"}; ///< Path to the current test log.
|
|
};
|
|
|
|
TEST_F(LogRotationTest, RotatesCurrentLogAndRetainsFivePreviousLogs) {
|
|
write_log_file(log_path, "current");
|
|
for (std::size_t generation = 1; generation <= logging::retained_log_file_count; ++generation) {
|
|
write_log_file(rotated_log_path(generation), std::to_string(generation));
|
|
}
|
|
|
|
EXPECT_FALSE(logging::rotate_log_file(log_path));
|
|
|
|
EXPECT_FALSE(std::filesystem::exists(log_path));
|
|
EXPECT_EQ(read_log_file(rotated_log_path(1)), "current");
|
|
EXPECT_EQ(read_log_file(rotated_log_path(2)), "1");
|
|
EXPECT_EQ(read_log_file(rotated_log_path(3)), "2");
|
|
EXPECT_EQ(read_log_file(rotated_log_path(4)), "3");
|
|
EXPECT_EQ(read_log_file(rotated_log_path(5)), "4");
|
|
}
|
|
|
|
TEST_F(LogRotationTest, SupportsMissingLogGenerations) {
|
|
write_log_file(rotated_log_path(2), "second");
|
|
|
|
EXPECT_FALSE(logging::rotate_log_file(log_path));
|
|
|
|
EXPECT_FALSE(std::filesystem::exists(log_path));
|
|
EXPECT_FALSE(std::filesystem::exists(rotated_log_path(1)));
|
|
EXPECT_FALSE(std::filesystem::exists(rotated_log_path(2)));
|
|
EXPECT_EQ(read_log_file(rotated_log_path(3)), "second");
|
|
}
|
|
|
|
TEST_F(LogRotationTest, ReportsFilesystemErrors) {
|
|
std::filesystem::create_directories(rotated_log_path(logging::retained_log_file_count) / "child");
|
|
write_log_file(log_path, "current");
|
|
|
|
EXPECT_TRUE(logging::rotate_log_file(log_path));
|
|
EXPECT_EQ(read_log_file(log_path), "current");
|
|
}
|
|
|
|
struct LogLevelsTest: BaseTest, testing::WithParamInterface<decltype(log_levels)::value_type> {};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Logging,
|
|
LogLevelsTest,
|
|
testing::ValuesIn(log_levels),
|
|
[](const auto &info) {
|
|
return std::string(std::get<0>(info.param));
|
|
}
|
|
);
|
|
|
|
TEST_P(LogLevelsTest, PutMessage) {
|
|
auto [label, plogger] = GetParam();
|
|
ASSERT_TRUE(plogger);
|
|
auto &logger = *plogger;
|
|
|
|
std::random_device rand_dev;
|
|
std::mt19937_64 rand_gen(rand_dev());
|
|
auto test_message = std::format("{}{}", rand_gen(), rand_gen());
|
|
BOOST_LOG(logger) << test_message;
|
|
|
|
ASSERT_TRUE(log_checker::line_contains(log_file, test_message));
|
|
}
|