From 09dc9e4c35bb1e7ea766b7a68b222ecd4eb1aea6 Mon Sep 17 00:00:00 2001 From: Dave Lane <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:12:10 -0400 Subject: [PATCH] chore: use lizardbyte-common in c++ (#5356) Co-authored-by: Kishi <41839133+Kishi85@users.noreply.github.com> --- .gitmodules | 4 -- README.md | 21 +++--- cmake/compile_definitions/common.cmake | 1 + cmake/dependencies/common.cmake | 5 ++ docs/Doxyfile | 2 +- src/platform/common.h | 15 ---- src/platform/linux/input/inputtino_seat.cpp | 10 ++- src/platform/linux/kwingrab.cpp | 23 +++---- src/platform/linux/misc.cpp | 68 ++++--------------- src/platform/linux/wayland.cpp | 11 +-- src/platform/macos/misc.mm | 8 --- src/platform/windows/display_base.cpp | 6 +- src/platform/windows/misc.cpp | 8 --- tests/CMakeLists.txt | 8 ++- tests/integration/test_config_consistency.cpp | 3 +- tests/integration/test_external_commands.cpp | 3 +- tests/integration/test_locale_consistency.cpp | 2 +- tests/tests_common.h | 4 +- tests/tests_events.h | 41 +++-------- tests/unit/platform/test_common.cpp | 42 ------------ .../unit/platform/windows/test_utf_utils.cpp | 2 +- tests/unit/test_audio.cpp | 1 + tests/unit/test_confighttp.cpp | 5 +- tests/unit/test_display_device.cpp | 2 +- tests/unit/test_file_handler.cpp | 6 +- tests/unit/test_http_pairing.cpp | 2 +- tests/unit/test_httpcommon.cpp | 6 +- tests/unit/test_logging.cpp | 2 +- tests/unit/test_mouse.cpp | 2 + tests/unit/test_network.cpp | 6 +- tests/unit/test_process.cpp | 4 +- tests/unit/test_video.cpp | 3 +- third-party/googletest | 1 - third-party/lizardbyte-common | 2 +- 34 files changed, 103 insertions(+), 226 deletions(-) delete mode 160000 third-party/googletest diff --git a/.gitmodules b/.gitmodules index 373fc9258..e1b90b926 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,10 +13,6 @@ [submodule "third-party/glad"] path = third-party/glad url = https://github.com/Dav1dde/glad.git -[submodule "third-party/googletest"] - path = third-party/googletest - url = https://github.com/google/googletest.git - branch = main [submodule "third-party/inputtino"] path = third-party/inputtino url = https://github.com/games-on-whales/inputtino.git diff --git a/README.md b/README.md index b9563661c..deb149dcd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@
- Sunshine icon + Sunshine icon

Sunshine

Self-hosted game stream host for Moonlight.

@@ -8,16 +12,15 @@ GitHub stars GitHub Releases Docker - GHCR - Flathub installs - Flathub Version - Winget Version - Gurubase + GHCR + Flathub installs + Flathub Version + Winget Version + Gurubase GitHub Workflow Status (CI) GitHub Workflow Status (localize) - Read the Docs - Codecov - SonarCloud + Codecov + SonarCloud ## ℹ️ About diff --git a/cmake/compile_definitions/common.cmake b/cmake/compile_definitions/common.cmake index 204a668eb..8f32cb5c6 100644 --- a/cmake/compile_definitions/common.cmake +++ b/cmake/compile_definitions/common.cmake @@ -150,6 +150,7 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} enet libdisplaydevice::display_device + lizardbyte::common nlohmann_json::nlohmann_json ${Opus_LIBRARY} ${FFMPEG_LIBRARIES} diff --git a/cmake/dependencies/common.cmake b/cmake/dependencies/common.cmake index ba20980f0..dc6886776 100644 --- a/cmake/dependencies/common.cmake +++ b/cmake/dependencies/common.cmake @@ -16,6 +16,11 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet") # web server add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/Simple-Web-Server") +# lizardbyte common helpers +set(LIZARDBYTE_COMMON_BUILD_TEST_SUPPORT ${BUILD_TESTS} + CACHE BOOL "Build lizardbyte-common GoogleTest support helpers" FORCE) +add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/lizardbyte-common") + # libdisplaydevice add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/libdisplaydevice") diff --git a/docs/Doxyfile b/docs/Doxyfile index 0c84d823d..74b3f3815 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -26,7 +26,7 @@ DOCSET_BUNDLE_ID = dev.lizardbyte.Sunshine DOCSET_PUBLISHER_ID = dev.lizardbyte.Sunshine.documentation PROJECT_BRIEF = "Self-hosted game stream host for Moonlight." PROJECT_ICON = ../sunshine.ico -PROJECT_LOGO = ../sunshine.png +PROJECT_LOGO = ../sunshine.svg PROJECT_NAME = Sunshine # project specific settings diff --git a/src/platform/common.h b/src/platform/common.h index 2bbcee8ff..24da9f53c 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -953,21 +953,6 @@ namespace platf { void restart(); - /** - * @brief Set an environment variable. - * @param name The name of the environment variable. - * @param value The value to set the environment variable to. - * @return 0 on success, non-zero on failure. - */ - int set_env(const std::string &name, const std::string &value); - - /** - * @brief Unset an environment variable. - * @param name The name of the environment variable. - * @return 0 on success, non-zero on failure. - */ - int unset_env(const std::string &name); - /** * @brief Platform buffer pointer and size for batched socket sends. */ diff --git a/src/platform/linux/input/inputtino_seat.cpp b/src/platform/linux/input/inputtino_seat.cpp index beb380838..e4f2a2101 100644 --- a/src/platform/linux/input/inputtino_seat.cpp +++ b/src/platform/linux/input/inputtino_seat.cpp @@ -2,8 +2,8 @@ * @file src/platform/linux/input/inputtino_seat.cpp * @brief Implementation for multi-seat naming (udev-only). */ -// standard includes -#include +// lib includes +#include // local includes #include "inputtino_seat.h" @@ -11,10 +11,8 @@ namespace platf::inputtino_seat { std::string get_target_seat() { - if (const char *seat = std::getenv("XDG_SEAT")) { - if (seat[0] != '\0') { - return seat; - } + if (std::string seat; lizardbyte::common::get_env("XDG_SEAT", seat) && !seat.empty()) { + return seat; } return {}; diff --git a/src/platform/linux/kwingrab.cpp b/src/platform/linux/kwingrab.cpp index fd3124870..6e0ba02f7 100644 --- a/src/platform/linux/kwingrab.cpp +++ b/src/platform/linux/kwingrab.cpp @@ -21,6 +21,7 @@ #include // lib includes +#include #include #include #include @@ -55,7 +56,7 @@ namespace kwin { * @return True when KWin reports that the permission system is disabled. */ static bool is_permission_system_deactivated() { - return getenvstr("KWIN_WAYLAND_NO_PERMISSION_CHECKS") == "1"; + return lizardbyte::common::get_env("KWIN_WAYLAND_NO_PERMISSION_CHECKS") == "1"; } /** @@ -160,17 +161,9 @@ namespace kwin { static inline bool initialized = false; static inline bool create_file = true; - static std::string getenvstr(std::string const &key) { - char const *val = std::getenv(key.c_str()); - if (!val) { - return ""; - } - return val; - } - static std::filesystem::path get_home_dir() { // Check HOME environment variable - if (std::string homedir = getenvstr("HOME"); !homedir.empty()) { + if (std::string homedir = lizardbyte::common::get_env("HOME"); !homedir.empty()) { return homedir; } // Fall back to home directory from NSS passwd @@ -182,7 +175,7 @@ namespace kwin { // Follow the XDG base directory specification for user data home: // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html std::filesystem::path xdg_data_home; - if (std::string dir = getenvstr("XDG_DATA_HOME"); !dir.empty()) { + if (std::string dir = lizardbyte::common::get_env("XDG_DATA_HOME"); !dir.empty()) { xdg_data_home = std::filesystem::path(dir); } else { const auto homedir = get_home_dir(); @@ -222,7 +215,7 @@ namespace kwin { static bool check_kwin_system_permissions(const std::string_view &filenameprefix, const std::string_view &executablepath) { // Find data dirs to check from XDG_DATA_DIRS std::vector xdg_data_dirs; - if (const std::string e = getenvstr("XDG_DATA_DIRS"); !e.empty()) { + if (const std::string e = lizardbyte::common::get_env("XDG_DATA_DIRS"); !e.empty()) { std::stringstream ss(e); std::string item; @@ -322,13 +315,13 @@ namespace kwin { screencast_permission_helper_t::setup(); } - const char *wl_name = std::getenv("WAYLAND_DISPLAY"); - if (!wl_name) { + std::string wl_name; + if (!lizardbyte::common::get_env("WAYLAND_DISPLAY", wl_name)) { BOOST_LOG(error) << "[kwingrab] WAYLAND_DISPLAY not set"sv; return -1; } - wl_display = wl_display_connect(wl_name); + wl_display = wl_display_connect(wl_name.c_str()); if (!wl_display) { BOOST_LOG(error) << "[kwingrab] cannot connect to Wayland display: "sv << wl_name; return -1; diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 1965e9f20..dae271d33 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #ifdef SUNSHINE_BUILD_DRM @@ -179,38 +180,35 @@ namespace platf { std::call_once(migration_flag, []() { bool found = false; bool migrate_config = true; - const char *dir; - const char *homedir; - const char *migrate_envvar; + fs::path homedir {lizardbyte::common::get_env("HOME")}; // Get the home directory - if ((homedir = getenv("HOME")) == nullptr || strlen(homedir) == 0) { + if (homedir.empty()) { // If HOME is empty or not set, use the current user's home directory homedir = getpwuid(geteuid())->pw_dir; } // May be set if running under a systemd service with the ConfigurationDirectory= option set. - if ((dir = getenv("CONFIGURATION_DIRECTORY")) != nullptr && strlen(dir) > 0) { + if (std::string dir; lizardbyte::common::get_env("CONFIGURATION_DIRECTORY", dir) && !dir.empty()) { found = true; config_path = fs::path(dir) / "sunshine"sv; } // Otherwise, follow the XDG base directory specification: // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - if (!found && (dir = getenv("XDG_CONFIG_HOME")) != nullptr && strlen(dir) > 0) { + if (std::string dir; !found && lizardbyte::common::get_env("XDG_CONFIG_HOME", dir) && !dir.empty()) { found = true; config_path = fs::path(dir) / "sunshine"sv; } // As a last resort, use the home directory if (!found) { migrate_config = false; - config_path = fs::path(homedir) / ".config/sunshine"sv; + config_path = homedir / ".config" / "sunshine"; } // migrate from the old config location if necessary - migrate_envvar = getenv("SUNSHINE_MIGRATE_CONFIG"); - if (migrate_config && found && migrate_envvar && strcmp(migrate_envvar, "1") == 0) { + if (std::string migrate_envvar; migrate_config && found && lizardbyte::common::get_env("SUNSHINE_MIGRATE_CONFIG", migrate_envvar) && migrate_envvar == "1") { std::error_code ec; - fs::path old_config_path = fs::path(homedir) / ".config/sunshine"sv; + fs::path old_config_path = homedir / ".config" / "sunshine"; if (old_config_path != config_path && fs::exists(old_config_path, ec)) { if (!fs::exists(config_path, ec)) { std::cout << "Migrating config from "sv << old_config_path << " to "sv << config_path << std::endl; @@ -365,7 +363,7 @@ namespace platf { */ void open_url(const std::string &url) { // set working dir to user home directory - auto working_dir = boost::filesystem::path(std::getenv("HOME")); + auto working_dir = boost::filesystem::path(lizardbyte::common::get_env("HOME")); std::string cmd = R"(xdg-open ")" + url + R"(")"; boost::process::v1::environment _env = boost::this_process::environment(); @@ -506,42 +504,6 @@ namespace platf { lifetime::exit_sunshine(0, true); } - /** - * @brief Read an environment variable as an optional string. - * - * @param name Human-readable name to assign. - * @return Environment variable value, or an empty string when unset. - */ - std::string get_env(const std::string &name) { - if (const auto value = getenv(name.c_str()); value != nullptr) { - return value; - } - return ""; - } - - int set_env(const std::string &name, const std::string &value) { - return setenv(name.c_str(), value.c_str(), 1); - } - - /** - * @brief Append a value to a separator-delimited environment variable. - * - * @param name Human-readable name to assign. - * @param value Entry to add when it is not already present. - * @param separator Character used to join or split the value. - * @return Result from updating the environment variable. - */ - int append_env(const std::string &name, const std::string &value, const std::string &separator) { - if (const std::string old_value = get_env(name); !old_value.contains(value)) { - return set_env(name, old_value.empty() ? value : old_value + separator + value); - } - return 0; - } - - int unset_env(const std::string &name) { - return unsetenv(name.c_str()); - } - bool request_process_group_exit(std::uintptr_t native_handle) { if (kill(-((pid_t) native_handle), SIGTERM) == 0 || errno == ESRCH) { BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle; @@ -1267,25 +1229,25 @@ namespace platf { std::unique_ptr init() { // enable low latency mode for AMD // https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30039 - set_env("AMD_DEBUG", "lowlatencyenc"); + lizardbyte::common::set_env("AMD_DEBUG", "lowlatencyenc"); // enable Vulkan video extensions for AMD RADV - set_env("RADV_PERFTEST", "video_encode"); + lizardbyte::common::set_env("RADV_PERFTEST", "video_encode"); // Above is deprecated on Mesa 26.1+ and replaced by (keep both to ensure best compatibility): - append_env("RADV_EXPERIMENTAL", "video_encode", ","); + lizardbyte::common::append_env("RADV_EXPERIMENTAL", "video_encode", ","); // These are allowed to fail. gbm::init(); window_system = window_system_e::NONE; #ifdef SUNSHINE_BUILD_WAYLAND - if (std::getenv("WAYLAND_DISPLAY")) { + if (std::string v; lizardbyte::common::get_env("WAYLAND_DISPLAY", v)) { window_system = window_system_e::WAYLAND; } #endif #if defined(SUNSHINE_BUILD_X11) || defined(SUNSHINE_BUILD_CUDA) - if (std::getenv("DISPLAY") && window_system != window_system_e::WAYLAND) { - if (std::getenv("WAYLAND_DISPLAY")) { + if (std::string v; lizardbyte::common::get_env("DISPLAY", v) && window_system != window_system_e::WAYLAND) { + if (lizardbyte::common::get_env("WAYLAND_DISPLAY", v)) { BOOST_LOG(warning) << "Wayland detected, yet sunshine will use X11 for screencasting, screencasting will only work on XWayland applications"sv; } diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index cdef9ae78..1be18e12d 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -2,9 +2,6 @@ * @file src/platform/linux/wayland.cpp * @brief Definitions for Wayland capture. */ -// standard includes -#include - // platform includes #include #include @@ -15,6 +12,9 @@ #include #include +// lib includes +#include + // local includes #include "graphics.h" #include "src/logging.h" @@ -53,8 +53,11 @@ namespace wl { }; int display_t::init(const char *display_name) { + std::string env_display_name; if (!display_name) { - display_name = std::getenv("WAYLAND_DISPLAY"); + if (lizardbyte::common::get_env("WAYLAND_DISPLAY", env_display_name)) { + display_name = env_display_name.c_str(); + } } if (!display_name) { diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index 56fca1340..7699556d4 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -296,14 +296,6 @@ namespace platf { lifetime::exit_sunshine(0, true); } - int set_env(const std::string &name, const std::string &value) { - return setenv(name.c_str(), value.c_str(), 1); - } - - int unset_env(const std::string &name) { - return unsetenv(name.c_str()); - } - bool request_process_group_exit(std::uintptr_t native_handle) { if (killpg((pid_t) native_handle, SIGTERM) == 0 || errno == ESRCH) { BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle; diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 071efc355..00b4abf8c 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -520,8 +520,7 @@ namespace platf::dxgi { height = desc.DesktopCoordinates.bottom - offset_y; display_rotation = desc.Rotation; - if (display_rotation == DXGI_MODE_ROTATION_ROTATE90 || - display_rotation == DXGI_MODE_ROTATION_ROTATE270) { + if (display_rotation == DXGI_MODE_ROTATION_ROTATE90 || display_rotation == DXGI_MODE_ROTATION_ROTATE270) { width_before_rotation = height; height_before_rotation = width; } else { @@ -621,8 +620,7 @@ namespace platf::dxgi { HANDLE token; LUID val; - if (OpenProcessToken(GetCurrentProcess(), flags, &token) && - !!LookupPrivilegeValue(nullptr, SE_INC_BASE_PRIORITY_NAME, &val)) { + if (OpenProcessToken(GetCurrentProcess(), flags, &token) && !!LookupPrivilegeValue(nullptr, SE_INC_BASE_PRIORITY_NAME, &val)) { tp.PrivilegeCount = 1; tp.Privileges[0].Luid = val; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index ee2ec7af5..25f509981 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1324,14 +1324,6 @@ namespace platf { lifetime::exit_sunshine(0, true); } - int set_env(const std::string &name, const std::string &value) { - return _putenv_s(name.c_str(), value.c_str()); - } - - int unset_env(const std::string &name) { - return _putenv_s(name.c_str(), ""); - } - /** * @brief Stores state while enumerating top-level Windows windows. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c38547f4d..1ebeea959 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,10 +15,12 @@ include_directories("${CMAKE_SOURCE_DIR}") enable_testing() # Add GoogleTest directory to the project -set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest") +set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/lizardbyte-common/third-party/googletest") set(INSTALL_GTEST OFF) set(INSTALL_GMOCK OFF) -add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest") +if(NOT TARGET gtest) + add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest") +endif() include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}") # coverage @@ -167,7 +169,7 @@ add_dependencies(${PROJECT_NAME} sync_locale_files) # Build the list of libraries to link set(TEST_LINK_LIBRARIES ${SUNSHINE_EXTERNAL_LIBRARIES} - gtest + lizardbyte::test_support ${PLATFORM_LIBRARIES} ) diff --git a/tests/integration/test_config_consistency.cpp b/tests/integration/test_config_consistency.cpp index 821c5923d..412db71f5 100644 --- a/tests/integration/test_config_consistency.cpp +++ b/tests/integration/test_config_consistency.cpp @@ -20,9 +20,10 @@ // local includes #include "src/file_handler.h" -class ConfigConsistencyTest: public ::testing::Test { +class ConfigConsistencyTest: public BaseTest { protected: void SetUp() override { + BaseTest::SetUp(); // Define the expected mapping between documentation sections and UI tabs expectedDocToTabMapping = { {"General", "general"}, diff --git a/tests/integration/test_external_commands.cpp b/tests/integration/test_external_commands.cpp index 92ec159e3..bbf2bb23f 100644 --- a/tests/integration/test_external_commands.cpp +++ b/tests/integration/test_external_commands.cpp @@ -37,9 +37,10 @@ struct ExternalCommandTestData { xfail_reason(std::move(xfail_rsn)) {} }; -class ExternalCommandTest: public ::testing::TestWithParam { +class ExternalCommandTest: public BaseTest, public ::testing::WithParamInterface { protected: void SetUp() override { + BaseTest::SetUp(); if constexpr (IS_WINDOWS) { current_platform = "windows"; } else if constexpr (IS_MACOS) { diff --git a/tests/integration/test_locale_consistency.cpp b/tests/integration/test_locale_consistency.cpp index 3fdc7ef49..c842e3174 100644 --- a/tests/integration/test_locale_consistency.cpp +++ b/tests/integration/test_locale_consistency.cpp @@ -23,7 +23,7 @@ namespace fs = std::filesystem; -class LocaleConsistencyTest: public ::testing::Test { +class LocaleConsistencyTest: public BaseTest { protected: // Extract locale options from config.cpp static std::set> extractConfigCppLocales() { diff --git a/tests/tests_common.h b/tests/tests_common.h index fe4ce1a03..5025943fe 100644 --- a/tests/tests_common.h +++ b/tests/tests_common.h @@ -12,7 +12,7 @@ #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif -#include +#include #include #include #include @@ -146,7 +146,7 @@ namespace test_utils { #define IS_FREEBSD false #endif -struct PlatformTestSuite: testing::Test { +struct PlatformTestSuite: BaseTest { static void SetUpTestSuite() { ASSERT_FALSE(platf_deinit); BOOST_LOG(tests) << "Setting up platform test suite"; diff --git a/tests/tests_events.h b/tests/tests_events.h index 63c27c160..be649f084 100644 --- a/tests/tests_events.h +++ b/tests/tests_events.h @@ -5,7 +5,7 @@ #pragma once #include "tests_common.h" -struct SunshineEventListener: testing::EmptyTestEventListener { +struct SunshineEventListener: BufferedTestEventListener { SunshineEventListener() { sink = boost::make_shared(); sink_buffer = boost::make_shared(); @@ -14,51 +14,30 @@ struct SunshineEventListener: testing::EmptyTestEventListener { } void OnTestProgramStart(const testing::UnitTest &unit_test) override { + static_cast(unit_test); boost::log::core::get()->add_sink(sink); } void OnTestProgramEnd(const testing::UnitTest &unit_test) override { + static_cast(unit_test); boost::log::core::get()->remove_sink(sink); } - void OnTestStart(const testing::TestInfo &test_info) override { - BOOST_LOG(tests) << "From " << test_info.file() << ":" << test_info.line(); - BOOST_LOG(tests) << " " << test_info.test_suite_name() << "/" << test_info.name() << " started"; +protected: + void logTestEvent(const std::string &message) override { + BOOST_LOG(tests) << message; } - void OnTestPartResult(const testing::TestPartResult &test_part_result) override { - std::string file = test_part_result.file_name(); - BOOST_LOG(tests) << "At " << file << ":" << test_part_result.line_number(); - - auto result_text = test_part_result.passed() ? "Success" : - test_part_result.nonfatally_failed() ? "Non-fatal failure" : - test_part_result.fatally_failed() ? "Failure" : - "Skip"; - - std::string summary = test_part_result.summary(); - std::string message = test_part_result.message(); - BOOST_LOG(tests) << " " << result_text << ": " << summary; - if (message != summary) { - BOOST_LOG(tests) << " " << message; - } + [[nodiscard]] std::string bufferedTestOutput() const override { + return sink_buffer->str(); } - void OnTestEnd(const testing::TestInfo &test_info) override { - auto &result = *test_info.result(); - - auto result_text = result.Passed() ? "passed" : - result.Skipped() ? "skipped" : - "failed"; - BOOST_LOG(tests) << test_info.test_suite_name() << "/" << test_info.name() << " " << result_text; - - if (result.Failed()) { - std::cout << sink_buffer->str(); - } - + void clearBufferedTestOutput() override { sink_buffer->str(""); sink_buffer->clear(); } +private: using sink_t = boost::log::sinks::synchronous_sink; boost::shared_ptr sink; boost::shared_ptr sink_buffer; diff --git a/tests/unit/platform/test_common.cpp b/tests/unit/platform/test_common.cpp index ff7372088..b497d3581 100644 --- a/tests/unit/platform/test_common.cpp +++ b/tests/unit/platform/test_common.cpp @@ -7,48 +7,6 @@ #include #include -struct SetEnvTest: ::testing::TestWithParam> { -protected: - void TearDown() override { - // Clean up environment variable after each test - const auto &[name, value, expected] = GetParam(); - platf::unset_env(name); - } -}; - -TEST_P(SetEnvTest, SetEnvironmentVariableTests) { - const auto &[name, value, expected] = GetParam(); - platf::set_env(name, value); - - const char *env_value = std::getenv(name.c_str()); - if (expected == 0 && !value.empty()) { - ASSERT_NE(env_value, nullptr); - ASSERT_EQ(std::string(env_value), value); - } else { - ASSERT_EQ(env_value, nullptr); - } -} - -TEST_P(SetEnvTest, UnsetEnvironmentVariableTests) { - const auto &[name, value, expected] = GetParam(); - platf::unset_env(name); - - const char *env_value = std::getenv(name.c_str()); - if (expected == 0) { - ASSERT_EQ(env_value, nullptr); - } -} - -INSTANTIATE_TEST_SUITE_P( - SetEnvTests, - SetEnvTest, - ::testing::Values( - std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_0", 0), - std::make_tuple("SUNSHINE_UNIT_TEST_ENV_VAR", "test_value_1", 0), - std::make_tuple("", "test_value", -1) - ) -); - TEST(HostnameTests, TestAsioEquality) { // These should be equivalent on all platforms for ASCII hostnames ASSERT_EQ(platf::get_host_name(), boost::asio::ip::host_name()); diff --git a/tests/unit/platform/windows/test_utf_utils.cpp b/tests/unit/platform/windows/test_utf_utils.cpp index d7afc41b1..bd62218ef 100644 --- a/tests/unit/platform/windows/test_utf_utils.cpp +++ b/tests/unit/platform/windows/test_utf_utils.cpp @@ -16,7 +16,7 @@ /** * @brief Test fixture for utf_utils namespace functions */ -class UtfUtilsTest: public testing::Test {}; +class UtfUtilsTest: public BaseTest {}; TEST_F(UtfUtilsTest, FromUtf8WithEmptyString) { const std::string empty_string = ""; diff --git a/tests/unit/test_audio.cpp b/tests/unit/test_audio.cpp index 648b838ea..9b894112b 100644 --- a/tests/unit/test_audio.cpp +++ b/tests/unit/test_audio.cpp @@ -10,6 +10,7 @@ using namespace audio; struct AudioTest: PlatformTestSuite, testing::WithParamInterface, config_t>> { void SetUp() override { + BaseTest::SetUp(); m_config = std::get<1>(GetParam()); m_mail = std::make_shared(); } diff --git a/tests/unit/test_confighttp.cpp b/tests/unit/test_confighttp.cpp index 195a666c2..a633ae3b5 100644 --- a/tests/unit/test_confighttp.cpp +++ b/tests/unit/test_confighttp.cpp @@ -89,7 +89,7 @@ X4wnh1bwdiidqpcgyuKossLOPxbS786WmsesaAWPnpoY6M8aija+ALwNNuWWmyMg * * This fixture creates a real server to test the actual confighttp functions. */ -class ConfigHttpTest: public ::testing::Test { // NOSONAR(cpp:S3656) - protected members are intentional for test fixture subclassing +class ConfigHttpTest: public BaseTest { // NOSONAR(cpp:S3656) - protected members are intentional for test fixture subclassing protected: std::unique_ptr> server; std::unique_ptr> client; @@ -107,6 +107,7 @@ protected: std::filesystem::path web_dir_test_file; void SetUp() override { + BaseTest::SetUp(); // Save current config saved_username = config::sunshine.username; saved_password = config::sunshine.password; @@ -354,6 +355,7 @@ protected: if (std::filesystem::exists(test_web_dir)) { std::filesystem::remove_all(test_web_dir); } + BaseTest::TearDown(); } static std::string create_auth_header(const std::string &username, const std::string &password) { @@ -1251,7 +1253,6 @@ TEST_F(BrowseDirectoryTest, IsBrowsableExecutable_LinuxGroupExecBit_ReturnsTrue) TEST_F(BrowseDirectoryTest, BuildBrowseEntries_TypeAny_ReturnsAllEntries) { const auto entries = confighttp::build_browse_entries(browse_test_dir, "any"); ASSERT_TRUE(entries.is_array()); - // subdir_a, subdir_b, file_alpha.txt, file_beta.txt, test_exec[.exe] = 5 ASSERT_EQ(entries.size(), 5u); } diff --git a/tests/unit/test_display_device.cpp b/tests/unit/test_display_device.cpp index 5fd00d31f..9b20f876b 100644 --- a/tests/unit/test_display_device.cpp +++ b/tests/unit/test_display_device.cpp @@ -43,7 +43,7 @@ namespace { const std::string max_uint_string {std::to_string(std::numeric_limits::max())}; template - struct DisplayDeviceConfigTest: testing::TestWithParam {}; + struct DisplayDeviceConfigTest: BaseTest, testing::WithParamInterface {}; } // namespace using ParseDeviceId = DisplayDeviceConfigTest>; diff --git a/tests/unit/test_file_handler.cpp b/tests/unit/test_file_handler.cpp index cf5b8a1f5..4ec53f2e9 100644 --- a/tests/unit/test_file_handler.cpp +++ b/tests/unit/test_file_handler.cpp @@ -7,7 +7,7 @@ #include #include -struct FileHandlerParentDirectoryTest: testing::TestWithParam> {}; +struct FileHandlerParentDirectoryTest: BaseTest, testing::WithParamInterface> {}; TEST_P(FileHandlerParentDirectoryTest, Run) { auto [input, expected] = GetParam(); @@ -24,7 +24,7 @@ INSTANTIATE_TEST_SUITE_P( ) ); -struct FileHandlerMakeDirectoryTest: testing::TestWithParam> {}; +struct FileHandlerMakeDirectoryTest: BaseTest, testing::WithParamInterface> {}; TEST_P(FileHandlerMakeDirectoryTest, Run) { auto [input, expected, remove] = GetParam(); @@ -52,7 +52,7 @@ INSTANTIATE_TEST_SUITE_P( ) ); -struct FileHandlerTests: testing::TestWithParam> {}; +struct FileHandlerTests: BaseTest, testing::WithParamInterface> {}; INSTANTIATE_TEST_SUITE_P( TestFiles, diff --git a/tests/unit/test_http_pairing.cpp b/tests/unit/test_http_pairing.cpp index 0b75e32b4..b9a06768a 100644 --- a/tests/unit/test_http_pairing.cpp +++ b/tests/unit/test_http_pairing.cpp @@ -76,7 +76,7 @@ X4wnh1bwdiidqpcgyuKossLOPxbS786WmsesaAWPnpoY6M8aija+ALwNNuWWmyMg 9SVDV76xJzM36Uq7Kg3QJYTlY04WmPIdJHkCtXWf9g== -----END CERTIFICATE-----)"; -struct PairingTest: testing::TestWithParam> {}; +struct PairingTest: BaseTest, testing::WithParamInterface> {}; TEST_P(PairingTest, Run) { auto [input, expected] = GetParam(); diff --git a/tests/unit/test_httpcommon.cpp b/tests/unit/test_httpcommon.cpp index a5a6c4886..2f7af0c58 100644 --- a/tests/unit/test_httpcommon.cpp +++ b/tests/unit/test_httpcommon.cpp @@ -11,7 +11,7 @@ // local imports #include -struct UrlEscapeTest: testing::TestWithParam> {}; +struct UrlEscapeTest: BaseTest, testing::WithParamInterface> {}; TEST_P(UrlEscapeTest, Run) { const auto &[input, expected] = GetParam(); @@ -28,7 +28,7 @@ INSTANTIATE_TEST_SUITE_P( ) ); -struct UrlGetHostTest: testing::TestWithParam> {}; +struct UrlGetHostTest: BaseTest, testing::WithParamInterface> {}; TEST_P(UrlGetHostTest, Run) { const auto &[input, expected] = GetParam(); @@ -45,7 +45,7 @@ INSTANTIATE_TEST_SUITE_P( ) ); -struct DownloadFileTest: testing::TestWithParam> {}; +struct DownloadFileTest: BaseTest, testing::WithParamInterface> {}; TEST_P(DownloadFileTest, Run) { const auto &[url, filename] = GetParam(); diff --git a/tests/unit/test_logging.cpp b/tests/unit/test_logging.cpp index cc6388598..e1ffb6adc 100644 --- a/tests/unit/test_logging.cpp +++ b/tests/unit/test_logging.cpp @@ -22,7 +22,7 @@ namespace { constexpr auto log_file = "test_sunshine.log"; } // namespace -struct LogLevelsTest: testing::TestWithParam {}; +struct LogLevelsTest: BaseTest, testing::WithParamInterface {}; INSTANTIATE_TEST_SUITE_P( Logging, diff --git a/tests/unit/test_mouse.cpp b/tests/unit/test_mouse.cpp index c2935e54b..eda5fd02e 100644 --- a/tests/unit/test_mouse.cpp +++ b/tests/unit/test_mouse.cpp @@ -8,6 +8,7 @@ struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface { void SetUp() override { + BaseTest::SetUp(); #ifdef _WIN32 // TODO: Windows tests are failing, `get_mouse_loc` seems broken and `platf::abs_mouse` too // the alternative `platf::abs_mouse` method seem to work better during tests, @@ -21,6 +22,7 @@ struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface -struct MdnsInstanceNameTest: testing::TestWithParam> {}; +struct MdnsInstanceNameTest: BaseTest, testing::WithParamInterface> {}; TEST_P(MdnsInstanceNameTest, Run) { auto [input, expected] = GetParam(); @@ -30,11 +30,12 @@ INSTANTIATE_TEST_SUITE_P( /** * @brief Test fixture for bind_address tests with setup/teardown */ -class BindAddressTest: public ::testing::Test { +class BindAddressTest: public BaseTest { protected: std::string original_bind_address; void SetUp() override { + BaseTest::SetUp(); // Save the original bind_address config original_bind_address = config::sunshine.bind_address; } @@ -42,6 +43,7 @@ protected: void TearDown() override { // Restore the original bind_address config config::sunshine.bind_address = original_bind_address; + BaseTest::TearDown(); } }; diff --git a/tests/unit/test_process.cpp b/tests/unit/test_process.cpp index 14ac6924c..0e4caf525 100644 --- a/tests/unit/test_process.cpp +++ b/tests/unit/test_process.cpp @@ -14,9 +14,10 @@ namespace fs = std::filesystem; -class ProcessPNGTest: public ::testing::Test { +class ProcessPNGTest: public BaseTest { protected: void SetUp() override { + BaseTest::SetUp(); // Create test directory test_dir = fs::temp_directory_path() / "sunshine_process_png_test"; // NOSONAR(cpp:S5443) - safe for tests fs::create_directories(test_dir); @@ -27,6 +28,7 @@ protected: if (fs::exists(test_dir)) { fs::remove_all(test_dir); } + BaseTest::TearDown(); } // Helper function to create a file with specific content diff --git a/tests/unit/test_video.cpp b/tests/unit/test_video.cpp index e71fb1d8f..b9c926bf8 100644 --- a/tests/unit/test_video.cpp +++ b/tests/unit/test_video.cpp @@ -8,6 +8,7 @@ struct EncoderTest: PlatformTestSuite, testing::WithParamInterface { void SetUp() override { + BaseTest::SetUp(); auto &encoder = *GetParam(); if (!video::validate_encoder(encoder, false)) { // Encoder failed validation, @@ -49,7 +50,7 @@ TEST_P(EncoderTest, ValidateEncoder) { // todo:: test something besides fixture setup } -struct FramerateX100Test: testing::TestWithParam> {}; +struct FramerateX100Test: BaseTest, testing::WithParamInterface> {}; TEST_P(FramerateX100Test, Run) { const auto &[x100, expected] = GetParam(); diff --git a/third-party/googletest b/third-party/googletest deleted file mode 160000 index 52eb8108c..000000000 --- a/third-party/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 52eb8108c5bdec04579160ae17225d66034bd723 diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index bd1b0bb88..06cd442b8 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit bd1b0bb88d47f953875170151d86ae464a7175e8 +Subproject commit 06cd442b808f02f1674f3192a84d25b9a503c482