Also, run all tests in a path with cyrillic chars and spacesdefault_compile_flags
@@ -11,6 +11,7 @@ | |||
#include <dds/util/paths.hpp> | |||
#include <dds/util/signal.hpp> | |||
#include <neo/assert.hpp> | |||
#include <range/v3/action/join.hpp> | |||
#include <range/v3/range/conversion.hpp> | |||
#include <range/v3/view/concat.hpp> | |||
@@ -22,6 +23,7 @@ | |||
#include <filesystem> | |||
#include <iostream> | |||
#include <locale.h> | |||
#include <sstream> | |||
namespace { | |||
@@ -896,7 +898,7 @@ struct cli_build_deps { | |||
## ## ## ## #### ## ## | |||
*/ | |||
int main(int argc, char** argv) { | |||
int main_fn(const std::vector<std::string>& argv) { | |||
spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v"); | |||
args::ArgumentParser parser("DDS - The drop-dead-simple library manager"); | |||
@@ -909,7 +911,7 @@ int main(int argc, char** argv) { | |||
cli_build_deps build_deps{cli}; | |||
try { | |||
parser.ParseCLI(argc, argv); | |||
parser.ParseCLI(argv); | |||
} catch (const args::Help&) { | |||
std::cout << parser; | |||
return 0; | |||
@@ -955,3 +957,47 @@ int main(int argc, char** argv) { | |||
return 2; | |||
} | |||
} | |||
#if NEO_OS_IS_WINDOWS | |||
std::string wstr_to_u8str(std::wstring_view in) { | |||
if (in.empty()) { | |||
return ""; | |||
} | |||
auto req_size = ::WideCharToMultiByte(CP_UTF8, | |||
0, | |||
in.data(), | |||
static_cast<int>(in.size()), | |||
nullptr, | |||
0, | |||
nullptr, | |||
nullptr); | |||
neo_assert(invariant, | |||
req_size > 0, | |||
"Failed to convert given unicode string for main() argv", | |||
req_size, | |||
std::system_category().message(::GetLastError()), | |||
::GetLastError()); | |||
std::string ret; | |||
ret.resize(req_size); | |||
::WideCharToMultiByte(CP_UTF8, | |||
0, | |||
in.data(), | |||
static_cast<int>(in.size()), | |||
ret.data(), | |||
static_cast<int>(ret.size()), | |||
nullptr, | |||
nullptr); | |||
return ret; | |||
} | |||
int wmain(int argc, wchar_t** argv) { | |||
std::vector<std::string> u8_argv; | |||
::setlocale(LC_ALL, ".utf8"); | |||
for (int i = 1; i < argc; ++i) { | |||
u8_argv.emplace_back(wstr_to_u8str(argv[i])); | |||
} | |||
return main_fn(u8_argv); | |||
} | |||
#else | |||
int main(int argc, char** argv) { return main_fn({argv + 1, argv + argc}); } | |||
#endif |
@@ -9,6 +9,17 @@ TEST_CASE("Create a simple database") { | |||
auto repo = dds::catalog::open(":memory:"s); | |||
} | |||
TEST_CASE("Open a catalog in a non-ascii path") { | |||
::setlocale(LC_ALL, ".utf8"); | |||
auto THIS_DIR = dds::fs::canonical(__FILE__).parent_path(); | |||
auto BUILD_DIR | |||
= (THIS_DIR.parent_path().parent_path().parent_path() / "_build").lexically_normal(); | |||
auto subdir = BUILD_DIR / "Ю́рий Алексе́евич Гага́рин"; | |||
dds::fs::remove_all(subdir); | |||
dds::catalog::open(subdir / "test.db"); | |||
dds::fs::remove_all(subdir); | |||
} | |||
class catalog_test_case { | |||
public: | |||
dds::catalog db = dds::catalog::open(":memory:"s); |
@@ -23,10 +23,23 @@ namespace { | |||
throw std::system_error(std::error_code(::GetLastError(), std::system_category()), what); | |||
} | |||
std::wstring widen(std::string_view s) { | |||
if (s.empty()) { | |||
return L""; | |||
} | |||
auto req_chars | |||
= ::MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0); | |||
std::wstring ret; | |||
ret.resize(req_chars); | |||
::MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), ret.data(), req_chars); | |||
return ret; | |||
} | |||
} // namespace | |||
proc_result dds::run_proc(const proc_options& opts) { | |||
auto cmd_str = quote_command(opts.command); | |||
auto cmd_str = quote_command(opts.command); | |||
auto cmd_wide = widen(cmd_str); | |||
dds_log(debug, "Spawning subprocess: {}", cmd_str); | |||
::SECURITY_ATTRIBUTES security = {}; | |||
@@ -46,14 +59,14 @@ proc_result dds::run_proc(const proc_options& opts) { | |||
wil::unique_process_information proc_info; | |||
::STARTUPINFOA startup_info = {}; | |||
::STARTUPINFOW startup_info = {}; | |||
::RtlSecureZeroMemory(&startup_info, sizeof startup_info); | |||
startup_info.hStdOutput = startup_info.hStdError = writer.get(); | |||
startup_info.dwFlags = STARTF_USESTDHANDLES; | |||
startup_info.cb = sizeof startup_info; | |||
// DO IT! | |||
okay = ::CreateProcessA(nullptr, // cmd[0].data(), | |||
cmd_str.data(), | |||
okay = ::CreateProcessW(nullptr, // cmd[0].data(), | |||
cmd_wide.data(), | |||
nullptr, | |||
nullptr, | |||
true, |
@@ -61,7 +61,7 @@ struct lock_data { | |||
shared_file_mutex::shared_file_mutex(path_ref filepath) | |||
: _path{filepath} { | |||
auto h = ::CreateFileA(_path.string().c_str(), | |||
auto h = ::CreateFileW(_path.native().c_str(), | |||
GENERIC_READ | GENERIC_WRITE, | |||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |||
nullptr, |
@@ -5,6 +5,7 @@ | |||
TEST_CASE("Simple glob") { | |||
auto this_dir = dds::fs::path(__FILE__).parent_path(); | |||
auto glob = dds::glob::compile("*.test.cpp"); | |||
::setlocale(LC_ALL, ".utf8"); | |||
auto it = glob.scan_from(this_dir); | |||
for (; it != glob.end(); ++it) { |
@@ -19,7 +19,7 @@ class DDS: | |||
self.dds_exe = dds_exe | |||
self.test_dir = test_dir | |||
self.source_root = project_dir | |||
self.scratch_dir = project_dir / '_test_scratch' | |||
self.scratch_dir = project_dir / '_test_scratch/Ю́рий Алексе́евич Гага́рин' | |||
self.scope = scope | |||
self.scope.callback(self.cleanup) | |||