• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   util_formatting_ostream.cpp
9  * \author Andrey Semashev
10  * \date   26.05.2013
11  *
12  * \brief  This header contains tests for the formatting output stream wrapper.
13  */
14 
15 #define BOOST_TEST_MODULE util_formatting_ostream
16 
17 #include <locale>
18 #include <string>
19 #include <iomanip>
20 #include <sstream>
21 #include <iostream>
22 #include <algorithm>
23 #include <boost/config.hpp>
24 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
25 #include <string_view>
26 #endif
27 #include <boost/test/unit_test.hpp>
28 #include <boost/utility/string_view.hpp>
29 #include <boost/log/utility/formatting_ostream.hpp>
30 #include "char_definitions.hpp"
31 
32 #if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
33 
34 #define BOOST_UTF8_DECL
35 #define BOOST_UTF8_BEGIN_NAMESPACE namespace {
36 #define BOOST_UTF8_END_NAMESPACE }
37 
38 #include <boost/detail/utf8_codecvt_facet.hpp>
39 #include <boost/detail/utf8_codecvt_facet.ipp>
40 
41 #endif // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
42 
43 namespace logging = boost::log;
44 
45 namespace {
46 
47 struct unreferencable_data
48 {
49     unsigned int m : 2;
50     unsigned int n : 6;
51 
52     enum my_enum
53     {
54         one = 1,
55         two = 2
56     };
57 
58     // The following static constants don't have definitions, so they can only be used in constant expressions.
59     // Trying to bind a reference to these members will result in linking errors.
60     static const int x = 7;
61     static const my_enum y = one;
62 
unreferencable_data__anonb2796b390111::unreferencable_data63     unreferencable_data()
64     {
65         m = 1;
66         n = 5;
67     }
68 };
69 
70 template< typename CharT >
71 struct test_impl
72 {
73     typedef CharT char_type;
74     typedef test_data< char_type > strings;
75     typedef std::basic_string< char_type > string_type;
76     typedef std::basic_ostringstream< char_type > ostream_type;
77     typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type;
78 
79     template< typename StringT >
width_formatting__anonb2796b390111::test_impl80     static void width_formatting()
81     {
82         // Check that widening works
83         {
84             string_type str_fmt;
85             formatting_ostream_type strm_fmt(str_fmt);
86             strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
87             strm_fmt.flush();
88 
89             ostream_type strm_correct;
90             strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
91 
92             BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
93         }
94 
95         // Check that the string is not truncated
96         {
97             string_type str_fmt;
98             formatting_ostream_type strm_fmt(str_fmt);
99             strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
100             strm_fmt.flush();
101 
102             ostream_type strm_correct;
103             strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
104 
105             BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
106         }
107     }
108 
109     template< typename StringT >
fill_formatting__anonb2796b390111::test_impl110     static void fill_formatting()
111     {
112         string_type str_fmt;
113         formatting_ostream_type strm_fmt(str_fmt);
114         strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
115         strm_fmt.flush();
116 
117         ostream_type strm_correct;
118         strm_correct << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
119 
120         BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
121     }
122 
123     template< typename StringT >
alignment__anonb2796b390111::test_impl124     static void alignment()
125     {
126         // Left alignment
127         {
128             string_type str_fmt;
129             formatting_ostream_type strm_fmt(str_fmt);
130             strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
131             strm_fmt.flush();
132 
133             ostream_type strm_correct;
134             strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
135 
136             BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
137         }
138 
139         // Right alignment
140         {
141             string_type str_fmt;
142             formatting_ostream_type strm_fmt(str_fmt);
143             strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
144             strm_fmt.flush();
145 
146             ostream_type strm_correct;
147             strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
148 
149             BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
150         }
151     }
152 
153 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
154     template< typename StringT >
rvalue_stream__anonb2796b390111::test_impl155     static void rvalue_stream()
156     {
157         string_type str_fmt;
158         formatting_ostream_type(str_fmt) << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC() << std::flush;
159 
160         ostream_type strm_correct;
161         strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
162 
163         BOOST_CHECK(equal_strings(str_fmt, strm_correct.str()));
164     }
165 #endif
166 
output_unreferencable_data__anonb2796b390111::test_impl167     static void output_unreferencable_data()
168     {
169         unreferencable_data data;
170         {
171             string_type str_fmt;
172             formatting_ostream_type strm_fmt(str_fmt);
173             strm_fmt << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y;
174             strm_fmt.flush();
175 
176             ostream_type strm_correct;
177             strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y);
178 
179             BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
180         }
181 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
182         {
183             string_type str_fmt;
184             formatting_ostream_type(str_fmt) << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y << std::flush;
185 
186             ostream_type strm_correct;
187             strm_correct << static_cast< unsigned int >(data.m) << static_cast< char_type >(' ') << static_cast< unsigned int >(data.n) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::x) << static_cast< char_type >(' ') << static_cast< int >(unreferencable_data::y);
188 
189             BOOST_CHECK(equal_strings(str_fmt, strm_correct.str()));
190         }
191 #endif
192     }
193 };
194 
195 } // namespace
196 
197 // Test support for width formatting
BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting,CharT,char_types)198 BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types)
199 {
200     typedef test_impl< CharT > test;
201     test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >();
202     test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >();
203     test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >();
204 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
205     test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >();
206 #endif
207 }
208 
209 // Test support for filler character setup
BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting,CharT,char_types)210 BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types)
211 {
212     typedef test_impl< CharT > test;
213     test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >();
214     test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >();
215     test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >();
216 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
217     test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >();
218 #endif
219 }
220 
221 // Test support for text alignment
BOOST_AUTO_TEST_CASE_TEMPLATE(alignment,CharT,char_types)222 BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types)
223 {
224     typedef test_impl< CharT > test;
225     test::BOOST_NESTED_TEMPLATE alignment< const CharT* >();
226     test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >();
227     test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >();
228 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
229     test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >();
230 #endif
231 }
232 
233 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
234 // Test support for rvalue stream objects
BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream,CharT,char_types)235 BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types)
236 {
237     typedef test_impl< CharT > test;
238     test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >();
239     test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >();
240     test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >();
241 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
242     test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >();
243 #endif
244 }
245 #endif
246 
247 // Test output of data to which a reference cannot be bound
BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data,CharT,char_types)248 BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data, CharT, char_types)
249 {
250     typedef test_impl< CharT > test;
251     test::output_unreferencable_data();
252 }
253 
254 namespace my_namespace {
255 
256 class A {};
257 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,A const &)258 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, A const&)
259 {
260     strm << "A";
261     return strm;
262 }
263 
264 class B {};
265 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,B &)266 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B&)
267 {
268     strm << "B";
269     return strm;
270 }
271 
272 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,B *)273 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*)
274 {
275     strm << "B*";
276     return strm;
277 }
278 
279 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,const B *)280 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*)
281 {
282     strm << "const B*";
283     return strm;
284 }
285 
286 class C {};
287 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,C const &)288 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&)
289 {
290     strm << "C";
291     return strm;
292 }
293 
294 enum E { eee };
295 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,E)296 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, E)
297 {
298     strm << "E";
299     return strm;
300 }
301 
302 } // namespace my_namespace
303 
304 // Test operator forwarding
BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding,CharT,char_types)305 BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types)
306 {
307     typedef CharT char_type;
308     typedef std::basic_string< char_type > string_type;
309     typedef std::basic_ostringstream< char_type > ostream_type;
310     typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type;
311 
312     string_type str_fmt;
313     formatting_ostream_type strm_fmt(str_fmt);
314 
315     const my_namespace::A a = my_namespace::A(); // const lvalue
316     my_namespace::B b; // lvalue
317     strm_fmt << a << b << my_namespace::C(); // rvalue
318     strm_fmt << my_namespace::eee;
319     strm_fmt << &b << (my_namespace::B const*)&b;
320     strm_fmt.flush();
321 
322     ostream_type strm_correct;
323     strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b;
324 
325     BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
326 }
327 
328 namespace my_namespace2 {
329 
330 class A {};
331 template< typename CharT, typename TraitsT, typename AllocatorT >
operator <<(logging::basic_formatting_ostream<CharT,TraitsT,AllocatorT> & strm,A const &)332 inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, A const&)
333 {
334     strm << "A";
335     return strm;
336 }
337 
338 class B {};
339 template< typename CharT, typename TraitsT, typename AllocatorT >
operator <<(logging::basic_formatting_ostream<CharT,TraitsT,AllocatorT> & strm,B &)340 inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, B&)
341 {
342     strm << "B";
343     return strm;
344 }
345 
346 class C {};
347 template< typename CharT, typename TraitsT, typename AllocatorT >
operator <<(logging::basic_formatting_ostream<CharT,TraitsT,AllocatorT> & strm,C const &)348 inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, C const&)
349 {
350     strm << "C";
351     return strm;
352 }
353 
354 class D {};
355 template< typename CharT, typename TraitsT, typename AllocatorT >
operator <<(logging::basic_formatting_ostream<CharT,TraitsT,AllocatorT> & strm,D &&)356 inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm,
357 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
358     D&&
359 #else
360     D const&
361 #endif
362     )
363 {
364     strm << "D";
365     return strm;
366 }
367 
368 enum E { eee };
369 template< typename CharT, typename TraitsT, typename AllocatorT >
operator <<(logging::basic_formatting_ostream<CharT,TraitsT,AllocatorT> & strm,E)370 inline logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (logging::basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, E)
371 {
372     strm << "E";
373     return strm;
374 }
375 
376 } // namespace my_namespace2
377 
378 // Test operator overriding
BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding,CharT,char_types)379 BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding, CharT, char_types)
380 {
381     typedef CharT char_type;
382     typedef std::basic_string< char_type > string_type;
383     typedef std::basic_ostringstream< char_type > ostream_type;
384     typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type;
385 
386     string_type str_fmt;
387     formatting_ostream_type strm_fmt(str_fmt);
388 
389     const my_namespace2::A a = my_namespace2::A(); // const lvalue
390     my_namespace2::B b; // lvalue
391     strm_fmt << a << b << my_namespace2::C() << my_namespace2::D(); // rvalue
392     strm_fmt << my_namespace2::eee;
393     strm_fmt.flush();
394 
395     ostream_type strm_correct;
396     strm_correct << "ABCDE";
397 
398     BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
399 }
400 
401 #if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
402 
403 namespace {
404 
405 const char narrow_chars[] = "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, \xd0\xbc\xd0\xb8\xd1\x80!";
406 const wchar_t wide_chars[] = { 0x041f, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, L',', L' ', 0x043c, 0x0438, 0x0440, L'!', 0 };
407 
408 template< typename StringT >
test_narrowing_code_conversion()409 void test_narrowing_code_conversion()
410 {
411     std::locale loc(std::locale::classic(), new utf8_codecvt_facet());
412 
413     // Test rvalues
414     {
415         std::string str_fmt;
416         logging::formatting_ostream strm_fmt(str_fmt);
417         strm_fmt.imbue(loc);
418         strm_fmt << (StringT)wide_chars;
419         strm_fmt.flush();
420 
421         BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars)));
422     }
423     // Test lvalues
424     {
425         std::string str_fmt;
426         logging::formatting_ostream strm_fmt(str_fmt);
427         strm_fmt.imbue(loc);
428         StringT wstr = StringT(wide_chars);
429         strm_fmt << wstr;
430         strm_fmt.flush();
431 
432         BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars)));
433     }
434     // Test const lvalues
435     {
436         std::string str_fmt;
437         logging::formatting_ostream strm_fmt(str_fmt);
438         strm_fmt.imbue(loc);
439         const StringT wstr = StringT(wide_chars);
440         strm_fmt << wstr;
441         strm_fmt.flush();
442 
443         BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars)));
444     }
445 }
446 
447 template< typename StringT >
test_widening_code_conversion()448 void test_widening_code_conversion()
449 {
450     std::locale loc(std::locale::classic(), new utf8_codecvt_facet());
451 
452     // Test rvalues
453     {
454         std::wstring str_fmt;
455         logging::wformatting_ostream strm_fmt(str_fmt);
456         strm_fmt.imbue(loc);
457         strm_fmt << (StringT)narrow_chars;
458         strm_fmt.flush();
459 
460         BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars)));
461     }
462     // Test lvalues
463     {
464         std::wstring str_fmt;
465         logging::wformatting_ostream strm_fmt(str_fmt);
466         strm_fmt.imbue(loc);
467         StringT str = StringT(narrow_chars);
468         strm_fmt << str;
469         strm_fmt.flush();
470 
471         BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars)));
472     }
473     // Test const lvalues
474     {
475         std::wstring str_fmt;
476         logging::wformatting_ostream strm_fmt(str_fmt);
477         strm_fmt.imbue(loc);
478         const StringT str = StringT(narrow_chars);
479         strm_fmt << str;
480         strm_fmt.flush();
481 
482         BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars)));
483     }
484 }
485 
486 } // namespace
487 
488 // Test character code conversion
BOOST_AUTO_TEST_CASE(character_code_conversion)489 BOOST_AUTO_TEST_CASE(character_code_conversion)
490 {
491     test_narrowing_code_conversion< const wchar_t* >();
492     test_widening_code_conversion< const char* >();
493     test_narrowing_code_conversion< std::wstring >();
494     test_widening_code_conversion< std::string >();
495     test_narrowing_code_conversion< boost::wstring_view >();
496     test_widening_code_conversion< boost::string_view >();
497 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
498     test_narrowing_code_conversion< std::wstring_view >();
499     test_widening_code_conversion< std::string_view >();
500 #endif
501 }
502 
503 #endif
504