1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2021-2022 Alexander Grund
4// Copyright (c) 2002, 2009, 2014 Peter Dimov
5//
6// Distributed under the Boost Software License, Version 1.0.
7// https://www.boost.org/LICENSE_1_0.txt
8
9#ifndef BOOST_LOCALE_UNIT_TEST_HPP
10#define BOOST_LOCALE_UNIT_TEST_HPP
11
12#include <boost/locale/config.hpp>
13#include <cstdlib>
14#include <iomanip>
15#include <iostream>
16#include <sstream>
17#include <stdexcept>
18#include <string>
19#include <typeinfo>
20#include <vector>
21
22#if defined(_MSC_VER) && defined(_CPPLIB_VER) && defined(_DEBUG)
23# include <crtdbg.h>
24#endif
25
26namespace boost { namespace locale { namespace test {
27 /// Name/path of current executable
28 std::string exe_name;
29
30 struct test_result {
31 test_result() : error_counter(0), test_counter(0)
32 {
33#if defined(_MSC_VER) && (_MSC_VER > 1310)
34 // disable message boxes on assert(), abort()
35 ::_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
36#endif
37#if defined(_MSC_VER) && defined(_CPPLIB_VER) && defined(_DEBUG)
38 // disable message boxes on iterator debugging violations
39 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
40 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
41#endif
42 }
43 int error_counter;
44 int test_counter;
45 };
46 inline test_result& results()
47 {
48 static test_result instance;
49 return instance;
50 }
51}}} // namespace boost::locale::test
52
53#ifndef BOOST_LOCALE_ERROR_LIMIT
54# define BOOST_LOCALE_ERROR_LIMIT 20
55#endif
56
57#define BOOST_LOCALE_STRINGIZE(x) #x
58
59#define THROW_IF_TOO_BIG(X) \
60 if((X) > BOOST_LOCALE_ERROR_LIMIT) \
61 throw std::runtime_error("Error limits reached, stopping unit test")
62
63#define TEST(X) \
64 do { \
65 boost::locale::test::results().test_counter++; \
66 if(X) \
67 break; \
68 std::cerr << "Error in line:" << __LINE__ << " " #X << std::endl; \
69 THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++); \
70 BOOST_LOCALE_START_CONST_CONDITION \
71 } while(0) BOOST_LOCALE_END_CONST_CONDITION
72
73#define TEST_REQUIRE(X) \
74 do { \
75 boost::locale::test::results().test_counter++; \
76 if(X) \
77 break; \
78 std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
79 throw std::runtime_error("Critical test " #X " failed"); \
80 BOOST_LOCALE_START_CONST_CONDITION \
81 } while(0) BOOST_LOCALE_END_CONST_CONDITION
82
83#define TEST_THROWS(X, E) \
84 do { \
85 boost::locale::test::results().test_counter++; \
86 try { \
87 X; \
88 } catch(E const& /*e*/) { \
89 break; \
90 } catch(...) { \
91 } \
92 std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
93 THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++); \
94 BOOST_LOCALE_START_CONST_CONDITION \
95 } while(0) BOOST_LOCALE_END_CONST_CONDITION
96
97void test_main(int argc, char** argv);
98
99int main(int argc, char** argv)
100{
101 {
102 using namespace boost::locale::test;
103 exe_name = argv[0];
104 if(exe_name.substr(pos: exe_name.length() - 4) == ".exe")
105 exe_name.resize(n: exe_name.length() - 4);
106 results(); // Instantiate
107 }
108 try {
109 test_main(argc, argv);
110 } catch(const std::exception& e) {
111 std::cerr << "Failed with exception " // LCOV_EXCL_LINE
112 << typeid(e).name() << '(' << e.what() << ')' << std::endl; // LCOV_EXCL_LINE
113 return EXIT_FAILURE; // LCOV_EXCL_LINE
114 }
115 using boost::locale::test::results;
116 if(results().test_counter > 0) {
117 int passed = results().test_counter - results().error_counter;
118 std::cout << std::endl;
119 std::cout << "Passed " << passed << " tests\n";
120 if(results().error_counter > 0)
121 std::cout << "Failed " << results().error_counter << " tests\n"; // LCOV_EXCL_LINE
122 std::cout << " " << std::fixed << std::setprecision(1) << std::setw(5)
123 << 100.0 * passed / results().test_counter << "% of tests completed successfully\n";
124 }
125 return results().error_counter == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
126}
127
128template<typename T>
129std::string to_string(T const& s)
130{
131 std::stringstream ss;
132 ss << s;
133 return ss.str();
134}
135
136const std::string& to_string(const std::string& s)
137{
138 return s;
139}
140
141template<typename T>
142std::string to_string(const std::vector<T>& v)
143{
144 std::stringstream ss;
145 bool first = true;
146 for(const T& e : v) {
147 if(!first)
148 ss << ", ";
149 first = false;
150 ss << to_string(e);
151 }
152 return ss.str();
153}
154
155/// Put the char into the stream making sure it is readable
156/// Fallback to the Unicode representation of it (e.g. U+00A0)
157template<typename Char>
158void stream_char(std::ostream& s, const Char c)
159{
160 if((c >= '!' && c <= '~') || c == ' ')
161 s << static_cast<char>(c);
162 else
163 s << "U+" << std::hex << std::uppercase << std::setw(sizeof(Char)) << std::setfill('0')
164 << static_cast<unsigned>(c);
165}
166
167template<typename Char>
168std::string to_string(const std::basic_string<Char>& s)
169{
170 std::stringstream ss;
171 for(const Char c : s)
172 stream_char(ss, c);
173 return ss.str();
174}
175
176template<size_t size>
177std::string to_string(const wchar_t (&s)[size])
178{
179 std::stringstream ss;
180 for(size_t i = 0; i < size; ++i)
181 stream_char(ss, s[i]);
182 return ss.str();
183}
184
185std::string to_string(const wchar_t* s)
186{
187 std::stringstream ss;
188 for(; *s; ++s)
189 stream_char(s&: ss, c: *s);
190 return ss.str();
191}
192
193// Unicode chars cannot be streamed directly (deleted overloads in C++20)
194template<typename Char>
195std::string to_string_char_impl(const Char c)
196{
197 std::stringstream ss;
198 stream_char(ss, c);
199 return ss.str();
200}
201
202std::string to_string(const wchar_t c)
203{
204 return to_string_char_impl(c);
205}
206std::string to_string(const char16_t c)
207{
208 return to_string_char_impl(c);
209}
210std::string to_string(const char32_t c)
211{
212 return to_string_char_impl(c);
213}
214
215#if BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 120000) && BOOST_WORKAROUND(__cplusplus, >= 202002L)
216// Avoid warning due to comparison-to-spaceship-rewrite, happening e.g. for string comparison
217// see https://github.com/llvm/llvm-project/issues/43670
218# define BOOST_LOCALE_SPACESHIP_NULLPTR_WARNING 1
219# pragma clang diagnostic push
220# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
221#else
222# define BOOST_LOCALE_SPACESHIP_NULLPTR_WARNING 0
223#endif
224
225template<typename T, typename U>
226void test_impl(bool success, T const& l, U const& r, const char* expr, const char* fail_expr, int line)
227{
228 boost::locale::test::results().test_counter++;
229 if(!success) {
230 std::cerr << "Error in line " << line << ": " << expr << std::endl;
231 std::cerr << "---- [" << to_string(l) << "] " << fail_expr << " [" << to_string(r) << "]" << std::endl;
232 THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++);
233 }
234}
235
236template<typename T, typename U>
237void test_eq_impl(T const& l, U const& r, const char* expr, int line)
238{
239 test_impl(l == r, l, r, expr, "!=", line);
240}
241
242template<typename T, typename U>
243void test_ne_impl(T const& l, U const& r, const char* expr, int line)
244{
245 test_impl(l != r, l, r, expr, "==", line);
246}
247
248template<typename T, typename U>
249void test_le_impl(T const& l, U const& r, const char* expr, int line)
250{
251 test_impl(l <= r, l, r, expr, ">", line);
252}
253
254template<typename T, typename U>
255void test_lt_impl(T const& l, U const& r, const char* expr, int line)
256{
257 test_impl(l < r, l, r, expr, ">=", line);
258}
259
260template<typename T, typename U>
261void test_ge_impl(T const& l, U const& r, const char* expr, int line)
262{
263 test_impl(l >= r, l, r, expr, "<", line);
264}
265
266template<typename T, typename U>
267void test_gt_impl(T const& l, U const& r, const char* expr, int line)
268{
269 test_impl(l > r, l, r, expr, "<=", line);
270}
271
272#define TEST_EQ(x, y) test_eq_impl(x, y, BOOST_LOCALE_STRINGIZE(x == y), __LINE__)
273#define TEST_NE(x, y) test_ne_impl(x, y, BOOST_LOCALE_STRINGIZE(x != y), __LINE__)
274#define TEST_LE(x, y) test_le_impl(x, y, BOOST_LOCALE_STRINGIZE(x <= y), __LINE__)
275#define TEST_LT(x, y) test_lt_impl(x, y, BOOST_LOCALE_STRINGIZE(x < y), __LINE__)
276#define TEST_GE(x, y) test_ge_impl(x, y, BOOST_LOCALE_STRINGIZE(x >= y), __LINE__)
277#define TEST_GT(x, y) test_gt_impl(x, y, BOOST_LOCALE_STRINGIZE(x > y), __LINE__)
278
279#if BOOST_LOCALE_SPACESHIP_NULLPTR_WARNING
280# pragma clang diagnostic pop
281#endif
282
283#ifdef BOOST_MSVC
284# define BOOST_LOCALE_DISABLE_UNREACHABLE_CODE_WARNING __pragma(warning(disable : 4702))
285#else
286# define BOOST_LOCALE_DISABLE_UNREACHABLE_CODE_WARNING
287#endif
288
289#endif
290

source code of boost/libs/locale/test/boostLocale/test/unit_test.hpp