mirror of
https://github.com/boostorg/nowide.git
synced 2026-02-22 03:22:32 +00:00
Improve test_system
Split parent and child code into functions for better output Introduce TEST_EQUAL for better output Compare each entry in env-pointer and argv Don't check env-pointer values against getenv() as they might differ: When launching the program from e.g. bash with `temp=bar`then there is TEMP and temp in the list (env-pointer) but getenv/GetEnvironmentVariable is case insensitive picking up the first matching value.
This commit is contained in:
@@ -51,6 +51,16 @@ inline void test_failed(const char* expr, const char* file, const int line, cons
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline void test_equal_impl(const T& lhs, const U& rhs, const char* file, const int line, const char* function)
|
||||
{
|
||||
if(lhs == rhs)
|
||||
return;
|
||||
std::ostringstream ss;
|
||||
ss << "[" << lhs << "!=" << rhs << "]";
|
||||
test_failed(ss.str().c_str(), file, line, function);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DISABLE_CONST_EXPR_DETECTED __pragma(warning(push)) __pragma(warning(disable : 4127))
|
||||
#define DISABLE_CONST_EXPR_DETECTED_POP __pragma(warning(pop))
|
||||
@@ -68,5 +78,13 @@ inline void test_failed(const char* expr, const char* file, const int line, cons
|
||||
test_failed(#x, __FILE__, __LINE__, __FUNCTION__); \
|
||||
DISABLE_CONST_EXPR_DETECTED \
|
||||
} while(0) DISABLE_CONST_EXPR_DETECTED_POP
|
||||
#define TEST_EQ(lhs, rhs) \
|
||||
do \
|
||||
{ \
|
||||
test_mon(); \
|
||||
test_equal_impl((lhs), (rhs), __FILE__, __LINE__, __FUNCTION__); \
|
||||
break; \
|
||||
DISABLE_CONST_EXPR_DETECTED \
|
||||
} while(0) DISABLE_CONST_EXPR_DETECTED_POP
|
||||
|
||||
#endif // #ifndef BOOST_NOWIDE_LIB_TEST_H_INCLUDED
|
||||
|
||||
@@ -5,76 +5,180 @@
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "test.hpp"
|
||||
#include <boost/nowide/args.hpp>
|
||||
#include <boost/nowide/cstdlib.hpp>
|
||||
#include <boost/nowide/detail/convert.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool is_ascii(const std::string& s)
|
||||
{
|
||||
for(std::string::const_iterator it = s.begin(); it != s.end(); ++it)
|
||||
{
|
||||
if(static_cast<unsigned char>(*it) > 0x7F)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string replace_non_ascii(const std::string& s)
|
||||
{
|
||||
std::string::const_iterator it = s.begin();
|
||||
namespace utf = boost::nowide::detail::utf;
|
||||
typedef utf::utf_traits<char> utf8;
|
||||
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<char>(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void compare_string_arrays(char** main_val, char** utf8_val, bool sort)
|
||||
{
|
||||
std::vector<std::string> 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]))
|
||||
continue;
|
||||
if(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 = boost::nowide::getenv(key.c_str());
|
||||
// If std_value is set, bnw value must be too and be equal, else bnw value must be unset too
|
||||
if(std_value)
|
||||
{
|
||||
TEST(bnw_value);
|
||||
// Compare only if ascii
|
||||
if(!is_ascii(std_value))
|
||||
continue;
|
||||
if(std::string(std_value) != std::string(bnw_value))
|
||||
TEST_EQ(std_value, replace_non_ascii(bnw_value));
|
||||
} else
|
||||
TEST(!bnw_value);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd";
|
||||
|
||||
void run_child(int argc, char** argv, char** env)
|
||||
{
|
||||
// Test arguments
|
||||
TEST(argc == 2);
|
||||
TEST_EQ(argv[1], example);
|
||||
TEST(argv[2] == 0);
|
||||
|
||||
// Test getenv
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST"));
|
||||
TEST_EQ(boost::nowide::getenv("BOOST_NOWIDE_TEST"), example);
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST_NONE") == 0);
|
||||
// Empty variables are unreliable on windows, hence skip. E.g. using "set FOO=" unsets FOO
|
||||
#ifndef BOOST_WINDOWS
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
|
||||
TEST_EQ(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"), std::string());
|
||||
#endif // !_WIN32
|
||||
|
||||
// This must be contained in env
|
||||
std::string sample = "BOOST_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 char* exe_path)
|
||||
{
|
||||
#if BOOST_NOWIDE_TEST_USE_NARROW
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST", example.c_str(), 1) == 0);
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST_NONE", example.c_str(), 1) == 0);
|
||||
TEST(boost::nowide::unsetenv("BOOST_NOWIDE_TEST_NONE") == 0);
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_EMPTY", "", 1) == 0);
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
|
||||
std::string command = "\"";
|
||||
command += exe_path;
|
||||
command += "\" ";
|
||||
command += example;
|
||||
TEST(boost::nowide::system(command.c_str()) == 0);
|
||||
std::cout << "Parent ok" << std::endl;
|
||||
#else
|
||||
std::wstring envVar = L"BOOST_NOWIDE_TEST=" + boost::nowide::widen(example);
|
||||
TEST(_wputenv(envVar.c_str()) == 0);
|
||||
std::wstring wcommand = boost::nowide::widen(exe_path) + L" " + boost::nowide::widen(example);
|
||||
TEST(_wsystem(wcommand.c_str()) == 0);
|
||||
std::cout << "Wide Parent ok" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** env)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string example = "\xd7\xa9-\xd0\xbc-\xce\xbd";
|
||||
boost::nowide::args a(argc, argv, env);
|
||||
if(argc == 2 && argv[1][0] != '-')
|
||||
const int old_argc = argc;
|
||||
char** old_argv = argv;
|
||||
char** old_env = env;
|
||||
{
|
||||
TEST(argv[1] == example);
|
||||
TEST(argv[2] == 0);
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST"));
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST_NONE") == 0);
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_TEST") == example);
|
||||
// Empty variables are unreliable on windows, hence skip. E.g. using "set FOO=" unsets FOO
|
||||
#ifndef BOOST_WINDOWS
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY") == std::string());
|
||||
#endif // !_WIN32
|
||||
|
||||
std::string sample = "BOOST_NOWIDE_TEST=" + example;
|
||||
bool found = false;
|
||||
for(char** e = env; *e != 0; e++)
|
||||
{
|
||||
char* eptr = *e;
|
||||
std::cout << "Checking " << eptr << std::endl;
|
||||
char* key_end = strchr(eptr, '=');
|
||||
TEST(key_end);
|
||||
std::string key = std::string(eptr, key_end);
|
||||
std::string value = key_end + 1;
|
||||
#ifdef BOOST_WINDOWS
|
||||
if(value.empty())
|
||||
continue;
|
||||
#endif
|
||||
std::cout << "Key: " << key << " Value: " << value << std::endl;
|
||||
TEST(boost::nowide::getenv(key.c_str()));
|
||||
TEST(boost::nowide::getenv(key.c_str()) == value);
|
||||
if(*e == sample)
|
||||
found = true;
|
||||
}
|
||||
TEST(found);
|
||||
std::cout << "Subprocess ok" << std::endl;
|
||||
} else if(argc == 1)
|
||||
{
|
||||
#if BOOST_NOWIDE_TEST_USE_NARROW
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST", example.c_str(), 1) == 0);
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_TEST_NONE", example.c_str(), 1) == 0);
|
||||
TEST(boost::nowide::unsetenv("BOOST_NOWIDE_TEST_NONE") == 0);
|
||||
TEST(boost::nowide::setenv("BOOST_NOWIDE_EMPTY", "", 1) == 0);
|
||||
TEST(boost::nowide::getenv("BOOST_NOWIDE_EMPTY"));
|
||||
std::string command = "\"";
|
||||
command += argv[0];
|
||||
command += "\" ";
|
||||
command += example;
|
||||
TEST(boost::nowide::system(command.c_str()) == 0);
|
||||
std::cout << "Parent ok" << std::endl;
|
||||
#else
|
||||
std::wstring envVar = L"BOOST_NOWIDE_TEST=" + boost::nowide::widen(example);
|
||||
TEST(_wputenv(envVar.c_str()) == 0);
|
||||
std::wstring wcommand = boost::nowide::widen(argv[0]) + L" " + boost::nowide::widen(example);
|
||||
TEST(_wsystem(wcommand.c_str()) == 0);
|
||||
std::cout << "Wide Parent ok" << std::endl;
|
||||
#endif
|
||||
boost::nowide::args _(argc, argv, env);
|
||||
TEST(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(argc == old_argc);
|
||||
TEST(argv == old_argv);
|
||||
TEST(env == old_env);
|
||||
|
||||
boost::nowide::args a(argc, argv, env);
|
||||
if(argc == 1)
|
||||
run_parent(argv[0]);
|
||||
else
|
||||
run_child(argc, argv, env);
|
||||
} catch(const std::exception& e)
|
||||
{
|
||||
std::cerr << "Failed " << e.what() << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user