Files

112 lines
4.5 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 steredbits and Pelagia contributors
// Tests of the util/log module: levels, names, secret filtering, sink, and
// ffmpeg log bridge.
#include <cstring>
#include <string>
#include <vector>
extern "C" {
#include <libavutil/log.h>
}
#include "player/av_log_bridge.h"
#include "test_framework.h"
#include "util/log.h"
static bool str_eq(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
static bool contains(const std::string& haystack, const std::string& needle) {
return haystack.find(needle) != std::string::npos;
}
struct Capture {
std::vector<std::string> lines;
static void sink(util::LogLevel, const char* line, void* ctx) {
static_cast<Capture*>(ctx)->lines.push_back(line);
}
std::string all() const {
std::string out;
for (const std::string& l : lines) {
out += l + "\n";
}
return out;
}
};
int main() {
using util::LogLevel;
CHECK_EQ(util::log_get_level(), LogLevel::Info);
util::log_set_level(LogLevel::Error);
CHECK_EQ(util::log_get_level(), LogLevel::Error);
util::log_set_level(LogLevel::Debug);
CHECK_EQ(util::log_get_level(), LogLevel::Debug);
CHECK(str_eq(util::log_level_name(LogLevel::Debug), "DEBUG"));
CHECK(str_eq(util::log_level_name(LogLevel::Info), "INFO"));
CHECK(str_eq(util::log_level_name(LogLevel::Warn), "WARN"));
CHECK(str_eq(util::log_level_name(LogLevel::Error), "ERROR"));
// log_write must not crash, whatever the filtered level.
util::log_set_level(LogLevel::Error);
LOG_DEBUG("filtered line %d", 42);
LOG_ERROR("ligne visible %s", "ok");
// --- Secret filtering ----------------------------------------------------
const std::string token = "0123456789abcdef0123456789abcdef";
util::log_add_secret(token);
util::log_add_secret("court"); // ignored: would mask ordinary text
CHECK_EQ(util::log_redact("token " + token + " fin"), "token ******** fin");
CHECK_EQ(util::log_redact("un mot court reste"), "un mot court reste");
// Patterns, even for a secret that was never registered.
CHECK_EQ(util::log_redact("GET /stream.ts?a=1&api_key=autre123&b=2"),
"GET /stream.ts?a=1&api_key=********&b=2");
CHECK_EQ(util::log_redact("url ...&API_KEY=xyz"), "url ...&API_KEY=********");
CHECK_EQ(util::log_redact("Authorization: MediaBrowser Client=\"J\", Token=\"abc\""),
"Authorization: MediaBrowser Client=\"J\", Token=\"********\"");
CHECK_EQ(util::log_redact("X-Emby-Token: abc def"), "X-Emby-Token: ******** def");
CHECK_EQ(util::log_redact("api_key=********"), "api_key=********"); // already masked
// Long lines (beyond the formatting buffer): filtered whole.
util::log_set_level(LogLevel::Debug);
Capture capture;
util::log_set_sink(&Capture::sink, &capture);
const std::string padding(3000, 'x');
LOG_INFO("%s api_key=%s fin", padding.c_str(), token.c_str());
LOG_DEBUG("Token=\"%s\"", token.c_str());
CHECK_EQ(capture.lines.size(), 2u);
CHECK(!contains(capture.all(), token));
CHECK(contains(capture.all(), "api_key=******** fin"));
CHECK(capture.lines.size() == 2 && capture.lines[0].size() > 3000);
// --- Pont ffmpeg ---------------------------------------------------------
// HTTP request as libavformat logs it at debug level, and a line
// emitted in two calls with the token split between the two.
capture.lines.clear();
player::install_ffmpeg_log_bridge(true);
av_log(nullptr, AV_LOG_DEBUG,
"request: GET /Videos/x/stream.ts?api_key=%s HTTP/1.1\r\n"
"Authorization: MediaBrowser Token=\"%s\"\r\n\n",
token.c_str(), token.c_str());
av_log(nullptr, AV_LOG_ERROR, "Server returned 401 for ...%s", token.substr(0, 16).c_str());
av_log(nullptr, AV_LOG_ERROR, "%s (authorization failed)\n", token.substr(16).c_str());
av_log(nullptr, AV_LOG_TRACE, "trace filtered by the level %s\n", token.c_str());
CHECK(!contains(capture.all(), token));
CHECK(!contains(capture.all(), token.substr(0, 16)));
CHECK(contains(capture.all(), "api_key=********"));
CHECK(contains(capture.all(), "Token=\"********\""));
CHECK(contains(capture.all(), "Server returned 401 for ...********"));
CHECK(!contains(capture.all(), "trace filtered"));
util::log_set_sink(nullptr, nullptr);
util::log_clear_secrets();
CHECK(contains(util::log_redact(token), token));
return testfw::test_failures();
}