From 86f7bb2c017adf0a95674cc32e5e14aba9aa4cd2 Mon Sep 17 00:00:00 2001 From: ns6089 <61738816+ns6089@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:45:10 +0300 Subject: [PATCH] feat(tests): rework tests in numerous ways --- src/audio.h | 3 + src/logging.cpp | 90 ++++++++------ src/logging.h | 6 + tests/CMakeLists.txt | 14 +-- tests/conftest.cpp | 202 ------------------------------- tests/tests_common.h | 34 ++++++ tests/tests_environment.h | 22 ++++ tests/tests_events.h | 70 +++++++++++ tests/tests_main.cpp | 15 +++ tests/unit/test_audio.cpp | 26 +--- tests/unit/test_file_handler.cpp | 24 ++-- tests/unit/test_httpcommon.cpp | 14 +-- tests/unit/test_logging.cpp | 99 +++++++-------- tests/unit/test_mouse.cpp | 57 ++++----- tests/unit/test_rswrapper.cpp | 3 +- tests/unit/test_stream.cpp | 2 +- tests/unit/test_video.cpp | 53 +++----- tests/utils.cpp | 20 --- tests/utils.h | 10 -- 19 files changed, 319 insertions(+), 445 deletions(-) delete mode 100644 tests/conftest.cpp create mode 100644 tests/tests_common.h create mode 100644 tests/tests_environment.h create mode 100644 tests/tests_events.h create mode 100644 tests/tests_main.cpp delete mode 100644 tests/utils.cpp delete mode 100644 tests/utils.h diff --git a/src/audio.h b/src/audio.h index 6d04d242b4c..208a5775871 100644 --- a/src/audio.h +++ b/src/audio.h @@ -6,6 +6,9 @@ #include "thread_safe.h" #include "utility.h" + +#include + namespace audio { enum stream_config_e : int { STEREO, ///< Stereo diff --git a/src/logging.cpp b/src/logging.cpp index a78b5b5de89..6d1085630d9 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -35,6 +35,9 @@ bl::sources::severity_logger info(2); // Should be informed about bl::sources::severity_logger warning(3); // Strange events bl::sources::severity_logger error(4); // Recoverable errors bl::sources::severity_logger fatal(5); // Unrecoverable errors +#ifdef SUNSHINE_TESTS +bl::sources::severity_logger tests(10); // Automatic tests output +#endif BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int) @@ -50,6 +53,51 @@ namespace logging { sink.reset(); } + void + formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os) { + constexpr const char *message = "Message"; + constexpr const char *severity = "Severity"; + + auto log_level = view.attribute_values()[severity].extract().get(); + + std::string_view log_type; + switch (log_level) { + case 0: + log_type = "Verbose: "sv; + break; + case 1: + log_type = "Debug: "sv; + break; + case 2: + log_type = "Info: "sv; + break; + case 3: + log_type = "Warning: "sv; + break; + case 4: + log_type = "Error: "sv; + break; + case 5: + log_type = "Fatal: "sv; + break; +#ifdef SUNSHINE_TESTS + case 10: + log_type = "Tests: "sv; + break; +#endif + }; + + auto now = std::chrono::system_clock::now(); + auto ms = std::chrono::duration_cast( + now - std::chrono::time_point_cast(now)); + + auto t = std::chrono::system_clock::to_time_t(now); + auto lt = *std::localtime(&t); + + os << "["sv << std::put_time(<, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv + << log_type << view.attribute_values()[message].extract(); + } + [[nodiscard]] std::unique_ptr init(int min_log_level, const std::string &log_file) { if (sink) { @@ -61,49 +109,13 @@ namespace logging { sink = boost::make_shared(); +#ifndef SUNSHINE_TESTS boost::shared_ptr stream { &std::cout, boost::null_deleter() }; sink->locked_backend()->add_stream(stream); +#endif sink->locked_backend()->add_stream(boost::make_shared(log_file)); sink->set_filter(severity >= min_log_level); - - sink->set_formatter([](const bl::record_view &view, bl::formatting_ostream &os) { - constexpr const char *message = "Message"; - constexpr const char *severity = "Severity"; - - auto log_level = view.attribute_values()[severity].extract().get(); - - std::string_view log_type; - switch (log_level) { - case 0: - log_type = "Verbose: "sv; - break; - case 1: - log_type = "Debug: "sv; - break; - case 2: - log_type = "Info: "sv; - break; - case 3: - log_type = "Warning: "sv; - break; - case 4: - log_type = "Error: "sv; - break; - case 5: - log_type = "Fatal: "sv; - break; - }; - - auto now = std::chrono::system_clock::now(); - auto ms = std::chrono::duration_cast( - now - std::chrono::time_point_cast(now)); - - auto t = std::chrono::system_clock::to_time_t(now); - auto lt = *std::localtime(&t); - - os << "["sv << std::put_time(<, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv - << log_type << view.attribute_values()[message].extract(); - }); + sink->set_formatter(&formatter); // Flush after each log record to ensure log file contents on disk isn't stale. // This is particularly important when running from a Windows service. diff --git a/src/logging.h b/src/logging.h index 54095393b38..ee580e5db87 100644 --- a/src/logging.h +++ b/src/logging.h @@ -16,6 +16,9 @@ extern boost::log::sources::severity_logger info; extern boost::log::sources::severity_logger warning; extern boost::log::sources::severity_logger error; extern boost::log::sources::severity_logger fatal; +#ifdef SUNSHINE_TESTS +extern boost::log::sources::severity_logger tests; +#endif #include "config.h" #include "stat_trackers.h" @@ -41,6 +44,9 @@ namespace logging { void deinit(); + void + formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os); + /** * @brief Initialize the logging system. * @param min_log_level The minimum log level to output. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 73979d371e8..9d626754015 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -41,10 +41,12 @@ if (NOT (TESTS_SOFTWARE_ENCODER_UNAVAILABLE STREQUAL "fail" OR TESTS_SOFTWARE_EN endif () list(APPEND TEST_DEFINITIONS TESTS_SOFTWARE_ENCODER_UNAVAILABLE="${TESTS_SOFTWARE_ENCODER_UNAVAILABLE}") # fail/skip -file(GLOB_RECURSE TEST_SOURCES - ${CMAKE_SOURCE_DIR}/tests/conftest.cpp - ${CMAKE_SOURCE_DIR}/tests/utils.cpp - ${CMAKE_SOURCE_DIR}/tests/test_*.cpp) +# this indicates we're building tests in case sunshine needs to adjust some code or add private tests +list(APPEND TEST_DEFINITIONS SUNSHINE_TESTS) + +file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS + ${CMAKE_SOURCE_DIR}/tests/*.h + ${CMAKE_SOURCE_DIR}/tests/*.cpp) set(SUNSHINE_SOURCES ${SUNSHINE_TARGET_FILES}) @@ -64,7 +66,6 @@ set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20) target_link_libraries(${PROJECT_NAME} ${SUNSHINE_EXTERNAL_LIBRARIES} gtest - gtest_main # if we use this we don't need our own main function ${PLATFORM_LIBRARIES}) target_compile_definitions(${PROJECT_NAME} PUBLIC ${SUNSHINE_DEFINITIONS} ${TEST_DEFINITIONS}) target_compile_options(${PROJECT_NAME} PRIVATE $<$:${SUNSHINE_COMPILE_OPTIONS}>;$<$:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301 @@ -72,8 +73,7 @@ target_link_options(${PROJECT_NAME} PRIVATE) if (WIN32) # prefer static libraries since we're linking statically - # this fixes gtest_main and libcurl linking errors, when using non MSYS2 version of CMake + # this fixes libcurl linking errors when using non MSYS2 version of CMake set_target_properties(${PROJECT_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1) endif () -add_test(NAME ${PROJECT_NAME} COMMAND sunshine_test) diff --git a/tests/conftest.cpp b/tests/conftest.cpp deleted file mode 100644 index 13bfc885903..00000000000 --- a/tests/conftest.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/** - * @file tests/conftest.cpp - * @brief Common test fixtures for all tests. - * While not a header, this file should be included in all test files. - * @todo Separate parts of this into a header file. - */ -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace boost_logging = boost::log; -namespace sinks = boost_logging::sinks; - -// Undefine the original TEST macro -#undef TEST - -// Redefine TEST to use our BaseTest class, to automatically use our BaseTest fixture -#define TEST(test_case_name, test_name) \ - GTEST_TEST_(test_case_name, test_name, ::BaseTest, \ - ::testing::internal::GetTypeId<::BaseTest>()) - -/** - * @brief Base class for tests. - * - * This class provides a base test fixture for all tests. - * - * ``cout``, ``stderr``, and ``stdout`` are redirected to a buffer, and the buffer is printed if the test fails. - * - * @todo Retain the color of the original output. - */ -class BaseTest: public ::testing::Test { -protected: - // https://stackoverflow.com/a/58369622/11214013 - - // we can possibly use some internal googletest functions to capture stdout and stderr, but I have not tested this - // https://stackoverflow.com/a/33186201/11214013 - - // Add a member variable for deinit_guard - std::unique_ptr deinit_guard; - - // Add a member variable to store the sink - boost::shared_ptr> test_sink; - - BaseTest(): - sbuf { nullptr }, pipe_stdout { nullptr }, pipe_stderr { nullptr } { - // intentionally empty - } - - ~BaseTest() override = default; - - void - SetUp() override { - // todo: only run this one time, instead of every time a test is run - // see: https://stackoverflow.com/questions/2435277/googletest-accessing-the-environment-from-a-test - // get command line args from the test executable - testArgs = ::testing::internal::GetArgvs(); - - // then get the directory of the test executable - // std::string path = ::testing::internal::GetArgvs()[0]; - testBinary = testArgs[0]; - - // get the directory of the test executable - testBinaryDir = std::filesystem::path(testBinary).parent_path(); - - // If testBinaryDir is empty or `.` then set it to the current directory - // maybe some better options here: https://stackoverflow.com/questions/875249/how-to-get-current-directory - if (testBinaryDir.empty() || testBinaryDir.string() == ".") { - testBinaryDir = std::filesystem::current_path(); - } - - // Create a sink that writes to our stringstream (BOOST_LOG) - typedef sinks::synchronous_sink test_text_sink; - test_sink = boost::make_shared(); - - // Set the stringstream as the target of the sink (BOOST_LOG) - boost::shared_ptr stream(&boost_log_buffer, [](std::ostream *) {}); - test_sink->locked_backend()->add_stream(stream); - - // Register the sink in the logging core (BOOST_LOG) - boost_logging::core::get()->add_sink(test_sink); - - sbuf = std::cout.rdbuf(); // save cout buffer (std::cout) - std::cout.rdbuf(cout_buffer.rdbuf()); // redirect cout to buffer (std::cout) - - // todo: do this only once - // setup a mail object - mail::man = std::make_shared(); - - deinit_guard = logging::init(0, "test.log"); - if (!deinit_guard) { - FAIL() << "Logging failed to initialize"; - } - } - - void - TearDown() override { - std::cout.rdbuf(sbuf); // restore cout buffer - - // get test info - const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); - - if (test_info->result()->Failed()) { - std::cout << std::endl - << "Test failed: " << test_info->name() << std::endl - << std::endl - << "Captured boost log:" << std::endl - << boost_log_buffer.str() << std::endl - << "Captured cout:" << std::endl - << cout_buffer.str() << std::endl - << "Captured stdout:" << std::endl - << stdout_buffer.str() << std::endl - << "Captured stderr:" << std::endl - << stderr_buffer.str() << std::endl; - } - - sbuf = nullptr; // clear sbuf - if (pipe_stdout) { - pclose(pipe_stdout); - pipe_stdout = nullptr; - } - if (pipe_stderr) { - pclose(pipe_stderr); - pipe_stderr = nullptr; - } - - // Remove the sink from the logging core (BOOST_LOG) - boost_logging::core::get()->remove_sink(test_sink); - test_sink.reset(); - } - - // functions and variables - std::vector testArgs; // CLI arguments used - std::filesystem::path testBinary; // full path of this binary - std::filesystem::path testBinaryDir; // full directory of this binary - std::stringstream boost_log_buffer; // declare boost_log_buffer - std::stringstream cout_buffer; // declare cout_buffer - std::stringstream stdout_buffer; // declare stdout_buffer - std::stringstream stderr_buffer; // declare stderr_buffer - std::streambuf *sbuf; - FILE *pipe_stdout; - FILE *pipe_stderr; - - int - exec(const char *cmd) { - std::array buffer {}; - pipe_stdout = popen((std::string(cmd) + " 2>&1").c_str(), "r"); - pipe_stderr = popen((std::string(cmd) + " 2>&1").c_str(), "r"); - if (!pipe_stdout || !pipe_stderr) { - throw std::runtime_error("popen() failed!"); - } - while (fgets(buffer.data(), buffer.size(), pipe_stdout) != nullptr) { - stdout_buffer << buffer.data(); - } - while (fgets(buffer.data(), buffer.size(), pipe_stderr) != nullptr) { - stderr_buffer << buffer.data(); - } - int returnCode = pclose(pipe_stdout); - pipe_stdout = nullptr; - if (returnCode != 0) { - std::cout << "Error: " << stderr_buffer.str() << std::endl - << "Return code: " << returnCode << std::endl; - } - return returnCode; - } -}; - -class PlatformInitBase: public virtual BaseTest { -protected: - void - SetUp() override { - std::cout << "PlatformInitTest:: starting Fixture SetUp" << std::endl; - - // initialize the platform - deinit_guard = platf::init(); - if (!deinit_guard) { - FAIL() << "Platform failed to initialize"; - } - - std::cout << "PlatformInitTest:: finished Fixture SetUp" << std::endl; - } - - void - TearDown() override { - std::cout << "PlatformInitTest:: starting Fixture TearDown" << std::endl; - deinit_guard.reset(nullptr); - std::cout << "PlatformInitTest:: finished Fixture TearDown" << std::endl; - } - -private: - std::unique_ptr deinit_guard; -}; diff --git a/tests/tests_common.h b/tests/tests_common.h new file mode 100644 index 00000000000..4af2ce41866 --- /dev/null +++ b/tests/tests_common.h @@ -0,0 +1,34 @@ +/** + * @file tests/tests_common.h + * @brief Common declarations. + */ +#pragma once +#include + +#include +#include +#include + +template +struct PlatformTestSuite: testing::Test { + static std::unique_ptr & + get_platform_deinit() { + static std::unique_ptr deinit; + return deinit; + } + + static void + SetUpTestSuite() { + auto &deinit = get_platform_deinit(); + BOOST_LOG(tests) << "Setting up platform test suite"; + deinit = platf::init(); + ASSERT_TRUE(deinit); + } + + static void + TearDownTestSuite() { + auto &deinit = get_platform_deinit(); + deinit = {}; + BOOST_LOG(tests) << "Tore down platform test suite"; + } +}; diff --git a/tests/tests_environment.h b/tests/tests_environment.h new file mode 100644 index 00000000000..5507194301b --- /dev/null +++ b/tests/tests_environment.h @@ -0,0 +1,22 @@ +/** + * @file tests/tests_environment.h + * @brief Declarations for SunshineEnvironment. + */ +#pragma once +#include "tests_common.h" + +struct SunshineEnvironment: testing::Environment { + void + SetUp() override { + mail::man = std::make_shared(); + deinit_log = logging::init(0, "test_sunshine.log"); + } + + void + TearDown() override { + deinit_log = {}; + mail::man = {}; + } + + std::unique_ptr deinit_log; +}; diff --git a/tests/tests_events.h b/tests/tests_events.h new file mode 100644 index 00000000000..34b38423621 --- /dev/null +++ b/tests/tests_events.h @@ -0,0 +1,70 @@ +/** + * @file tests/tests_events.h + * @brief Declarations for SunshineEventListener. + */ +#pragma once +#include "tests_common.h" + +struct SunshineEventListener: testing::EmptyTestEventListener { + SunshineEventListener() { + sink = boost::make_shared(); + sink_buffer = boost::make_shared(); + sink->locked_backend()->add_stream(sink_buffer); + sink->set_formatter(&logging::formatter); + } + + void + OnTestProgramStart(const testing::UnitTest &unit_test) override { + boost::log::core::get()->add_sink(sink); + } + + void + OnTestProgramEnd(const testing::UnitTest &unit_test) override { + 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"; + } + + 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; + } + } + + 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(); + } + + sink_buffer->str(""); + sink_buffer->clear(); + } + + using sink_t = boost::log::sinks::synchronous_sink; + boost::shared_ptr sink; + boost::shared_ptr sink_buffer; +}; diff --git a/tests/tests_main.cpp b/tests/tests_main.cpp new file mode 100644 index 00000000000..f80985c5177 --- /dev/null +++ b/tests/tests_main.cpp @@ -0,0 +1,15 @@ +/** + * @file tests/tests_main.cpp + * @brief Entry point definition. + */ +#include "tests_common.h" +#include "tests_environment.h" +#include "tests_events.h" + +int +main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + testing::AddGlobalTestEnvironment(new SunshineEnvironment); + testing::UnitTest::GetInstance()->listeners().Append(new SunshineEventListener); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit/test_audio.cpp b/tests/unit/test_audio.cpp index e0ff4bd72df..9a9369492ae 100644 --- a/tests/unit/test_audio.cpp +++ b/tests/unit/test_audio.cpp @@ -2,39 +2,24 @@ * @file tests/unit/test_audio.cpp * @brief Test src/audio.*. */ -#include #include -#include +#include "../tests_common.h" using namespace audio; -class AudioTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface, config_t>> { -protected: +struct AudioTest: PlatformTestSuite, testing::WithParamInterface, config_t>> { void SetUp() override { - BaseTest::SetUp(); - PlatformInitBase::SetUp(); - - std::string_view p_name = std::get<0>(GetParam()); - std::cout << "AudioTest(" << p_name << "):: starting Fixture SetUp" << std::endl; - m_config = std::get<1>(GetParam()); m_mail = std::make_shared(); } - void - TearDown() override { - PlatformInitBase::TearDown(); - BaseTest::TearDown(); - } - -protected: config_t m_config; safe::mail_t m_mail; }; -static std::bitset +constexpr std::bitset config_flags(int flag = -1) { std::bitset<3> result = std::bitset(); if (flag >= 0) { @@ -46,11 +31,12 @@ config_flags(int flag = -1) { INSTANTIATE_TEST_SUITE_P( Configurations, AudioTest, - ::testing::Values( + testing::Values( std::make_tuple("HIGH_STEREO", config_t { 5, 2, 0x3, { 0 }, config_flags(config_t::HIGH_QUALITY) }), std::make_tuple("SURROUND51", config_t { 5, 6, 0x3F, { 0 }, config_flags() }), std::make_tuple("SURROUND71", config_t { 5, 8, 0x63F, { 0 }, config_flags() }), - std::make_tuple("SURROUND51_CUSTOM", config_t { 5, 6, 0x3F, { 6, 4, 2, { 0, 1, 4, 5, 2, 3 } }, config_flags(config_t::CUSTOM_SURROUND_PARAMS) }))); + std::make_tuple("SURROUND51_CUSTOM", config_t { 5, 6, 0x3F, { 6, 4, 2, { 0, 1, 4, 5, 2, 3 } }, config_flags(config_t::CUSTOM_SURROUND_PARAMS) })), + [](const auto &info) { return std::string(std::get<0>(info.param)); }); TEST_P(AudioTest, TestEncode) { std::thread timer([&] { diff --git a/tests/unit/test_file_handler.cpp b/tests/unit/test_file_handler.cpp index 3c249743ef9..eb884974ff2 100644 --- a/tests/unit/test_file_handler.cpp +++ b/tests/unit/test_file_handler.cpp @@ -4,9 +4,9 @@ */ #include -#include +#include "../tests_common.h" -class FileHandlerParentDirectoryTest: public ::testing::TestWithParam> {}; +struct FileHandlerParentDirectoryTest: testing::TestWithParam> {}; TEST_P(FileHandlerParentDirectoryTest, Run) { auto [input, expected] = GetParam(); @@ -16,12 +16,12 @@ TEST_P(FileHandlerParentDirectoryTest, Run) { INSTANTIATE_TEST_SUITE_P( FileHandlerTests, FileHandlerParentDirectoryTest, - ::testing::Values( + testing::Values( std::make_tuple("/path/to/file.txt", "/path/to"), std::make_tuple("/path/to/directory", "/path/to"), std::make_tuple("/path/to/directory/", "/path/to"))); -class FileHandlerMakeDirectoryTest: public ::testing::TestWithParam> {}; +struct FileHandlerMakeDirectoryTest: testing::TestWithParam> {}; TEST_P(FileHandlerMakeDirectoryTest, Run) { auto [input, expected, remove] = GetParam(); @@ -41,28 +41,18 @@ TEST_P(FileHandlerMakeDirectoryTest, Run) { INSTANTIATE_TEST_SUITE_P( FileHandlerTests, FileHandlerMakeDirectoryTest, - ::testing::Values( + testing::Values( std::make_tuple("dir_123", true, false), std::make_tuple("dir_123", true, true), std::make_tuple("dir_123/abc", true, false), std::make_tuple("dir_123/abc", true, true))); -class FileHandlerTests: public virtual BaseTest, public ::testing::WithParamInterface> { -protected: - void - SetUp() override { - BaseTest::SetUp(); - } +struct FileHandlerTests: testing::TestWithParam> {}; - void - TearDown() override { - BaseTest::TearDown(); - } -}; INSTANTIATE_TEST_SUITE_P( TestFiles, FileHandlerTests, - ::testing::Values( + testing::Values( std::make_tuple(0, ""), // empty file std::make_tuple(1, "a"), // single character std::make_tuple(2, "Mr. Blue Sky - Electric Light Orchestra"), // single line diff --git a/tests/unit/test_httpcommon.cpp b/tests/unit/test_httpcommon.cpp index c979304d215..bb0bb5625f7 100644 --- a/tests/unit/test_httpcommon.cpp +++ b/tests/unit/test_httpcommon.cpp @@ -4,9 +4,9 @@ */ #include -#include +#include "../tests_common.h" -class UrlEscapeTest: public ::testing::TestWithParam> {}; +struct UrlEscapeTest: testing::TestWithParam> {}; TEST_P(UrlEscapeTest, Run) { auto [input, expected] = GetParam(); @@ -16,12 +16,12 @@ TEST_P(UrlEscapeTest, Run) { INSTANTIATE_TEST_SUITE_P( UrlEscapeTests, UrlEscapeTest, - ::testing::Values( + testing::Values( std::make_tuple("igdb_0123456789", "igdb_0123456789"), std::make_tuple("../../../", "..%2F..%2F..%2F"), std::make_tuple("..*\\", "..%2A%5C"))); -class UrlGetHostTest: public ::testing::TestWithParam> {}; +struct UrlGetHostTest: testing::TestWithParam> {}; TEST_P(UrlGetHostTest, Run) { auto [input, expected] = GetParam(); @@ -31,12 +31,12 @@ TEST_P(UrlGetHostTest, Run) { INSTANTIATE_TEST_SUITE_P( UrlGetHostTests, UrlGetHostTest, - ::testing::Values( + testing::Values( std::make_tuple("https://images.igdb.com/example.txt", "images.igdb.com"), std::make_tuple("http://localhost:8080", "localhost"), std::make_tuple("nonsense!!}{::", ""))); -class DownloadFileTest: public ::testing::TestWithParam> {}; +struct DownloadFileTest: testing::TestWithParam> {}; TEST_P(DownloadFileTest, Run) { auto [url, filename] = GetParam(); @@ -48,6 +48,6 @@ TEST_P(DownloadFileTest, Run) { INSTANTIATE_TEST_SUITE_P( DownloadFileTests, DownloadFileTest, - ::testing::Values( + testing::Values( std::make_tuple("https://httpbin.org/base64/aGVsbG8h", "hello.txt"), std::make_tuple("https://httpbin.org/redirect-to?url=/base64/aGVsbG8h", "hello-redirect.txt"))); diff --git a/tests/unit/test_logging.cpp b/tests/unit/test_logging.cpp index 1c6944748c2..99b4b264840 100644 --- a/tests/unit/test_logging.cpp +++ b/tests/unit/test_logging.cpp @@ -2,74 +2,63 @@ * @file tests/unit/test_logging.cpp * @brief Test src/logging.*. */ +#include + +#include "../tests_common.h" + #include +#include -#include +namespace { + std::array log_levels = { + std::tuple("verbose", &verbose), + std::tuple("debug", &debug), + std::tuple("info", &info), + std::tuple("warning", &warning), + std::tuple("error", &error), + std::tuple("fatal", &fatal), + }; -#include + constexpr auto log_file = "test_sunshine.log"; +} // namespace -class LoggerInitTest: public virtual BaseTest, public ::testing::WithParamInterface { -protected: - void - SetUp() override { - BaseTest::SetUp(); - } +struct LogLevelsTest: testing::TestWithParam {}; - void - TearDown() override { - BaseTest::TearDown(); - } -}; INSTANTIATE_TEST_SUITE_P( - LogLevel, - LoggerInitTest, - ::testing::Values( - 0, - 1, - 2, - 3, - 4, - 5)); -TEST_P(LoggerInitTest, InitLogging) { - int logLevel = GetParam(); - std::string logFilePath = "test_log_" + std::to_string(logLevel) + ".log"; + Logging, + LogLevelsTest, + testing::ValuesIn(log_levels), + [](const auto &info) { return std::string(std::get<0>(info.param)); }); - // deinit the BaseTest logger - BaseTest::deinit_guard.reset(); +TEST_P(LogLevelsTest, PutMessage) { + auto [label, plogger] = GetParam(); + ASSERT_TRUE(plogger); + auto &logger = *plogger; - auto log_deinit = logging::init(logLevel, logFilePath); - if (!log_deinit) { - FAIL() << "Failed to initialize logging"; - } -} + std::random_device rand_dev; + std::mt19937_64 rand_gen(rand_dev()); + auto test_message = std::to_string(rand_gen()) + std::to_string(rand_gen()); + BOOST_LOG(logger) << test_message; -TEST(LogFlushTest, CheckLogFile) { - // Write a log message - BOOST_LOG(info) << "Test message"; + // Flush logger and search for the message in the log file - // Call log_flush logging::log_flush(); - // Check the contents of the log file - std::ifstream log_file("test.log"); - std::string line; + std::ifstream input(log_file); + ASSERT_TRUE(input.is_open()); + bool found = false; - while (std::getline(log_file, line)) { - if (line.find("Test message") != std::string::npos) { - found = true; - break; + for (std::string line; std::getline(input, line);) { + if (line.find(test_message) != std::string::npos) { + // Assume that logger may change the case of log level label + std::transform(line.begin(), line.end(), line.begin(), + [](char c) { return std::tolower(c); }); + + if (line.find(label) != std::string::npos) { + found = true; + break; + } } } - - EXPECT_TRUE(found); -} - -TEST(PrintHelpTest, CheckOutput) { - std::string name = "test"; - logging::print_help(name.c_str()); - - std::string output = cout_buffer.str(); - - EXPECT_NE(output.find("Usage: " + name), std::string::npos); - EXPECT_NE(output.find("--help"), std::string::npos); + ASSERT_TRUE(found); } diff --git a/tests/unit/test_mouse.cpp b/tests/unit/test_mouse.cpp index d97cb9c1ec6..cb41dc2877c 100644 --- a/tests/unit/test_mouse.cpp +++ b/tests/unit/test_mouse.cpp @@ -3,38 +3,33 @@ * @brief Test src/input.*. */ #include -#include -#include +#include "../tests_common.h" -class MouseHIDTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface { -protected: +struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface { void SetUp() override { - BaseTest::SetUp(); - PlatformInitBase::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, // but I'm not sure about real work - GTEST_SKIP_("MouseTest:: skipped for now. TODO Windows"); + GTEST_SKIP() << "TODO Windows"; #elif __linux__ // TODO: Inputtino waiting https://github.com/games-on-whales/inputtino/issues/6 is resolved. - GTEST_SKIP_("MouseTest:: skipped for now. TODO Inputtino"); + GTEST_SKIP() << "TODO Inputtino"; #endif } void TearDown() override { std::this_thread::sleep_for(std::chrono::milliseconds(200)); - PlatformInitBase::TearDown(); - BaseTest::TearDown(); } }; + INSTANTIATE_TEST_SUITE_P( MouseInputs, MouseHIDTest, - ::testing::Values( + testing::Values( util::point_t { 40, 40 }, util::point_t { 70, 150 })); // todo: add tests for hitting screen edges @@ -42,30 +37,30 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(MouseHIDTest, MoveInputTest) { util::point_t mouse_delta = GetParam(); - std::cout << "MoveInputTest:: got param: " << mouse_delta << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: got param: " << mouse_delta; platf::input_t input = platf::input(); - std::cout << "MoveInputTest:: init input" << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: init input"; - std::cout << "MoveInputTest:: get current mouse loc" << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: get current mouse loc"; auto old_loc = platf::get_mouse_loc(input); - std::cout << "MoveInputTest:: got current mouse loc: " << old_loc << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: got current mouse loc: " << old_loc; - std::cout << "MoveInputTest:: move: " << mouse_delta << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: move: " << mouse_delta; platf::move_mouse(input, mouse_delta.x, mouse_delta.y); std::this_thread::sleep_for(std::chrono::milliseconds(200)); - std::cout << "MoveInputTest:: moved: " << mouse_delta << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: moved: " << mouse_delta; - std::cout << "MoveInputTest:: get updated mouse loc" << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: get updated mouse loc"; auto new_loc = platf::get_mouse_loc(input); - std::cout << "MoveInputTest:: got updated mouse loc: " << new_loc << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: got updated mouse loc: " << new_loc; bool has_input_moved = old_loc.x != new_loc.x && old_loc.y != new_loc.y; if (!has_input_moved) { - std::cout << "MoveInputTest:: haven't moved" << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: haven't moved"; } else { - std::cout << "MoveInputTest:: moved" << std::endl; + BOOST_LOG(tests) << "MoveInputTest:: moved"; } EXPECT_TRUE(has_input_moved); @@ -77,14 +72,14 @@ TEST_P(MouseHIDTest, MoveInputTest) { TEST_P(MouseHIDTest, AbsMoveInputTest) { util::point_t mouse_pos = GetParam(); - std::cout << "AbsMoveInputTest:: got param: " << mouse_pos << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: got param: " << mouse_pos; platf::input_t input = platf::input(); - std::cout << "AbsMoveInputTest:: init input" << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: init input"; - std::cout << "AbsMoveInputTest:: get current mouse loc" << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: get current mouse loc"; auto old_loc = platf::get_mouse_loc(input); - std::cout << "AbsMoveInputTest:: got current mouse loc: " << old_loc << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: got current mouse loc: " << old_loc; #ifdef _WIN32 platf::touch_port_t abs_port { @@ -99,22 +94,22 @@ TEST_P(MouseHIDTest, AbsMoveInputTest) { #else platf::touch_port_t abs_port {}; #endif - std::cout << "AbsMoveInputTest:: move: " << mouse_pos << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: move: " << mouse_pos; platf::abs_mouse(input, abs_port, mouse_pos.x, mouse_pos.y); std::this_thread::sleep_for(std::chrono::milliseconds(200)); - std::cout << "AbsMoveInputTest:: moved: " << mouse_pos << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: moved: " << mouse_pos; - std::cout << "AbsMoveInputTest:: get updated mouse loc" << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: get updated mouse loc"; auto new_loc = platf::get_mouse_loc(input); - std::cout << "AbsMoveInputTest:: got updated mouse loc: " << new_loc << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: got updated mouse loc: " << new_loc; bool has_input_moved = old_loc.x != new_loc.x || old_loc.y != new_loc.y; if (!has_input_moved) { - std::cout << "AbsMoveInputTest:: haven't moved" << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: haven't moved"; } else { - std::cout << "AbsMoveInputTest:: moved" << std::endl; + BOOST_LOG(tests) << "AbsMoveInputTest:: moved"; } EXPECT_TRUE(has_input_moved); diff --git a/tests/unit/test_rswrapper.cpp b/tests/unit/test_rswrapper.cpp index a74a558bfad..c786690fd3b 100644 --- a/tests/unit/test_rswrapper.cpp +++ b/tests/unit/test_rswrapper.cpp @@ -2,12 +2,11 @@ * @file tests/unit/test_rswrapper.cpp * @brief Test src/rswrapper.* */ - extern "C" { #include } -#include +#include "../tests_common.h" TEST(ReedSolomonWrapperTests, InitTest) { reed_solomon_init(); diff --git a/tests/unit/test_stream.cpp b/tests/unit/test_stream.cpp index 8973af324df..b3e19bcc2df 100644 --- a/tests/unit/test_stream.cpp +++ b/tests/unit/test_stream.cpp @@ -13,7 +13,7 @@ namespace stream { concat_and_insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data1, const std::string_view &data2); } -#include +#include "../tests_common.h" TEST(ConcatAndInsertTests, ConcatNoInsertionTest) { char b1[] = { 'a', 'b' }; diff --git a/tests/unit/test_video.cpp b/tests/unit/test_video.cpp index 650445c01a2..e37d69c7260 100644 --- a/tests/unit/test_video.cpp +++ b/tests/unit/test_video.cpp @@ -4,60 +4,45 @@ */ #include -#include +#include "../tests_common.h" -class EncoderTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface, video::encoder_t *>> { -protected: +struct EncoderTest: PlatformTestSuite, testing::WithParamInterface { void SetUp() override { - BaseTest::SetUp(); - PlatformInitBase::SetUp(); - - std::string_view p_name = std::get<0>(GetParam()); - std::cout << "EncoderTest(" << p_name << "):: starting Fixture SetUp" << std::endl; - - std::cout << "EncoderTest(" << p_name << "):: validating encoder" << std::endl; - video::encoder_t *encoder = std::get<1>(GetParam()); - bool isEncoderValid; - isEncoderValid = video::validate_encoder(*encoder, false); - - if (!isEncoderValid) { - // if encoder is software fail, otherwise skip - if (encoder == &video::software && std::string(TESTS_SOFTWARE_ENCODER_UNAVAILABLE) == "fail") { - FAIL() << "EncoderTest(" << p_name << "):: software encoder not available"; + auto &encoder = *GetParam(); + if (!video::validate_encoder(encoder, false)) { + // Encoder failed validation, + // if it's software - fail (unless overriden with compile definition), otherwise skip + if (encoder.name == "software" && std::string(TESTS_SOFTWARE_ENCODER_UNAVAILABLE) == "fail") { + FAIL() << "Software encoder not available"; } else { - GTEST_SKIP_((std::string("EncoderTest(") + std::string(p_name) + "):: encoder not available").c_str()); + GTEST_SKIP() << "Encoder not available"; } } - else { - std::cout << "EncoderTest(" << p_name << "):: encoder available" << std::endl; - } - } - - void - TearDown() override { - PlatformInitBase::TearDown(); - BaseTest::TearDown(); } }; + INSTANTIATE_TEST_SUITE_P( EncoderVariants, EncoderTest, - ::testing::Values( + testing::Values( #if !defined(__APPLE__) - std::make_tuple(video::nvenc.name, &video::nvenc), + &video::nvenc, #endif #ifdef _WIN32 - std::make_tuple(video::amdvce.name, &video::amdvce), std::make_tuple(video::quicksync.name, &video::quicksync), + &video::amdvce, + &video::quicksync, #endif #ifdef __linux__ - std::make_tuple(video::vaapi.name, &video::vaapi), + &video::vaapi, #endif #ifdef __APPLE__ - std::make_tuple(video::videotoolbox.name, &video::videotoolbox), + &video::videotoolbox, #endif - std::make_tuple(video::software.name, &video::software))); + &video::software), + [](const auto &info) { return std::string(info.param->name); }); + TEST_P(EncoderTest, ValidateEncoder) { // todo:: test something besides fixture setup } diff --git a/tests/utils.cpp b/tests/utils.cpp deleted file mode 100644 index 22e5ac51ef7..00000000000 --- a/tests/utils.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file tests/utils.cpp - * @brief Definition for utility functions. - */ -#include "utils.h" - -/** - * @brief Set an environment variable. - * @param name Name of the environment variable - * @param value Value of the environment variable - * @return 0 on success, non-zero error code on failure - */ -int -setEnv(const std::string &name, const std::string &value) { -#ifdef _WIN32 - return _putenv_s(name.c_str(), value.c_str()); -#else - return setenv(name.c_str(), value.c_str(), 1); -#endif -} diff --git a/tests/utils.h b/tests/utils.h deleted file mode 100644 index f08cee1a612..00000000000 --- a/tests/utils.h +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @file tests/utils.h - * @brief Declarations for utility functions. - */ -#pragma once - -#include - -int -setEnv(const std::string &name, const std::string &value);