From 334267807fbd4e76b4025ea553522329f02570a5 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:03:42 -0400 Subject: [PATCH] tests: add httpcommon tests and add new file_handler methods (#2712) Co-authored-by: Mariotaku --- .../linux/flatpak/dev.lizardbyte.sunshine.yml | 2 + src/config.cpp | 4 +- src/confighttp.cpp | 4 +- src/file_handler.cpp | 31 +++++++++++ src/file_handler.h | 7 +++ src/httpcommon.cpp | 8 +++ tests/unit/test_file_handler.cpp | 41 ++++++++++++++ tests/unit/test_httpcommon.cpp | 53 +++++++++++++++++++ 8 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 tests/unit/test_httpcommon.cpp diff --git a/packaging/linux/flatpak/dev.lizardbyte.sunshine.yml b/packaging/linux/flatpak/dev.lizardbyte.sunshine.yml index ea20aa2a81a..1fe3eb192c0 100644 --- a/packaging/linux/flatpak/dev.lizardbyte.sunshine.yml +++ b/packaging/linux/flatpak/dev.lizardbyte.sunshine.yml @@ -265,6 +265,8 @@ modules: append-path: /usr/lib/sdk/node18/bin build-args: - --share=network + test-args: + - --share=network env: BUILD_VERSION: "@BUILD_VERSION@" BRANCH: "@GITHUB_BRANCH@" diff --git a/src/config.cpp b/src/config.cpp index 8faa9f01fa7..8ee20d52e06 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1255,9 +1255,7 @@ namespace config { bool config_loaded = false; try { // Create appdata folder if it does not exist - if (!boost::filesystem::exists(platf::appdata().string())) { - boost::filesystem::create_directories(platf::appdata().string()); - } + file_handler::make_directory(platf::appdata().string()); // Create empty config file if it does not exist if (!fs::exists(sunshine.config_file)) { diff --git a/src/confighttp.cpp b/src/confighttp.cpp index a237c1fc3a7..9b2c0d95f49 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -501,9 +501,7 @@ namespace confighttp { auto url = inputTree.get("url", ""); const std::string coverdir = platf::appdata().string() + "/covers/"; - if (!boost::filesystem::exists(coverdir)) { - boost::filesystem::create_directories(coverdir); - } + file_handler::make_directory(coverdir); std::basic_string path = coverdir + http::url_escape(key) + ".png"; if (!url.empty()) { diff --git a/src/file_handler.cpp b/src/file_handler.cpp index 1c7f98e9b3b..f86c1aff654 100644 --- a/src/file_handler.cpp +++ b/src/file_handler.cpp @@ -12,6 +12,37 @@ #include "logging.h" namespace file_handler { + /** + * @breif Get the parent directory of a file or directory. + * @param path The path of the file or directory. + * @return `std::string` : The parent directory. + */ + std::string + get_parent_directory(const std::string &path) { + // remove any trailing path separators + std::string trimmed_path = path; + while (!trimmed_path.empty() && trimmed_path.back() == '/') { + trimmed_path.pop_back(); + } + + std::filesystem::path p(trimmed_path); + return p.parent_path().string(); + } + + /** + * @brief Make a directory. + * @param path The path of the directory. + * @return `bool` : `true` on success, `false` on failure. + */ + bool + make_directory(const std::string &path) { + // first, check if the directory already exists + if (std::filesystem::exists(path)) { + return true; + } + + return std::filesystem::create_directories(path); + } /** * @brief Read a file to string. diff --git a/src/file_handler.h b/src/file_handler.h index aa2387f8469..5ff8015c635 100644 --- a/src/file_handler.h +++ b/src/file_handler.h @@ -7,8 +7,15 @@ #include namespace file_handler { + std::string + get_parent_directory(const std::string &path); + + bool + make_directory(const std::string &path); + std::string read_file(const char *path); + int write_file(const char *path, const std::string_view &contents); } // namespace file_handler diff --git a/src/httpcommon.cpp b/src/httpcommon.cpp index 123e7e9393c..6356273cc55 100644 --- a/src/httpcommon.cpp +++ b/src/httpcommon.cpp @@ -200,6 +200,14 @@ namespace http { BOOST_LOG(error) << "Couldn't create CURL instance"; return false; } + + std::string file_dir = file_handler::get_parent_directory(file); + if (!file_handler::make_directory(file_dir)) { + BOOST_LOG(error) << "Couldn't create directory ["sv << file_dir << ']'; + curl_easy_cleanup(curl); + return false; + } + FILE *fp = fopen(file.c_str(), "wb"); if (!fp) { BOOST_LOG(error) << "Couldn't open ["sv << file << ']'; diff --git a/tests/unit/test_file_handler.cpp b/tests/unit/test_file_handler.cpp index b77ba6188cc..faad1579d40 100644 --- a/tests/unit/test_file_handler.cpp +++ b/tests/unit/test_file_handler.cpp @@ -6,6 +6,47 @@ #include +class FileHandlerParentDirectoryTest: public ::testing::TestWithParam> {}; + +TEST_P(FileHandlerParentDirectoryTest, Run) { + auto [input, expected] = GetParam(); + EXPECT_EQ(file_handler::get_parent_directory(input), expected); +} + +INSTANTIATE_TEST_SUITE_P( + FileHandlerTests, + FileHandlerParentDirectoryTest, + ::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> {}; + +TEST_P(FileHandlerMakeDirectoryTest, Run) { + auto [input, expected, remove] = GetParam(); + const std::string test_dir = platf::appdata().string() + "/tests/path/"; + input = test_dir + input; + + EXPECT_EQ(file_handler::make_directory(input), expected); + EXPECT_TRUE(std::filesystem::exists(input)); + + // remove test directory + if (remove) { + std::filesystem::remove_all(test_dir); + EXPECT_FALSE(std::filesystem::exists(test_dir)); + } +} + +INSTANTIATE_TEST_SUITE_P( + FileHandlerTests, + FileHandlerMakeDirectoryTest, + ::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 diff --git a/tests/unit/test_httpcommon.cpp b/tests/unit/test_httpcommon.cpp new file mode 100644 index 00000000000..87e83e4ae8b --- /dev/null +++ b/tests/unit/test_httpcommon.cpp @@ -0,0 +1,53 @@ +/** + * @file tests/test_httpcommon.cpp + * @brief Test src/httpcommon.*. + */ +#include + +#include + +class UrlEscapeTest: public ::testing::TestWithParam> {}; + +TEST_P(UrlEscapeTest, Run) { + auto [input, expected] = GetParam(); + ASSERT_EQ(http::url_escape(input), expected); +} + +INSTANTIATE_TEST_SUITE_P( + UrlEscapeTests, + UrlEscapeTest, + ::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> {}; + +TEST_P(UrlGetHostTest, Run) { + auto [input, expected] = GetParam(); + ASSERT_EQ(http::url_get_host(input), expected); +} + +INSTANTIATE_TEST_SUITE_P( + UrlGetHostTests, + UrlGetHostTest, + ::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> {}; + +TEST_P(DownloadFileTest, Run) { + auto [url, filename] = GetParam(); + const std::string test_dir = platf::appdata().string() + "/tests/"; + std::basic_string path = test_dir + filename; + ASSERT_TRUE(http::download_file(url, path)); +} + +INSTANTIATE_TEST_SUITE_P( + DownloadFileTests, + DownloadFileTest, + ::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")));