// // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include "test.hpp" #include #include #include #include #include #include #include bool is_ascii(const std::string& s) { return std::find_if(s.begin(), s.end(), [](const char c) { return static_cast(c) > 0x7F; }) == s.end(); } std::string replace_non_ascii(const std::string& s) // LCOV_EXCL_LINE { std::string::const_iterator it = s.begin(); namespace utf = nowide::utf; using utf8 = utf::utf_traits; std::string result; result.reserve(s.size()); while(it != s.end()) { utf::code_point c = utf8::decode(it, s.end()); TEST(c != utf::illegal && c != utf::incomplete); if(c > 0x7F) c = '?'; // WinAPI seems to do this result.push_back(static_cast(c)); } return result; } void compare_string_arrays(char** main_val, char** utf8_val, bool sort) { std::vector vec_main, vec_utf8; for(; *main_val; ++main_val) vec_main.push_back(std::string(*main_val)); for(; *utf8_val; ++utf8_val) vec_utf8.push_back(std::string(*utf8_val)); // Same number of strings TEST_EQ(vec_main.size(), vec_utf8.size()); if(sort) { // Order doesn't matter std::sort(vec_main.begin(), vec_main.end()); std::sort(vec_utf8.begin(), vec_utf8.end()); } for(size_t i = 0; i < vec_main.size(); ++i) { // Skip strings with non-ascii chars if(is_ascii(vec_main[i]) && vec_main[i] != vec_utf8[i]) TEST_EQ(vec_main[i], replace_non_ascii(vec_utf8[i])); } } void compare_getenv(char** env) { // For all all variables in env check against getenv for(char** e = env; *e != 0; e++) { const char* key_begin = *e; const char* key_end = strchr(key_begin, '='); TEST(key_end); std::string key = std::string(key_begin, key_end); const char* std_value = std::getenv(key.c_str()); const char* bnw_value = nowide::getenv(key.c_str()); assert(std_value); TEST(bnw_value); // Compare only if ascii if(is_ascii(std_value) && std::string(std_value) != std::string(bnw_value)) TEST_EQ(std_value, replace_non_ascii(bnw_value)); } } const std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd"; void run_child(int argc, char** argv, char** env) { // Test arguments TEST_EQ(argc, 2); TEST_EQ(argv[1], example); TEST(argv[2] == nullptr); // Test getenv TEST(nowide::getenv("NOWIDE_TEST")); TEST_EQ(nowide::getenv("NOWIDE_TEST"), example); TEST(!nowide::getenv("NOWIDE_TEST_NONE")); // Empty variables are unreliable on windows, hence skip. E.g. using "set FOO=" unsets FOO #ifndef NOWIDE_WINDOWS TEST(nowide::getenv("NOWIDE_EMPTY")); TEST_EQ(nowide::getenv("NOWIDE_EMPTY"), std::string()); #endif // !_WIN32 // This must be contained in env std::string sample = "NOWIDE_TEST=" + example; bool found = false; for(char** e = env; *e != 0; e++) { if(*e == sample) found = true; } TEST(found); std::cout << "Subprocess ok" << std::endl; } void run_parent(const std::string& exe_path) { TEST(nowide::system(nullptr) != 0); const std::string command = "\"" + exe_path + "\" " + example; #if NOWIDE_TEST_USE_NARROW TEST_EQ(nowide::setenv("NOWIDE_TEST", example.c_str(), 1), 0); TEST_EQ(nowide::setenv("NOWIDE_TEST_NONE", example.c_str(), 1), 0); TEST_EQ(nowide::unsetenv("NOWIDE_TEST_NONE"), 0); TEST_EQ(nowide::setenv("NOWIDE_EMPTY", "", 1), 0); TEST(nowide::getenv("NOWIDE_EMPTY")); TEST_EQ(nowide::system(command.c_str()), 0); std::cout << "Parent ok" << std::endl; #else const std::wstring envVar = L"NOWIDE_TEST=" + nowide::widen(example); TEST_EQ(_wputenv(envVar.c_str()), 0); const std::wstring wcommand = nowide::widen(command); TEST_EQ(_wsystem(wcommand.c_str()), 0); std::cout << "Wide Parent ok" << std::endl; #endif } // coverity[root_function] void test_main(int argc, char** argv, char** env) { const int old_argc = argc; char** old_argv = argv; char** old_env = env; { nowide::args _(argc, argv, env); TEST_EQ(argc, old_argc); std::cout << "Checking arguments" << std::endl; compare_string_arrays(old_argv, argv, false); std::cout << "Checking env" << std::endl; compare_string_arrays(old_env, env, true); compare_getenv(env); } // When `args` is destructed the old values must be restored TEST_EQ(argc, old_argc); TEST(argv == old_argv); TEST(env == old_env); nowide::args a(argc, argv, env); if(argc == 1) run_parent(argv[0]); else run_child(argc, argv, env); }