Bound the diagnostic log and cut the per-file scan noise
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// Drives the diagnostic log past its byte budget, without foobar2000.
|
||||
//
|
||||
// log_rotation_test
|
||||
//
|
||||
// The component's log is bounded by rolling: a file that reaches the budget is
|
||||
// moved to a .1 sibling and a fresh one is started. What pushes it there is a
|
||||
// Media Library scan, which a test cannot arrange, so the budget is spent here
|
||||
// with lines of a known size instead. What is checked is what the bound
|
||||
// promises: neither file passes the budget, there is no chain of rolls, the
|
||||
// newest lines are the ones in the live file, and the session banner is written
|
||||
// again after a roll -- which is the only reason a rolled log still says which
|
||||
// build produced it.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "../src/log.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::wstring temp_directory() {
|
||||
wchar_t buffer[MAX_PATH + 1] = {};
|
||||
const DWORD length = GetTempPathW(MAX_PATH, buffer);
|
||||
return std::wstring(buffer, length) + L"joc_log_rotation_test";
|
||||
}
|
||||
|
||||
bool read_file(const std::wstring& path, std::string* out) {
|
||||
std::FILE* file = _wfopen(path.c_str(), L"rb");
|
||||
if (file == nullptr) return false;
|
||||
char buffer[64 * 1024];
|
||||
std::size_t got = 0;
|
||||
while ((got = std::fread(buffer, 1, sizeof(buffer), file)) != 0) out->append(buffer, got);
|
||||
std::fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long long file_size(const std::wstring& path) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA data{};
|
||||
if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == FALSE) return 0;
|
||||
return (static_cast<unsigned long long>(data.nFileSizeHigh) << 32) | data.nFileSizeLow;
|
||||
}
|
||||
|
||||
bool contains(const std::string& haystack, const std::string& needle) {
|
||||
return haystack.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
int failures = 0;
|
||||
|
||||
void check(bool ok, const char* what) {
|
||||
std::printf("%-4s %s\n", ok ? "ok" : "FAIL", what);
|
||||
if (!ok) ++failures;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const std::wstring dir = temp_directory();
|
||||
CreateDirectoryW(dir.c_str(), nullptr);
|
||||
const std::wstring live = dir + L"\\rotation.log";
|
||||
const std::wstring rolled = live + L".1";
|
||||
const std::wstring chained = live + L".2";
|
||||
DeleteFileW(live.c_str());
|
||||
DeleteFileW(rolled.c_str());
|
||||
DeleteFileW(chained.c_str());
|
||||
SetEnvironmentVariableW(L"JOC_LOG", live.c_str());
|
||||
|
||||
const unsigned long long budget = joc_log::budget_bytes();
|
||||
std::printf("budget %llu byte(s) per file\n", budget);
|
||||
|
||||
joc_log::open();
|
||||
joc_log::header_begin();
|
||||
joc_log::line("=== header marker ===");
|
||||
joc_log::header_end();
|
||||
|
||||
// Enough lines to spend the budget twice over, so the roll has to happen and
|
||||
// the file it produces has to be replaced rather than added to.
|
||||
const std::string filler(160, 'x');
|
||||
const unsigned lines = static_cast<unsigned>(budget / 190ull * 3ull);
|
||||
for (unsigned i = 1; i <= lines; ++i) joc_log::line("filler %u %s", i, filler.c_str());
|
||||
|
||||
const unsigned long long live_size = file_size(live);
|
||||
const unsigned long long rolled_size = file_size(rolled);
|
||||
std::printf("wrote %u line(s): live %llu byte(s), rolled %llu byte(s)\n", lines, live_size,
|
||||
rolled_size);
|
||||
|
||||
check(live_size > 0 && live_size <= budget + 4096, "live file is inside the budget");
|
||||
check(rolled_size > 0 && rolled_size <= budget + 4096, "rolled file is inside the budget");
|
||||
check(live_size + rolled_size <= 2 * budget + 8192, "the two together are inside twice it");
|
||||
check(file_size(chained) == 0, "no second roll is kept");
|
||||
|
||||
std::string live_text;
|
||||
std::string rolled_text;
|
||||
check(read_file(live, &live_text), "live file reads back");
|
||||
check(read_file(rolled, &rolled_text), "rolled file reads back");
|
||||
check(contains(live_text, "[log rolled"), "the live file says it rolled");
|
||||
check(contains(live_text, "=== header marker ==="), "the banner was written again");
|
||||
|
||||
char newest[64] = {};
|
||||
std::snprintf(newest, sizeof(newest), "filler %u ", lines);
|
||||
check(contains(live_text, newest), "the newest line is in the live file");
|
||||
check(!contains(rolled_text, newest), "and not in the rolled one");
|
||||
|
||||
if (failures == 0) {
|
||||
std::printf("log rotation holds\n");
|
||||
return 0;
|
||||
}
|
||||
std::printf("%d check(s) failed\n", failures);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user