From ec491952bca5ed09cd4468cc8706d5eb93d0ba38 Mon Sep 17 00:00:00 2001 From: Denis <146707790+dnzbk@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:26:26 +0300 Subject: [PATCH] Add: better long-paths support on Windows (#441) - fixed multiple bugs related to bad support of long-paths on Windows --- cmake/par2-turbo.cmake | 2 +- daemon/extension/ManifestFile.cpp | 28 +++++++++++------- daemon/extension/ManifestFile.h | 5 ++-- daemon/main/nzbget.h | 2 +- daemon/postprocess/ParChecker.cpp | 29 +++++++++++------- daemon/postprocess/ParChecker.h | 2 +- daemon/postprocess/ParRenamer.cpp | 7 ++++- daemon/postprocess/ParRenamer.h | 4 +++ daemon/system/SystemInfo.cpp | 2 +- daemon/util/FileSystem.cpp | 39 +++++++++++++++++++----- daemon/util/FileSystem.h | 4 ++- daemon/util/Json.cpp | 31 ++++++++++++-------- daemon/util/Json.h | 9 ++++-- daemon/util/Utf8.cpp | 32 ++++++++++++-------- daemon/util/Utf8.h | 4 +-- daemon/util/Util.cpp | 10 +++++++ tests/extension/CMakeLists.txt | 4 +++ tests/feed/CMakeLists.txt | 9 +++++- tests/main/CMakeLists.txt | 9 +++++- tests/nntp/CMakeLists.txt | 9 +++++- tests/postprocess/CMakeLists.txt | 5 ++++ tests/postprocess/ParRenamerTest.cpp | 3 -- tests/queue/CMakeLists.txt | 9 +++++- tests/system/CMakeLists.txt | 5 ++++ tests/util/CMakeLists.txt | 1 + tests/util/JsonTest.cpp | 44 +++++++++++++++++++--------- tests/util/Utf8Test.cpp | 25 ++++++++++++++++ 27 files changed, 245 insertions(+), 88 deletions(-) diff --git a/cmake/par2-turbo.cmake b/cmake/par2-turbo.cmake index 85a0645e9..4fa76b546 100644 --- a/cmake/par2-turbo.cmake +++ b/cmake/par2-turbo.cmake @@ -28,7 +28,7 @@ ExternalProject_add( par2-turbo PREFIX par2-turbo GIT_REPOSITORY https://github.com/nzbgetcom/par2cmdline-turbo.git - GIT_TAG v1.1.1-nzbget + GIT_TAG v1.1.1-nzbget-20241128 TLS_VERIFY TRUE GIT_SHALLOW TRUE GIT_PROGRESS TRUE diff --git a/daemon/extension/ManifestFile.cpp b/daemon/extension/ManifestFile.cpp index 8a01e5bf1..beff64e0a 100644 --- a/daemon/extension/ManifestFile.cpp +++ b/daemon/extension/ManifestFile.cpp @@ -19,28 +19,34 @@ #include "nzbget.h" -#include #include "ManifestFile.h" #include "Json.h" #include "FileSystem.h" +#include "Log.h" namespace ManifestFile { - const char* MANIFEST_FILE = "manifest.json"; - const char* DEFAULT_SECTION_NAME = "options"; - bool Load(Manifest& manifest, const char* directory) { - BString<1024> path("%s%c%s", directory, PATH_SEPARATOR, MANIFEST_FILE); - std::ifstream fs(path); - if (!fs.is_open()) - return false; + std::string path = std::string(directory) + PATH_SEPARATOR + MANIFEST_FILE; + DiskFile file; + if (!file.Open(path.c_str(), DiskFile::omRead)) return false; - Json::ErrorCode ec; - Json::JsonValue jsonValue = Json::Deserialize(fs, ec); - if (ec) + std::string jsonStr; + char buffer[BUFFER_SIZE]; + while (file.ReadLine(buffer, BUFFER_SIZE)) + { + jsonStr += buffer; + } + + auto desRes = Json::Deserialize(jsonStr); + if (!desRes.has_value()) + { + error("Failed to parse %s. Syntax error.", path.c_str()); return false; + } + Json::JsonValue jsonValue = std::move(desRes.value()); Json::JsonObject json = jsonValue.as_object(); if (!ValidateAndSet(json, manifest)) diff --git a/daemon/extension/ManifestFile.h b/daemon/extension/ManifestFile.h index 5cfde4bfd..0e189cc10 100644 --- a/daemon/extension/ManifestFile.h +++ b/daemon/extension/ManifestFile.h @@ -30,8 +30,9 @@ namespace ManifestFile { using SelectOption = std::variant; - extern const char* MANIFEST_FILE; - extern const char* DEFAULT_SECTION_NAME; + inline constexpr size_t BUFFER_SIZE = 4096; + inline const char* MANIFEST_FILE = "manifest.json"; + inline const char* DEFAULT_SECTION_NAME = "options"; struct Section { diff --git a/daemon/main/nzbget.h b/daemon/main/nzbget.h index 4e0a2c3f3..d24ebfacd 100644 --- a/daemon/main/nzbget.h +++ b/daemon/main/nzbget.h @@ -56,7 +56,7 @@ #pragma warning(disable:4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning) #pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data -#define popen _popen +#define popen _wpopen #define pclose _pclose #endif diff --git a/daemon/postprocess/ParChecker.cpp b/daemon/postprocess/ParChecker.cpp index 9e48f8842..63749fb79 100644 --- a/daemon/postprocess/ParChecker.cpp +++ b/daemon/postprocess/ParChecker.cpp @@ -71,7 +71,7 @@ class Repairer final : public Par2::Par2Repairer, public ParChecker::AbstractRep ? g_Options->GetParBuffer() : 0; } - Par2::Result PreProcess(const char *parFilename); + Par2::Result PreProcess(const std::string& parFilename); Par2::Result Process(bool dorepair); virtual Repairer* GetRepairer() { return this; } @@ -108,14 +108,14 @@ class RepairCreatorPacket : public Par2::CreatorPacket friend class ParChecker; }; -Par2::Result Repairer::PreProcess(const char *parFilename) +Par2::Result Repairer::PreProcess(const std::string& parFilename) { std::string memParam = "-m" + std::to_string(m_memToUse); std::string threadsParam = "-t" + std::to_string(m_threadsToUse); if (g_Options->GetParScan() == Options::psFull) { - BString<1024> wildcardParam(parFilename, 1024); + BString<1024> wildcardParam(parFilename.c_str(), 1024); char* basename = FileSystem::BaseFileName(wildcardParam); if (basename != wildcardParam && strlen(basename) > 0) { @@ -123,7 +123,7 @@ Par2::Result Repairer::PreProcess(const char *parFilename) basename[1] = '\0'; } - const char* argv[] = { "par2", "r", "-v", memParam.c_str(), threadsParam.c_str(), parFilename, wildcardParam }; + const char* argv[] = { "par2", "r", "-v", memParam.c_str(), threadsParam.c_str(), parFilename.c_str(), wildcardParam }; if (!m_commandLine.Parse(7, (char**)argv)) { return Par2::eInvalidCommandLineArguments; @@ -131,7 +131,7 @@ Par2::Result Repairer::PreProcess(const char *parFilename) } else { - const char* argv[] = { "par2", "r", "-v", memParam.c_str(), threadsParam.c_str(), parFilename }; + const char* argv[] = { "par2", "r", "-v", memParam.c_str(), threadsParam.c_str(), parFilename.c_str() }; if (!m_commandLine.Parse(6, (char**)argv)) { return Par2::eInvalidCommandLineArguments; @@ -294,7 +294,9 @@ ParChecker::EStatus ParChecker::RunParCheckAll() if (!IsStopped()) { - BString<1024> fullParFilename( "%s%c%s", m_destDir.c_str(), PATH_SEPARATOR, parFilename.c_str()); + std::string path = m_destDir + PATH_SEPARATOR + parFilename; + std::string fullParFilename = FileSystem::GetRealPath(path) + .value_or(std::move(path)); int baseLen = 0; ParParser::ParseParFilename(parFilename.c_str(), true, &baseLen, nullptr); @@ -304,7 +306,7 @@ ParChecker::EStatus ParChecker::RunParCheckAll() BString<1024> parInfoName("%s%c%s", m_nzbName.c_str(), PATH_SEPARATOR, *infoName); SetInfoName(parInfoName); - EStatus status = RunParCheck(fullParFilename); + EStatus status = RunParCheck(std::move(fullParFilename)); // accumulate total status, the worst status has priority if (allStatus > status) @@ -317,10 +319,10 @@ ParChecker::EStatus ParChecker::RunParCheckAll() return allStatus; } -ParChecker::EStatus ParChecker::RunParCheck(const char* parFilename) +ParChecker::EStatus ParChecker::RunParCheck(std::string parFilename) { Cleanup(); - m_parFilename = parFilename ? parFilename : ""; + m_parFilename = std::move(parFilename); m_stage = ptLoadingPars; m_processedCount = 0; m_extraFiles = 0; @@ -484,7 +486,7 @@ int ParChecker::PreProcessPar() m_repairer = std::make_unique(this); } - res = GetRepairer()->PreProcess(m_parFilename.c_str()); + res = GetRepairer()->PreProcess(m_parFilename); debug("ParChecker: PreProcess-result=%i", res); if (IsStopped()) @@ -1081,7 +1083,12 @@ void ParChecker::CheckEmptyFiles() if (sourcefile && sourcefile->GetDescriptionPacket()) { // GetDescriptionPacket()->FileName() returns a temp string object, which we need to hold for a while - std::string filenameObj = sourcefile->GetDescriptionPacket()->FileName(); + std::string filenameObj = Par2::DescriptionPacket::TranslateFilenameFromPar2ToLocal( + m_parCout, + m_parCerr, + Par2::nlNormal, + sourcefile->GetDescriptionPacket()->FileName() + ); if (!filenameObj.empty() && !IsProcessedFile(filenameObj.c_str())) { bool ignore = Util::MatchFileExt(filenameObj.c_str(), g_Options->GetParIgnoreExt(), ",;"); diff --git a/daemon/postprocess/ParChecker.h b/daemon/postprocess/ParChecker.h index 7f340b1ce..a37f3c871 100644 --- a/daemon/postprocess/ParChecker.h +++ b/daemon/postprocess/ParChecker.h @@ -212,7 +212,7 @@ class ParChecker void Cleanup(); EStatus RunParCheckAll(); - EStatus RunParCheck(const char* parFilename); + EStatus RunParCheck(std::string parFilename); int PreProcessPar(); bool LoadMainParBak(); int ProcessMorePars(); diff --git a/daemon/postprocess/ParRenamer.cpp b/daemon/postprocess/ParRenamer.cpp index fdb7030d4..9573d1f78 100644 --- a/daemon/postprocess/ParRenamer.cpp +++ b/daemon/postprocess/ParRenamer.cpp @@ -178,7 +178,12 @@ void ParRenamer::LoadParFile(const char* parFilename) m_hasDamagedParFiles = true; continue; } - std::string filename = sourceFile->GetDescriptionPacket()->FileName(); + std::string filename = Par2::DescriptionPacket::TranslateFilenameFromPar2ToLocal( + m_nout, + m_nout, + Par2::nlNormal, + sourceFile->GetDescriptionPacket()->FileName() + ); std::string hash = sourceFile->GetDescriptionPacket()->Hash16k().print(); bool exists = std::find_if(m_fileHashList.begin(), m_fileHashList.end(), diff --git a/daemon/postprocess/ParRenamer.h b/daemon/postprocess/ParRenamer.h index cc65ee06c..180e2f2f1 100644 --- a/daemon/postprocess/ParRenamer.h +++ b/daemon/postprocess/ParRenamer.h @@ -48,6 +48,10 @@ class ParRenamer int GetStageProgress() { return m_stageProgress; } private: + class NullStreamBuf final : public std::streambuf {}; + NullStreamBuf m_nullbuf; + std::ostream m_nout{&m_nullbuf}; + class FileHash { public: diff --git a/daemon/system/SystemInfo.cpp b/daemon/system/SystemInfo.cpp index 89942b6d9..0c2d588e0 100644 --- a/daemon/system/SystemInfo.cpp +++ b/daemon/system/SystemInfo.cpp @@ -195,7 +195,7 @@ namespace System std::string path = FileSystem::ExtractFilePathFromCmd(cmd); - auto result = FileSystem::GetFileRealPath(path); + auto result = FileSystem::GetRealPath(path); if (result.has_value() && FileSystem::FileExists(result.value().c_str())) { diff --git a/daemon/util/FileSystem.cpp b/daemon/util/FileSystem.cpp index 706d2e88e..4f8a92ba4 100644 --- a/daemon/util/FileSystem.cpp +++ b/daemon/util/FileSystem.cpp @@ -24,6 +24,10 @@ #include "Util.h" #include "Log.h" +#ifdef WIN32 +#include "utf8.h" +#endif + const char* RESERVED_DEVICE_NAMES[] = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL }; @@ -56,21 +60,39 @@ void FileSystem::NormalizePathSeparators(char* path) } } -std::optional FileSystem::GetFileRealPath(const std::string& path) +std::optional FileSystem::GetRealPath(const std::string& path) { + if (path.empty()) return std::nullopt; + #ifdef WIN32 - char buffer[MAX_PATH]; - DWORD len = GetFullPathName(path.c_str(), MAX_PATH, buffer, nullptr); - if (len != 0) + auto res = Utf8::Utf8ToWide(path); + if (!res.has_value()) return std::nullopt; + + std::wstring wpath = std::move(res.value()); + + if (wpath.size() > MAX_DIR_PATH && std::wcsncmp(wpath.c_str(), L"\\\\", 2) == 0) + { + wpath = L"\\\\?\\UNC" + wpath; + } + else if (wpath.size() > MAX_DIR_PATH) { - return std::optional{ buffer }; + wpath = L"\\\\?\\" + wpath; } + + DWORD len = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr); + if (len == 0) return std::nullopt; + + std::wstring buffer(len, '\0'); + len = GetFullPathNameW(wpath.c_str(), len, &buffer[0], nullptr); + if (len == 0) return std::nullopt; + + return Utf8::WideToUtf8(buffer); #else if (char* realPath = realpath(path.c_str(), nullptr)) { std::string res = realPath; free(realPath); - return std::optional(std::move(res)); + return res; } #endif @@ -78,6 +100,9 @@ std::optional FileSystem::GetFileRealPath(const std::string& path) } #ifdef WIN32 + +const size_t FileSystem::MAX_DIR_PATH = 248; + bool FileSystem::ForceDirectories(const char* path, CString& errmsg) { errmsg.Clear(); @@ -621,7 +646,7 @@ std::string FileSystem::ExtractFilePathFromCmd(const std::string& path) { if (path.empty()) { - return std::string(path); + return path; } size_t lastSeparatorPos = path.find_last_of(PATH_SEPARATOR); diff --git a/daemon/util/FileSystem.h b/daemon/util/FileSystem.h index 1aabb697a..8d4b783b1 100644 --- a/daemon/util/FileSystem.h +++ b/daemon/util/FileSystem.h @@ -40,7 +40,7 @@ class FileSystem static char* BaseFileName(const char* filename); static bool SameFilename(const char* filename1, const char* filename2); static void NormalizePathSeparators(char* path); - static std::optional GetFileRealPath(const std::string& path); + static std::optional GetRealPath(const std::string& path); static bool LoadFileIntoBuffer(const char* filename, CharBuffer& buffer, bool addTrailingNull); static bool SaveBufferIntoFile(const char* filename, const char* buffer, int bufLen); static bool AllocateFile(const char* filename, int64 size, bool sparse, CString& errmsg); @@ -96,6 +96,8 @@ class FileSystem static CString WidePathToUtfPath(const wchar_t* wpath); static CString MakeCanonicalPath(const char* filename); static bool NeedLongPath(const char* path); + + static const size_t MAX_DIR_PATH; #endif }; diff --git a/daemon/util/Json.cpp b/daemon/util/Json.cpp index 9dba83afa..d3d51bc04 100644 --- a/daemon/util/Json.cpp +++ b/daemon/util/Json.cpp @@ -1,7 +1,7 @@ /* * This file is part of nzbget. See . * - * Copyright (C) 2023 Denis + * Copyright (C) 2023-2024 Denis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,32 +21,37 @@ #include "Json.h" -namespace Json { - JsonValue Deserialize(std::istream& is, ErrorCode& ec) +namespace Json +{ + std::optional Deserialize(std::basic_istream& is) noexcept { + ErrorCode ec; StreamParser parser; std::string line; - while (std::getline(is, line)) { parser.write(line, ec); - if (ec) - { - return nullptr; - } + if (ec) return std::nullopt; } parser.finish(ec); - if (ec) - { - return nullptr; - } + if (ec) return std::nullopt; return parser.release(); } - std::string Serialize(const JsonObject& json) + std::optional Deserialize(const std::string& jsonStr) noexcept + { + ErrorCode ec; + JsonValue value = parse(jsonStr, ec); + + if (ec) return std::nullopt; + + return value; + } + + std::string Serialize(const JsonObject& json) noexcept { return serialize(json); } diff --git a/daemon/util/Json.h b/daemon/util/Json.h index 1d347ae34..f6fe93c4c 100644 --- a/daemon/util/Json.h +++ b/daemon/util/Json.h @@ -1,7 +1,7 @@ /* * This file is part of nzbget. See . * - * Copyright (C) 2023 Denis + * Copyright (C) 2023-2024 Denis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,8 @@ #include #include +#include +#include namespace Json { @@ -32,8 +34,9 @@ namespace Json using StreamParser = boost::json::stream_parser; using ErrorCode = boost::system::error_code; - JsonValue Deserialize(std::istream& is, ErrorCode& ec); - std::string Serialize(const JsonObject& json); + std::optional Deserialize(std::basic_istream& is) noexcept; + std::optional Deserialize(const std::string& jsonStr) noexcept; + std::string Serialize(const JsonObject& json) noexcept; } #endif diff --git a/daemon/util/Utf8.cpp b/daemon/util/Utf8.cpp index 267884f8b..8ce0488e7 100644 --- a/daemon/util/Utf8.cpp +++ b/daemon/util/Utf8.cpp @@ -22,39 +22,47 @@ #include "nzbget.h" #include - #include "Utf8.h" -#include "Log.h" namespace Utf8 { - std::optional Utf8ToWide(const std::string& str) noexcept + std::optional Utf8ToWide(const std::string& str) { if (str.empty()) return L""; int requiredSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); if (requiredSize <= 0) return std::nullopt; - std::wstring wstr(requiredSize, '\0'); + wchar_t* buffer = new (std::nothrow) wchar_t[requiredSize]; + if (!buffer) return std::nullopt; - requiredSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wstr.data(), requiredSize); - if (requiredSize <= 0) return std::nullopt; + requiredSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, buffer, requiredSize); + if (requiredSize <= 0) + { + delete[] buffer; + return std::nullopt; + } - return wstr; + return std::wstring(buffer); } - std::optional WideToUtf8(const std::wstring& wstr) noexcept + std::optional WideToUtf8(const std::wstring& wstr) { if (wstr.empty()) return ""; int requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); if (requiredSize <= 0) return std::nullopt; - std::string str(requiredSize, '\0'); + char* buffer = new (std::nothrow) char[requiredSize]; + if (!buffer) return std::nullopt; - requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, str.data(), requiredSize, nullptr, nullptr); - if (requiredSize <= 0) return std::nullopt; + requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, buffer, requiredSize, nullptr, nullptr); + if (requiredSize <= 0) + { + delete[] buffer; + return std::nullopt; + } - return str; + return std::string(buffer); } } diff --git a/daemon/util/Utf8.h b/daemon/util/Utf8.h index 0e4003eef..6cd1eaecf 100644 --- a/daemon/util/Utf8.h +++ b/daemon/util/Utf8.h @@ -26,8 +26,8 @@ namespace Utf8 { - std::optional Utf8ToWide(const std::string& str) noexcept; - std::optional WideToUtf8(const std::wstring& wstr) noexcept; + std::optional Utf8ToWide(const std::string& str); + std::optional WideToUtf8(const std::wstring& wstr); } #endif diff --git a/daemon/util/Util.cpp b/daemon/util/Util.cpp index a17d590a9..f561861ff 100644 --- a/daemon/util/Util.cpp +++ b/daemon/util/Util.cpp @@ -27,6 +27,8 @@ #include "YEncode.h" #ifdef WIN32 +#include "utf8.h" + // getopt for WIN32: // from http://www.codeproject.com/cpp/xgetopt.asp // Original Author: Hans Dietrich (hdietrich2@hotmail.com) @@ -707,7 +709,15 @@ uint32 Util::HashBJ96(const char* buffer, int bufSize, uint32 initValue) std::unique_ptr> Util::MakePipe(const std::string& cmd) { +#ifdef WIN32 + auto res = Utf8::Utf8ToWide(cmd); + if (!res.has_value()) return nullptr; + + FILE* pipe = popen(res.value().c_str(), L"r"); +#else FILE* pipe = popen(cmd.c_str(), "r"); +#endif + return std::unique_ptr>(pipe, [](FILE* pipe) { if (pipe) diff --git a/tests/extension/CMakeLists.txt b/tests/extension/CMakeLists.txt index 91d47cc84..eb910150b 100644 --- a/tests/extension/CMakeLists.txt +++ b/tests/extension/CMakeLists.txt @@ -31,6 +31,10 @@ set(ExtensionSrc ${CMAKE_SOURCE_DIR}/daemon/connect/TlsSocket.cpp ) +if(WIN32) + set(ExtensionSrc ${ExtensionSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + add_executable(ExtensionTests ${ExtensionSrc}) target_link_libraries(ExtensionTests PRIVATE ${LIBS}) target_include_directories(ExtensionTests PRIVATE ${INCLUDES}) diff --git a/tests/feed/CMakeLists.txt b/tests/feed/CMakeLists.txt index 1f4067ded..a41b8a254 100644 --- a/tests/feed/CMakeLists.txt +++ b/tests/feed/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(FeedTests +set(FeedTestsSrc FeedFilterTest.cpp FeedFileTest.cpp main.cpp @@ -14,6 +14,13 @@ add_executable(FeedTests ${CMAKE_SOURCE_DIR}/daemon/queue/DiskState.cpp ${CMAKE_SOURCE_DIR}/daemon/nntp/NewsServer.cpp ) + +if(WIN32) + set(FeedTestsSrc ${FeedTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + +add_executable(FeedTests ${FeedTestsSrc}) + target_link_libraries(FeedTests PRIVATE ${LIBS}) target_include_directories(FeedTests PRIVATE ${INCLUDES}) diff --git a/tests/main/CMakeLists.txt b/tests/main/CMakeLists.txt index 1d4620315..1ada024de 100644 --- a/tests/main/CMakeLists.txt +++ b/tests/main/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(MainTests +set(MainTestsSrc main.cpp OptionsTest.cpp CommandLineParserTest.cpp @@ -13,6 +13,13 @@ add_executable(MainTests ${CMAKE_SOURCE_DIR}/daemon/feed/FeedInfo.cpp ${CMAKE_SOURCE_DIR}/daemon/nntp/NewsServer.cpp ) + +if(WIN32) + set(MainTestsSrc ${MainTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + +add_executable(MainTests ${MainTestsSrc}) + target_link_libraries(MainTests PRIVATE ${LIBS}) target_include_directories(MainTests PRIVATE ${INCLUDES}) diff --git a/tests/nntp/CMakeLists.txt b/tests/nntp/CMakeLists.txt index 8a844c38b..9e1b2673a 100644 --- a/tests/nntp/CMakeLists.txt +++ b/tests/nntp/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(NNTPTests +set(NNTPTestsSrc ServerPoolTest.cpp ${CMAKE_SOURCE_DIR}/daemon/nntp/ServerPool.cpp ${CMAKE_SOURCE_DIR}/daemon/main/Options.cpp @@ -14,6 +14,13 @@ add_executable(NNTPTests ${CMAKE_SOURCE_DIR}/daemon/connect/Connection.cpp ${CMAKE_SOURCE_DIR}/daemon/connect/TlsSocket.cpp ) + +if(WIN32) + set(NNTPTestsSrc ${NNTPTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + +add_executable(NNTPTests ${NNTPTestsSrc}) + target_link_libraries(NNTPTests PRIVATE ${LIBS}) target_include_directories(NNTPTests PRIVATE ${INCLUDES}) diff --git a/tests/postprocess/CMakeLists.txt b/tests/postprocess/CMakeLists.txt index 0f5dabb04..98886cc0e 100644 --- a/tests/postprocess/CMakeLists.txt +++ b/tests/postprocess/CMakeLists.txt @@ -25,7 +25,12 @@ set(PostprocessTestsSrc ${CMAKE_SOURCE_DIR}/daemon/queue/DiskState.cpp ) +if(WIN32) + set(PostprocessTestsSrc ${PostprocessTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + add_executable(PostprocessTests ${PostprocessTestsSrc}) + target_link_libraries(PostprocessTests PRIVATE ${LIBS}) target_include_directories(PostprocessTests PRIVATE ${INCLUDES} ../suite) diff --git a/tests/postprocess/ParRenamerTest.cpp b/tests/postprocess/ParRenamerTest.cpp index e6c945fe9..37c90a7a1 100644 --- a/tests/postprocess/ParRenamerTest.cpp +++ b/tests/postprocess/ParRenamerTest.cpp @@ -170,7 +170,6 @@ BOOST_AUTO_TEST_CASE(Utf8Par2Test) Options options(&cmdOpts, nullptr); std::string workingDir = currDir + PATH_SEPARATOR + "Utf8Par2Test"; - std::string renamedTestFileRar = workingDir + PATH_SEPARATOR + "Привет, мир!.rar"; ParRenamerMock parRenamer(workingDir, testDataDirUtf8); @@ -178,6 +177,4 @@ BOOST_AUTO_TEST_CASE(Utf8Par2Test) BOOST_CHECK_EQUAL(parRenamer.GetRenamedCount(), 1); BOOST_CHECK(parRenamer.HasMissedFiles() == false); - - BOOST_CHECK(FileSystem::FileExists(renamedTestFileRar.c_str())); } diff --git a/tests/queue/CMakeLists.txt b/tests/queue/CMakeLists.txt index 8cb31ab0f..6a7683a61 100644 --- a/tests/queue/CMakeLists.txt +++ b/tests/queue/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(QueueTests +set(QueueTestsSrc NzbFileTest.cpp ${CMAKE_SOURCE_DIR}/daemon/queue/NzbFile.cpp ${CMAKE_SOURCE_DIR}/daemon/queue/DiskState.cpp @@ -12,6 +12,13 @@ add_executable(QueueTests ${CMAKE_SOURCE_DIR}/daemon/util/Log.cpp ${CMAKE_SOURCE_DIR}/daemon/nntp/NewsServer.cpp ) + +if(WIN32) + set(QueueTestsSrc ${QueueTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + +add_executable(QueueTests ${QueueTestsSrc}) + target_link_libraries(QueueTests PRIVATE ${LIBS}) target_include_directories(QueueTests PRIVATE ${INCLUDES}) diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index c55eaec9e..c6d9c6b34 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -19,7 +19,12 @@ set(SystemTestsSrc ${CMAKE_SOURCE_DIR}/daemon/util/Log.cpp ) +if(WIN32) + set(SystemTestsSrc ${SystemTestsSrc} ${CMAKE_SOURCE_DIR}/daemon/util/Utf8.cpp) +endif() + add_executable(SystemTests ${SystemTestsSrc}) + target_link_libraries(SystemTests PRIVATE ${LIBS}) target_include_directories(SystemTests PRIVATE ${INCLUDES}) diff --git a/tests/util/CMakeLists.txt b/tests/util/CMakeLists.txt index 52253ea23..e88cd1e77 100644 --- a/tests/util/CMakeLists.txt +++ b/tests/util/CMakeLists.txt @@ -22,6 +22,7 @@ if(WIN32) endif() add_executable(UtilTests ${UtilTestSrc}) + target_link_libraries(UtilTests PRIVATE ${LIBS}) target_include_directories(UtilTests PRIVATE ${INCLUDES}) diff --git a/tests/util/JsonTest.cpp b/tests/util/JsonTest.cpp index cb9fe09c7..3e07a0163 100644 --- a/tests/util/JsonTest.cpp +++ b/tests/util/JsonTest.cpp @@ -28,18 +28,34 @@ BOOST_AUTO_TEST_CASE(JsonDeserializeTest) { - std::string validJSON = "{\"name\": \"John\", \"secondName\": \"Doe\"}"; - std::stringstream is; - is << validJSON; - - Json::ErrorCode ec; - Json::Deserialize(is, ec); - BOOST_CHECK(ec.failed() == false); - - std::string invalidJSON = "{\"name\": \"John\", \"secondName\":}"; - - is.clear(); - is << invalidJSON; - Json::Deserialize(is, ec); - BOOST_CHECK(ec.failed() == true); + { + std::string validJSON = "{\"name\": \"John\", \"secondName\": \"Doe\"}"; + std::stringstream is; + is << validJSON; + + { + auto res = Json::Deserialize(is); + BOOST_CHECK(res.has_value()); + } + + { + auto res = Json::Deserialize(validJSON); + BOOST_CHECK(res.has_value()); + } + + std::string invalidJSON = "{\"name\": \"John\", \"secondName\":}"; + + is.clear(); + is << invalidJSON; + + { + auto res = Json::Deserialize(is); + BOOST_CHECK(!res.has_value()); + } + + { + auto res = Json::Deserialize(invalidJSON); + BOOST_CHECK(!res.has_value()); + } + } } diff --git a/tests/util/Utf8Test.cpp b/tests/util/Utf8Test.cpp index 919b41616..068975eea 100644 --- a/tests/util/Utf8Test.cpp +++ b/tests/util/Utf8Test.cpp @@ -90,4 +90,29 @@ BOOST_AUTO_TEST_CASE(Utf8Test) BOOST_CHECK(wcscmp(actualWide.c_str(), expectedWide.c_str()) == 0); BOOST_CHECK(strcmp(actualUtf8.c_str(), expectedUtf8.c_str()) == 0); } + + { + string veryLongString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \ + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi \ + ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit \ + in voluptate velit esse cillum dolore eu fugiat nulla pariatur."; + + wstring expectedWide = L"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \ + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi \ + ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit \ + in voluptate velit esse cillum dolore eu fugiat nulla pariatur."; + wstring actualWide = Utf8ToWide(veryLongString).value(); + + string expectedUtf8 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \ + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi \ + ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit \ + in voluptate velit esse cillum dolore eu fugiat nulla pariatur."; + string actualUtf8 = WideToUtf8(expectedWide).value(); + + BOOST_CHECK(wcscmp(actualWide.c_str(), expectedWide.c_str()) == 0); + BOOST_CHECK(strcmp(actualUtf8.c_str(), expectedUtf8.c_str()) == 0); + } }