From 2c433defe68399bd884017b7b466f55f29494c25 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 28 Jun 2026 20:13:55 -0400 Subject: [PATCH 01/15] Use lizardbyte-common env helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire `lizardbyte::common` into the build and replace Sunshine’s platform-specific `set_env`/`unset_env` implementations with shared helpers from `lizardbyte-common` (including Linux `append_env` usage for RADV flags). This removes duplicated environment-variable code from platform layers, drops the now-obsolete env wrapper declarations and unit tests, and updates the `third-party/lizardbyte-common` submodule to the revision that provides the shared env utilities. --- cmake/compile_definitions/common.cmake | 1 + cmake/dependencies/common.cmake | 4 +++ src/platform/common.h | 14 --------- src/platform/linux/misc.cpp | 42 +++----------------------- src/platform/macos/misc.mm | 7 ----- src/platform/windows/misc.cpp | 7 ----- tests/unit/platform/test_common.cpp | 42 -------------------------- third-party/lizardbyte-common | 2 +- 8 files changed, 10 insertions(+), 109 deletions(-) diff --git a/cmake/compile_definitions/common.cmake b/cmake/compile_definitions/common.cmake index 204a668eb1d..8f32cb5c65e 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 ba20980f05b..2c240152c1d 100644 --- a/cmake/dependencies/common.cmake +++ b/cmake/dependencies/common.cmake @@ -16,6 +16,10 @@ 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 OFF 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/src/platform/common.h b/src/platform/common.h index 2bbcee8ff07..a60ed896ff3 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -953,20 +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/misc.cpp b/src/platform/linux/misc.cpp index 1965e9f20ae..5cd6a0b8019 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -506,41 +507,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) { @@ -1267,12 +1233,12 @@ 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(); diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index 56fca134028..5ea7e4dad5c 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -296,13 +296,6 @@ void restart() { 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) { diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index ee2ec7af5de..5b0916d4c31 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1324,13 +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/unit/platform/test_common.cpp b/tests/unit/platform/test_common.cpp index ff7372088d6..b497d35815f 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/third-party/lizardbyte-common b/third-party/lizardbyte-common index bd1b0bb88d4..4c05418359b 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit bd1b0bb88d47f953875170151d86ae464a7175e8 +Subproject commit 4c05418359b38b09f3d5802243858a1d462a3b06 From f36f8f9247181425bb00aa6ed3d21312d2c89fbe Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:05:37 -0400 Subject: [PATCH 02/15] Use lizardbyte test support for tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch test infrastructure from the standalone googletest submodule to lizardbyte-common’s bundled GoogleTest and `lizardbyte::test_support`. CMake now enables common test helpers when `BUILD_TESTS` is on, avoids re-adding gtest if it already exists, and removes the `third-party/googletest` submodule. Test fixtures were updated to inherit from `BaseTest` (and `testing::WithParamInterface` for parameterized cases) to align with the shared test framework. --- .gitmodules | 4 ---- cmake/dependencies/common.cmake | 2 +- tests/CMakeLists.txt | 8 +++++--- tests/integration/test_config_consistency.cpp | 2 +- tests/integration/test_external_commands.cpp | 2 +- tests/integration/test_locale_consistency.cpp | 2 +- tests/tests_common.h | 4 ++-- tests/unit/platform/windows/test_utf_utils.cpp | 2 +- tests/unit/test_confighttp.cpp | 2 +- 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_network.cpp | 4 ++-- tests/unit/test_process.cpp | 2 +- tests/unit/test_video.cpp | 2 +- third-party/googletest | 1 - 18 files changed, 26 insertions(+), 29 deletions(-) delete mode 160000 third-party/googletest diff --git a/.gitmodules b/.gitmodules index 373fc925811..e1b90b92633 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/cmake/dependencies/common.cmake b/cmake/dependencies/common.cmake index 2c240152c1d..3cff3bf76a5 100644 --- a/cmake/dependencies/common.cmake +++ b/cmake/dependencies/common.cmake @@ -17,7 +17,7 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet") add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/Simple-Web-Server") # lizardbyte common helpers -set(LIZARDBYTE_COMMON_BUILD_TEST_SUPPORT OFF CACHE BOOL "Build lizardbyte-common GoogleTest support helpers" FORCE) +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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c38547f4d57..1ebeea95981 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 821c5923df7..2a5efc5751d 100644 --- a/tests/integration/test_config_consistency.cpp +++ b/tests/integration/test_config_consistency.cpp @@ -20,7 +20,7 @@ // local includes #include "src/file_handler.h" -class ConfigConsistencyTest: public ::testing::Test { +class ConfigConsistencyTest: public BaseTest { protected: void SetUp() override { // Define the expected mapping between documentation sections and UI tabs diff --git a/tests/integration/test_external_commands.cpp b/tests/integration/test_external_commands.cpp index 92ec159e3e2..1a5d09b35a4 100644 --- a/tests/integration/test_external_commands.cpp +++ b/tests/integration/test_external_commands.cpp @@ -37,7 +37,7 @@ struct ExternalCommandTestData { xfail_reason(std::move(xfail_rsn)) {} }; -class ExternalCommandTest: public ::testing::TestWithParam { +class ExternalCommandTest: public BaseTest, public ::testing::WithParamInterface { protected: void SetUp() override { if constexpr (IS_WINDOWS) { diff --git a/tests/integration/test_locale_consistency.cpp b/tests/integration/test_locale_consistency.cpp index 3fdc7ef496f..c842e31744f 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 fe4ce1a0365..5025943fe1e 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/unit/platform/windows/test_utf_utils.cpp b/tests/unit/platform/windows/test_utf_utils.cpp index d7afc41b134..bd62218efa6 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_confighttp.cpp b/tests/unit/test_confighttp.cpp index 195a666c2c1..916f708076d 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; diff --git a/tests/unit/test_display_device.cpp b/tests/unit/test_display_device.cpp index 5fd00d31f34..9b20f876b48 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 cf5b8a1f5ab..4ec53f2e98c 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 0b75e32b4e5..b9a06768ae9 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 a5a6c4886e1..2f7af0c585f 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 cc638859892..e1ffb6adc63 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_network.cpp b/tests/unit/test_network.cpp index ef150539b03..098be49bad5 100644 --- a/tests/unit/test_network.cpp +++ b/tests/unit/test_network.cpp @@ -6,7 +6,7 @@ #include -struct MdnsInstanceNameTest: testing::TestWithParam> {}; +struct MdnsInstanceNameTest: BaseTest, testing::WithParamInterface> {}; TEST_P(MdnsInstanceNameTest, Run) { auto [input, expected] = GetParam(); @@ -30,7 +30,7 @@ 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; diff --git a/tests/unit/test_process.cpp b/tests/unit/test_process.cpp index 14ac6924ce5..d07c0ade233 100644 --- a/tests/unit/test_process.cpp +++ b/tests/unit/test_process.cpp @@ -14,7 +14,7 @@ namespace fs = std::filesystem; -class ProcessPNGTest: public ::testing::Test { +class ProcessPNGTest: public BaseTest { protected: void SetUp() override { // Create test directory diff --git a/tests/unit/test_video.cpp b/tests/unit/test_video.cpp index e71fb1d8f6d..034b7bda8d8 100644 --- a/tests/unit/test_video.cpp +++ b/tests/unit/test_video.cpp @@ -49,7 +49,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 52eb8108c5b..00000000000 --- a/third-party/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 52eb8108c5bdec04579160ae17225d66034bd723 From 4c78849a128baaddb1600e91a269504b79ce005a Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:18:18 -0400 Subject: [PATCH 03/15] Refactor test listener to buffered base Update `SunshineEventListener` to inherit from `BufferedTestEventListener` and delegate event formatting to the shared base hooks (`logTestEvent`, buffered output getters/clearers). This removes duplicated per-event handling logic in `tests_events.h` while preserving Boost log buffering behavior. The `third-party/lizardbyte-common` submodule is bumped to the revision that provides the new shared listener implementation. --- tests/tests_events.h | 41 +++++++++-------------------------- third-party/lizardbyte-common | 2 +- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/tests/tests_events.h b/tests/tests_events.h index 63c27c16017..be649f084dd 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/third-party/lizardbyte-common b/third-party/lizardbyte-common index 4c05418359b..0fcde420419 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit 4c05418359b38b09f3d5802243858a1d462a3b06 +Subproject commit 0fcde420419c77fdde04ffb04ceda0b560bd1cfc From ee95334736562d99ef2cdec7372bc008d4d67c8f Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 28 Jun 2026 23:28:43 -0400 Subject: [PATCH 04/15] fix clang-format --- src/platform/common.h | 1 - src/platform/linux/misc.cpp | 3 +-- src/platform/macos/misc.mm | 1 - src/platform/windows/display_base.cpp | 2 +- src/platform/windows/misc.cpp | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/platform/common.h b/src/platform/common.h index a60ed896ff3..24da9f53cfa 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -953,7 +953,6 @@ namespace platf { void restart(); - /** * @brief Platform buffer pointer and size for batched socket sends. */ diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 5cd6a0b8019..30ed5f97151 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -43,8 +43,8 @@ #include #include #include -#include #include +#include #include #ifdef SUNSHINE_BUILD_DRM @@ -507,7 +507,6 @@ namespace platf { lifetime::exit_sunshine(0, true); } - 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; diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index 5ea7e4dad5c..7699556d470 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -296,7 +296,6 @@ void restart() { lifetime::exit_sunshine(0, true); } - 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 071efc355c3..3a62e3b086a 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -28,7 +28,7 @@ typedef long NTSTATUS; /** * @brief Enumerates supported d3 DKMT GPU PREFERENCE QUERY STATE options. */ -typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE: DWORD { +typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE : DWORD { D3DKMT_GPU_PREFERENCE_STATE_UNINITIALIZED, ///< The GPU preference isn't initialized. D3DKMT_GPU_PREFERENCE_STATE_HIGH_PERFORMANCE, ///< The highest performing GPU is preferred. D3DKMT_GPU_PREFERENCE_STATE_MINIMUM_POWER, ///< The minimum-powered GPU is preferred. diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 5b0916d4c31..25f5099810b 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1324,7 +1324,6 @@ namespace platf { lifetime::exit_sunshine(0, true); } - /** * @brief Stores state while enumerating top-level Windows windows. */ From d7cfcbf54740f9c594166fed1cae3372147b2e5b Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 28 Jun 2026 23:45:55 -0400 Subject: [PATCH 05/15] Bump lizardbyte-common submodule Update `third-party/lizardbyte-common` to commit `d219f38090db20119906c80860833592a6be669a` to pull in the latest upstream changes. --- third-party/lizardbyte-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index 0fcde420419..d219f38090d 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit 0fcde420419c77fdde04ffb04ceda0b560bd1cfc +Subproject commit d219f38090db20119906c80860833592a6be669a From 9164e452f209a15ddd2389ab17c27e815bc6a352 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:16:44 -0400 Subject: [PATCH 06/15] Call BaseTest hooks in test fixtures Ensure integration and unit test fixtures invoke BaseTest::SetUp() and BaseTest::TearDown() so shared test initialization/cleanup always runs. This improves test consistency and avoids missing base fixture state. Also applies minor formatting-only cleanup in Windows display_base.cpp. --- src/platform/windows/display_base.cpp | 6 ++---- tests/integration/test_config_consistency.cpp | 1 + tests/integration/test_external_commands.cpp | 1 + tests/unit/test_audio.cpp | 1 + tests/unit/test_confighttp.cpp | 2 ++ tests/unit/test_mouse.cpp | 2 ++ tests/unit/test_network.cpp | 2 ++ tests/unit/test_process.cpp | 2 ++ tests/unit/test_video.cpp | 1 + 9 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 3a62e3b086a..b297b90ae23 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/tests/integration/test_config_consistency.cpp b/tests/integration/test_config_consistency.cpp index 2a5efc5751d..412db71f547 100644 --- a/tests/integration/test_config_consistency.cpp +++ b/tests/integration/test_config_consistency.cpp @@ -23,6 +23,7 @@ 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 1a5d09b35a4..bbf2bb23f00 100644 --- a/tests/integration/test_external_commands.cpp +++ b/tests/integration/test_external_commands.cpp @@ -40,6 +40,7 @@ struct ExternalCommandTestData { 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/unit/test_audio.cpp b/tests/unit/test_audio.cpp index 648b838ea17..9b894112b30 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 916f708076d..dd20332a266 100644 --- a/tests/unit/test_confighttp.cpp +++ b/tests/unit/test_confighttp.cpp @@ -107,6 +107,7 @@ class ConfigHttpTest: public BaseTest { // NOSONAR(cpp:S3656) - protected membe 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 @@ class ConfigHttpTest: public BaseTest { // NOSONAR(cpp:S3656) - protected membe 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) { diff --git a/tests/unit/test_mouse.cpp b/tests/unit/test_mouse.cpp index c2935e54b65..eda5fd02ed5 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 { void SetUp() override { + BaseTest::SetUp(); auto &encoder = *GetParam(); if (!video::validate_encoder(encoder, false)) { // Encoder failed validation, From f6b59f6314980ba0460cc0b669367500ac788cec Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:23:00 -0400 Subject: [PATCH 07/15] Update lizardbyte-common submodule Bump `third-party/lizardbyte-common` from `d219f380` to `60bf936e` to pull in the latest upstream changes. --- third-party/lizardbyte-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index d219f38090d..60bf936e134 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit d219f38090db20119906c80860833592a6be669a +Subproject commit 60bf936e134359c65c8a044369db227f976947e5 From bdd23eec9d92974ad7be399727e35bdfbaf5856b Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:46:43 -0400 Subject: [PATCH 08/15] Fix lint errors --- cmake/dependencies/common.cmake | 3 ++- src/platform/windows/display_base.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/dependencies/common.cmake b/cmake/dependencies/common.cmake index 3cff3bf76a5..dc688677655 100644 --- a/cmake/dependencies/common.cmake +++ b/cmake/dependencies/common.cmake @@ -17,7 +17,8 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet") 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) +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 diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index b297b90ae23..00b4abf8c2a 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -28,7 +28,7 @@ typedef long NTSTATUS; /** * @brief Enumerates supported d3 DKMT GPU PREFERENCE QUERY STATE options. */ -typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE : DWORD { +typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE: DWORD { D3DKMT_GPU_PREFERENCE_STATE_UNINITIALIZED, ///< The GPU preference isn't initialized. D3DKMT_GPU_PREFERENCE_STATE_HIGH_PERFORMANCE, ///< The highest performing GPU is preferred. D3DKMT_GPU_PREFERENCE_STATE_MINIMUM_POWER, ///< The minimum-powered GPU is preferred. From 74c8350aa98eb5e1bab7c3dd9d6f9ed0c35f2b35 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:02:30 -0400 Subject: [PATCH 09/15] Bump lizardbyte-common submodule Updates the `third-party/lizardbyte-common` submodule reference to commit `3f29834c942d71d626f2daf67323fc61426bf524` from `60bf936e134359c65c8a044369db227f976947e5`. --- third-party/lizardbyte-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index 60bf936e134..3f29834c942 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit 60bf936e134359c65c8a044369db227f976947e5 +Subproject commit 3f29834c942d71d626f2daf67323fc61426bf524 From 16d05d9dd41e08147661f1f7db49c601cc63939c Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:20:47 -0400 Subject: [PATCH 10/15] Use common env helper in Linux platform code Replace direct `std::getenv`/custom wrappers with `lizardbyte::common::get_env` across Linux input, KWin grab, misc, and Wayland modules. This removes duplicated env parsing logic, standardizes empty/missing-variable handling, and updates Wayland display connection code to work with the new string-based env access. Co-Authored-By: Kishi <41839133+Kishi85@users.noreply.github.com> --- src/platform/linux/input/inputtino_seat.cpp | 10 ++++----- src/platform/linux/kwingrab.cpp | 23 +++++++-------------- src/platform/linux/misc.cpp | 21 ++++++++----------- src/platform/linux/wayland.cpp | 11 ++++++---- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/platform/linux/input/inputtino_seat.cpp b/src/platform/linux/input/inputtino_seat.cpp index beb38083803..e4f2a210157 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 fd31248708e..6e0ba02f77d 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 30ed5f97151..86450fed3b8 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -180,24 +180,22 @@ namespace platf { std::call_once(migration_flag, []() { bool found = false; bool migrate_config = true; - const char *dir; - const char *homedir; - const char *migrate_envvar; + std::string homedir; // Get the home directory - if ((homedir = getenv("HOME")) == nullptr || strlen(homedir) == 0) { + if (!lizardbyte::common::get_env("HOME", homedir) || 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; } @@ -208,8 +206,7 @@ namespace platf { } // 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; if (old_config_path != config_path && fs::exists(old_config_path, ec)) { @@ -366,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(); @@ -1244,13 +1241,13 @@ namespace platf { 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 cdef9ae78d6..1be18e12de3 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) { From 4fcdfedced48454e03b021c9c26d64d2ea3f8fc0 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:44:35 -0400 Subject: [PATCH 11/15] Use fs::path for HOME config paths Switch HOME handling in Linux misc config setup from std::string to fs::path and build fallback/migration paths with path joins. This makes path construction consistent and safer for both the default ~/.config/sunshine location and old-config migration checks. --- src/platform/linux/misc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 86450fed3b8..dae271d33ac 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -180,10 +180,10 @@ namespace platf { std::call_once(migration_flag, []() { bool found = false; bool migrate_config = true; - std::string homedir; + fs::path homedir {lizardbyte::common::get_env("HOME")}; // Get the home directory - if (!lizardbyte::common::get_env("HOME", homedir) || homedir.empty()) { + if (homedir.empty()) { // If HOME is empty or not set, use the current user's home directory homedir = getpwuid(geteuid())->pw_dir; } @@ -202,13 +202,13 @@ namespace platf { // 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 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; From 4be7f9f226c712ae7f22c717bfa489a4730efdb0 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:10:14 -0400 Subject: [PATCH 12/15] Sonar fix --- tests/unit/test_confighttp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test_confighttp.cpp b/tests/unit/test_confighttp.cpp index dd20332a266..a633ae3b5a3 100644 --- a/tests/unit/test_confighttp.cpp +++ b/tests/unit/test_confighttp.cpp @@ -1253,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); } From 6e5619a31bef9e5dec648d699ade05f755331dbe Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:26:49 -0400 Subject: [PATCH 13/15] docs(readme): update heading Use svg icon and badges --- README.md | 20 ++++++++++++-------- docs/Doxyfile | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b9563661c2e..21075259504 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,16 @@ 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/docs/Doxyfile b/docs/Doxyfile index 0c84d823d9e..74b3f3815ea 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 From 7209652401bbbf41b23680bc2c1ec3b59c227030 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 30 Jun 2026 09:55:51 -0400 Subject: [PATCH 14/15] Remove docs badge and bump common submodule Drops the Read the Docs badge from the README status badges and updates the `third-party/lizardbyte-common` submodule to commit `49aa8c6`. --- README.md | 1 - third-party/lizardbyte-common | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 21075259504..deb149dcd71 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ Gurubase GitHub Workflow Status (CI) GitHub Workflow Status (localize) - Read the Docs Codecov SonarCloud diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index 3f29834c942..49aa8c60404 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit 3f29834c942d71d626f2daf67323fc61426bf524 +Subproject commit 49aa8c6040487a83a57ee454dae30a4c93275f5b From 979e1ea8280aaba88efcb3923ce0d3fceb5170e7 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 30 Jun 2026 17:56:14 -0400 Subject: [PATCH 15/15] Bump lizardbyte-common submodule Update `third-party/lizardbyte-common` from `49aa8c60` to `06cd442b` to pull in the latest upstream common-library changes. --- third-party/lizardbyte-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/lizardbyte-common b/third-party/lizardbyte-common index 49aa8c60404..06cd442b808 160000 --- a/third-party/lizardbyte-common +++ b/third-party/lizardbyte-common @@ -1 +1 @@ -Subproject commit 49aa8c6040487a83a57ee454dae30a4c93275f5b +Subproject commit 06cd442b808f02f1674f3192a84d25b9a503c482