Files
Waybar/test/hyprland/backend.cpp
Austin Horstman 87a5b7ed0f test(hyprland): add failure-path fd-leak coverage
Hyprland tests did not explicitly verify descriptor behavior on key failure
paths.

I added focused tests for missing instance signature and connect-failure paths
that assert file descriptor counts stay stable across repeated attempts.

Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-03-02 08:17:29 -06:00

118 lines
3.4 KiB
C++

#if __has_include(<catch2/catch_test_macros.hpp>)
#include <catch2/catch_test_macros.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include <system_error>
#include "modules/hyprland/backend.hpp"
namespace fs = std::filesystem;
namespace hyprland = waybar::modules::hyprland;
namespace {
class IPCTestHelper : public hyprland::IPC {
public:
static void resetSocketFolder() { socketFolder_.clear(); }
};
std::size_t countOpenFds() {
#if defined(__linux__)
std::size_t count = 0;
for (const auto& _ : fs::directory_iterator("/proc/self/fd")) {
(void)_;
++count;
}
return count;
#else
return 0;
#endif
}
} // namespace
TEST_CASE("XDGRuntimeDirExists", "[getSocketFolder]") {
// Test case: XDG_RUNTIME_DIR exists and contains "hypr" directory
// Arrange
constexpr auto instanceSig = "instance_sig";
const fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000";
std::error_code ec;
fs::remove_all(tempDir, ec);
fs::path expectedPath = tempDir / "hypr" / instanceSig;
fs::create_directories(expectedPath);
setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1);
IPCTestHelper::resetSocketFolder();
// Act
fs::path actualPath = hyprland::IPC::getSocketFolder(instanceSig);
// Assert expected result
REQUIRE(actualPath == expectedPath);
fs::remove_all(tempDir, ec);
}
TEST_CASE("XDGRuntimeDirDoesNotExist", "[getSocketFolder]") {
// Test case: XDG_RUNTIME_DIR does not exist
// Arrange
constexpr auto instanceSig = "instance_sig";
unsetenv("XDG_RUNTIME_DIR");
fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig;
IPCTestHelper::resetSocketFolder();
// Act
fs::path actualPath = hyprland::IPC::getSocketFolder(instanceSig);
// Assert expected result
REQUIRE(actualPath == expectedPath);
}
TEST_CASE("XDGRuntimeDirExistsNoHyprDir", "[getSocketFolder]") {
// Test case: XDG_RUNTIME_DIR exists but does not contain "hypr" directory
// Arrange
constexpr auto instanceSig = "instance_sig";
fs::path tempDir = fs::temp_directory_path() / "hypr_test/run/user/1000";
std::error_code ec;
fs::remove_all(tempDir, ec);
fs::create_directories(tempDir);
setenv("XDG_RUNTIME_DIR", tempDir.c_str(), 1);
fs::path expectedPath = fs::path("/tmp") / "hypr" / instanceSig;
IPCTestHelper::resetSocketFolder();
// Act
fs::path actualPath = hyprland::IPC::getSocketFolder(instanceSig);
// Assert expected result
REQUIRE(actualPath == expectedPath);
fs::remove_all(tempDir, ec);
}
TEST_CASE("getSocket1Reply throws on no socket", "[getSocket1Reply]") {
unsetenv("HYPRLAND_INSTANCE_SIGNATURE");
IPCTestHelper::resetSocketFolder();
std::string request = "test_request";
CHECK_THROWS(hyprland::IPC::getSocket1Reply(request));
}
#if defined(__linux__)
TEST_CASE("getSocket1Reply failure paths do not leak fds", "[getSocket1Reply][fd-leak]") {
const auto baseline = countOpenFds();
unsetenv("HYPRLAND_INSTANCE_SIGNATURE");
for (int i = 0; i < 16; ++i) {
IPCTestHelper::resetSocketFolder();
CHECK_THROWS(hyprland::IPC::getSocket1Reply("test_request"));
}
const auto after_missing_signature = countOpenFds();
REQUIRE(after_missing_signature == baseline);
setenv("HYPRLAND_INSTANCE_SIGNATURE", "definitely-not-running", 1);
for (int i = 0; i < 16; ++i) {
IPCTestHelper::resetSocketFolder();
CHECK_THROWS(hyprland::IPC::getSocket1Reply("test_request"));
}
const auto after_connect_failures = countOpenFds();
REQUIRE(after_connect_failures == baseline);
}
#endif