feat(tests): rework tests in numerous ways (#3059)
Some checks failed
CI / GitHub Env Debug (push) Waiting to run
CI / Setup Release (push) Waiting to run
CI / Setup Flatpak Matrix (push) Waiting to run
CI / Linux Flatpak (push) Blocked by required conditions
CI / Linux ${{ matrix.type }} (--appimage-build, 22.04, AppImage) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (macos, 12) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (macos, 13) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (macos, 14) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (ubuntu, latest) (push) Blocked by required conditions
CI / Homebrew (${{ matrix.os_name }}-${{ matrix.os_version }}${{ matrix.release == true && ' (Release)' || '' }}) (ubuntu, latest, true) (push) Blocked by required conditions
CI / Macports (macOS-${{ matrix.os_version }}) (12, true) (push) Blocked by required conditions
CI / Macports (macOS-${{ matrix.os_version }}) (13) (push) Blocked by required conditions
CI / Macports (macOS-${{ matrix.os_version }}) (14) (push) Blocked by required conditions
CI / Windows (push) Blocked by required conditions
CI Docker / Check Dockerfiles (push) Waiting to run
CI Docker / Setup Release (push) Blocked by required conditions
CI Docker / Lint Dockerfile${{ matrix.tag }} (push) Blocked by required conditions
CI Docker / Docker${{ matrix.tag }} (push) Blocked by required conditions
CodeQL / Get language matrix (push) Waiting to run
CodeQL / Analyze (${{ matrix.name }}) (push) Blocked by required conditions
Build GH-Pages / update_pages (push) Waiting to run
localize / Update Localization (push) Has been cancelled

This commit is contained in:
ns6089 2024-08-22 23:48:24 +03:00 committed by GitHub
parent 3088823ffc
commit 764ce03520
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 316 additions and 446 deletions

View File

@ -6,6 +6,9 @@
#include "thread_safe.h"
#include "utility.h"
#include <bitset>
namespace audio {
enum stream_config_e : int {
STEREO, ///< Stereo

View File

@ -35,6 +35,9 @@ bl::sources::severity_logger<int> info(2); // Should be informed about
bl::sources::severity_logger<int> warning(3); // Strange events
bl::sources::severity_logger<int> error(4); // Recoverable errors
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors
#ifdef SUNSHINE_TESTS
bl::sources::severity_logger<int> 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<int>().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<std::chrono::milliseconds>(
now - std::chrono::time_point_cast<std::chrono::seconds>(now));
auto t = std::chrono::system_clock::to_time_t(now);
auto lt = *std::localtime(&t);
os << "["sv << std::put_time(&lt, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv
<< log_type << view.attribute_values()[message].extract<std::string>();
}
[[nodiscard]] std::unique_ptr<deinit_t>
init(int min_log_level, const std::string &log_file) {
if (sink) {
@ -61,49 +109,13 @@ namespace logging {
sink = boost::make_shared<text_sink>();
#ifndef SUNSHINE_TESTS
boost::shared_ptr<std::ostream> stream { &std::cout, boost::null_deleter() };
sink->locked_backend()->add_stream(stream);
#endif
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(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<int>().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<std::chrono::milliseconds>(
now - std::chrono::time_point_cast<std::chrono::seconds>(now));
auto t = std::chrono::system_clock::to_time_t(now);
auto lt = *std::localtime(&t);
os << "["sv << std::put_time(&lt, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv
<< log_type << view.attribute_values()[message].extract<std::string>();
});
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.

View File

@ -16,6 +16,9 @@ extern boost::log::sources::severity_logger<int> info;
extern boost::log::sources::severity_logger<int> warning;
extern boost::log::sources::severity_logger<int> error;
extern boost::log::sources::severity_logger<int> fatal;
#ifdef SUNSHINE_TESTS
extern boost::log::sources::severity_logger<int> 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.

View File

@ -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 $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${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)

View File

@ -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 <filesystem>
#include <gtest/gtest.h>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/shared_ptr.hpp>
#include <src/globals.h>
#include <src/logging.h>
#include <src/platform/common.h>
#include <tests/utils.h>
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<logging::deinit_t> deinit_guard;
// Add a member variable to store the sink
boost::shared_ptr<sinks::synchronous_sink<sinks::text_ostream_backend>> 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<sinks::text_ostream_backend> test_text_sink;
test_sink = boost::make_shared<test_text_sink>();
// Set the stringstream as the target of the sink (BOOST_LOG)
boost::shared_ptr<std::ostream> 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<safe::mail_raw_t>();
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<std::string> 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<char, 128> 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<platf::deinit_t> deinit_guard;
};

30
tests/tests_common.h Normal file
View File

@ -0,0 +1,30 @@
/**
* @file tests/tests_common.h
* @brief Common declarations.
*/
#pragma once
#include <gtest/gtest.h>
#include <src/globals.h>
#include <src/logging.h>
#include <src/platform/common.h>
struct PlatformTestSuite: testing::Test {
static void
SetUpTestSuite() {
ASSERT_FALSE(platf_deinit);
BOOST_LOG(tests) << "Setting up platform test suite";
platf_deinit = platf::init();
ASSERT_TRUE(platf_deinit);
}
static void
TearDownTestSuite() {
ASSERT_TRUE(platf_deinit);
platf_deinit = {};
BOOST_LOG(tests) << "Tore down platform test suite";
}
private:
inline static std::unique_ptr<platf::deinit_t> platf_deinit;
};

22
tests/tests_environment.h Normal file
View File

@ -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<safe::mail_raw_t>();
deinit_log = logging::init(0, "test_sunshine.log");
}
void
TearDown() override {
deinit_log = {};
mail::man = {};
}
std::unique_ptr<logging::deinit_t> deinit_log;
};

70
tests/tests_events.h Normal file
View File

@ -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_t>();
sink_buffer = boost::make_shared<std::stringstream>();
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::log::sinks::text_ostream_backend>;
boost::shared_ptr<sink_t> sink;
boost::shared_ptr<std::stringstream> sink_buffer;
};

15
tests/tests_main.cpp Normal file
View File

@ -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();
}

View File

@ -2,39 +2,24 @@
* @file tests/unit/test_audio.cpp
* @brief Test src/audio.*.
*/
#include <bitset>
#include <src/audio.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
using namespace audio;
class AudioTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface<std::tuple<std::basic_string_view<char>, config_t>> {
protected:
struct AudioTest: PlatformTestSuite, testing::WithParamInterface<std::tuple<std::basic_string_view<char>, 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<safe::mail_raw_t>();
}
void
TearDown() override {
PlatformInitBase::TearDown();
BaseTest::TearDown();
}
protected:
config_t m_config;
safe::mail_t m_mail;
};
static std::bitset<config_t::MAX_FLAGS>
constexpr std::bitset<config_t::MAX_FLAGS>
config_flags(int flag = -1) {
std::bitset<3> result = std::bitset<config_t::MAX_FLAGS>();
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([&] {

View File

@ -4,9 +4,9 @@
*/
#include <src/file_handler.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
class FileHandlerParentDirectoryTest: public ::testing::TestWithParam<std::tuple<std::string, std::string>> {};
struct FileHandlerParentDirectoryTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
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<std::tuple<std::string, bool, bool>> {};
struct FileHandlerMakeDirectoryTest: testing::TestWithParam<std::tuple<std::string, bool, bool>> {};
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<std::tuple<int, std::string>> {
protected:
void
SetUp() override {
BaseTest::SetUp();
}
struct FileHandlerTests: testing::TestWithParam<std::tuple<int, std::string>> {};
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

View File

@ -4,9 +4,9 @@
*/
#include <src/httpcommon.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
class UrlEscapeTest: public ::testing::TestWithParam<std::tuple<std::string, std::string>> {};
struct UrlEscapeTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
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<std::tuple<std::string, std::string>> {};
struct UrlGetHostTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
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<std::tuple<std::string, std::string>> {};
struct DownloadFileTest: testing::TestWithParam<std::tuple<std::string, std::string>> {};
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")));

View File

@ -2,74 +2,63 @@
* @file tests/unit/test_logging.cpp
* @brief Test src/logging.*.
*/
#include <fstream>
#include <src/logging.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
class LoggerInitTest: public virtual BaseTest, public ::testing::WithParamInterface<int> {
protected:
void
SetUp() override {
BaseTest::SetUp();
}
#include <fstream>
#include <random>
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),
};
constexpr auto log_file = "test_sunshine.log";
} // namespace
struct LogLevelsTest: testing::TestWithParam<decltype(log_levels)::value_type> {};
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);
}

View File

@ -3,38 +3,33 @@
* @brief Test src/input.*.
*/
#include <src/input.h>
#include <src/platform/common.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
class MouseHIDTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface<util::point_t> {
protected:
struct MouseHIDTest: PlatformTestSuite, testing::WithParamInterface<util::point_t> {
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);

View File

@ -2,12 +2,11 @@
* @file tests/unit/test_rswrapper.cpp
* @brief Test src/rswrapper.*
*/
extern "C" {
#include <src/rswrapper.h>
}
#include <tests/conftest.cpp>
#include "../tests_common.h"
TEST(ReedSolomonWrapperTests, InitTest) {
reed_solomon_init();

View File

@ -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 <tests/conftest.cpp>
#include "../tests_common.h"
TEST(ConcatAndInsertTests, ConcatNoInsertionTest) {
char b1[] = { 'a', 'b' };

View File

@ -4,60 +4,45 @@
*/
#include <src/video.h>
#include <tests/conftest.cpp>
#include "../tests_common.h"
class EncoderTest: public virtual BaseTest, public PlatformInitBase, public ::testing::WithParamInterface<std::tuple<std::basic_string_view<char>, video::encoder_t *>> {
protected:
struct EncoderTest: PlatformTestSuite, testing::WithParamInterface<video::encoder_t *> {
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
}

View File

@ -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
}

View File

@ -1,10 +0,0 @@
/**
* @file tests/utils.h
* @brief Declarations for utility functions.
*/
#pragma once
#include <string>
int
setEnv(const std::string &name, const std::string &value);