Files
libvirtualhid/tests/unit/test_windows_rotating_trace_log.cpp

199 lines
7.5 KiB
C++

/**
* @file tests/unit/test_windows_rotating_trace_log.cpp
* @brief Unit tests for the bounded Windows UMDF trace log.
*/
// platform includes
#include <Windows.h>
// standard includes
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <format>
#include <fstream>
#include <iterator>
#include <limits>
#include <string>
#include <string_view>
// local includes
#include "fixtures/fixtures.hpp"
#include "platform/windows/driver/rotating_trace_log.hpp"
#include "platform/windows/driver/unique_win32_handle.hpp"
namespace {
BOOL WINAPI fail_file_size(HANDLE, PLARGE_INTEGER) {
return FALSE;
}
void write_file(const std::filesystem::path &path, std::string_view content) {
auto output = std::ofstream {path, std::ios::binary};
output << content;
}
std::string read_file(const std::filesystem::path &path) {
auto input = std::ifstream {path, std::ios::binary};
return {std::istreambuf_iterator<char> {input}, std::istreambuf_iterator<char> {}};
}
class WindowsRotatingTraceLogTest: public WindowsTest {
protected:
void SetUp() override {
WindowsTest::SetUp();
std::filesystem::remove_all(test_directory);
std::filesystem::create_directories(test_directory);
}
void TearDown() override {
std::filesystem::remove_all(test_directory);
WindowsTest::TearDown();
}
std::filesystem::path rotated_path(std::size_t generation) const {
return lvh::detail::windows::rotated_trace_log_path(log_path.native(), generation);
}
const std::filesystem::path test_directory {
std::filesystem::path {LIBVIRTUALHID_TEST_BINARY_DIR} /
std::format("libvirtualhid-rotating-trace-log-tests-{}", GetCurrentProcessId())
};
const std::filesystem::path log_path {test_directory / "driver.log"};
};
TEST_F(WindowsRotatingTraceLogTest, AppendsWithoutRotatingAtTheSizeLimit) {
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "1234", 8U, 2U));
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "5678", 8U, 2U));
EXPECT_EQ(read_file(log_path), "12345678");
EXPECT_FALSE(std::filesystem::exists(rotated_path(1U)));
}
TEST_F(WindowsRotatingTraceLogTest, RejectsInvalidArguments) {
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log({}, "line", 8U, 2U));
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), {}, 8U, 2U));
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "line", 0U, 2U));
EXPECT_TRUE(
lvh::detail::windows::trace_log_arguments_valid(log_path.native(), (std::numeric_limits<DWORD>::max)(), 8U)
);
EXPECT_FALSE(
lvh::detail::windows::trace_log_arguments_valid(
log_path.native(),
static_cast<std::size_t>((std::numeric_limits<DWORD>::max)()) + 1U,
8U
)
);
}
TEST_F(WindowsRotatingTraceLogTest, RejectsAnUnavailableLogPath) {
const auto unavailable_path = test_directory / "missing" / "driver.log";
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(unavailable_path.native(), "line", 8U, 2U));
}
TEST_F(WindowsRotatingTraceLogTest, ReportsFileSizeAndWriteFailures) {
auto operations = lvh::detail::windows::TraceLogOperations {};
operations.get_file_size = &fail_file_size;
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "line", 8U, 2U, operations));
operations = {};
operations.write_file = [](HANDLE, auto, DWORD, LPDWORD bytes_written, LPOVERLAPPED) {
*bytes_written = 0U;
return FALSE;
};
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "line", 8U, 2U, operations));
operations.write_file = [](HANDLE, auto, DWORD bytes_to_write, LPDWORD bytes_written, LPOVERLAPPED) {
*bytes_written = bytes_to_write - 1U;
return TRUE;
};
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "line", 8U, 2U, operations));
}
TEST_F(WindowsRotatingTraceLogTest, RecognizesMissingPathErrors) {
EXPECT_TRUE(lvh::detail::windows::trace_log_path_missing(ERROR_FILE_NOT_FOUND));
EXPECT_TRUE(lvh::detail::windows::trace_log_path_missing(ERROR_PATH_NOT_FOUND));
EXPECT_FALSE(lvh::detail::windows::trace_log_path_missing(ERROR_ACCESS_DENIED));
}
TEST_F(WindowsRotatingTraceLogTest, NormalizesAnInvalidWin32Handle) {
EXPECT_FALSE(lvh::detail::windows::make_unique_win32_handle(INVALID_HANDLE_VALUE));
}
TEST_F(WindowsRotatingTraceLogTest, RotatesBeforeAnAppendExceedsTheLimit) {
write_file(log_path, "12345678");
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "new", 8U, 2U));
EXPECT_EQ(read_file(log_path), "new");
EXPECT_EQ(read_file(rotated_path(1U)), "12345678");
}
TEST_F(WindowsRotatingTraceLogTest, RetainsOnlyTheConfiguredGenerations) {
constexpr auto max_size = std::uint64_t {4};
constexpr auto retained_files = std::size_t {2};
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "aaaa", max_size, retained_files));
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "bbbb", max_size, retained_files));
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "cccc", max_size, retained_files));
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "dddd", max_size, retained_files));
EXPECT_EQ(read_file(log_path), "dddd");
EXPECT_EQ(read_file(rotated_path(1U)), "cccc");
EXPECT_EQ(read_file(rotated_path(2U)), "bbbb");
EXPECT_FALSE(std::filesystem::exists(rotated_path(3U)));
}
TEST_F(WindowsRotatingTraceLogTest, DiscardsTheCurrentLogWhenNoBackupsAreRetained) {
write_file(log_path, "full");
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "new", 4U, 0U));
EXPECT_EQ(read_file(log_path), "new");
EXPECT_FALSE(std::filesystem::exists(rotated_path(1U)));
}
TEST_F(WindowsRotatingTraceLogTest, SupportsMissingGenerations) {
write_file(log_path, "current");
write_file(rotated_path(2U), "second");
EXPECT_TRUE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "new", 7U, 3U));
EXPECT_EQ(read_file(log_path), "new");
EXPECT_EQ(read_file(rotated_path(1U)), "current");
EXPECT_FALSE(std::filesystem::exists(rotated_path(2U)));
EXPECT_EQ(read_file(rotated_path(3U)), "second");
}
TEST_F(WindowsRotatingTraceLogTest, LeavesTheBoundedLogUntouchedWhenRotationFails) {
write_file(log_path, "full");
std::filesystem::create_directory(rotated_path(1U));
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "new", 4U, 1U));
EXPECT_EQ(read_file(log_path), "full");
EXPECT_TRUE(std::filesystem::is_directory(rotated_path(1U)));
}
TEST_F(WindowsRotatingTraceLogTest, ReportsALockedGenerationMoveFailure) {
write_file(log_path, "full");
write_file(rotated_path(1U), "previous");
const auto locked_generation = lvh::detail::windows::make_unique_win32_handle(CreateFileW(
rotated_path(1U).c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
));
ASSERT_TRUE(locked_generation);
EXPECT_FALSE(lvh::detail::windows::append_rotating_trace_log(log_path.native(), "new", 4U, 2U));
EXPECT_EQ(read_file(log_path), "full");
EXPECT_EQ(read_file(rotated_path(1U)), "previous");
EXPECT_FALSE(std::filesystem::exists(rotated_path(2U)));
}
} // namespace