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   src_record_ostream.cpp
9  * \author Andrey Semashev
10  * \date   23.08.2015
11  *
12  * \brief  This header contains tests for the log record formatting output stream.
13  */
14 
15 #define BOOST_TEST_MODULE src_record_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/core/record.hpp>
30 #include <boost/log/sources/record_ostream.hpp>
31 #include <boost/log/expressions/message.hpp>
32 #include <boost/log/attributes/value_extraction.hpp>
33 #include "char_definitions.hpp"
34 #include "make_record.hpp"
35 
36 namespace logging = boost::log;
37 namespace expr = boost::log::expressions;
38 
39 namespace {
40 
41 struct unreferencable_data
42 {
43     unsigned int m : 2;
44     unsigned int n : 6;
45 
46     enum my_enum
47     {
48         one = 1,
49         two = 2
50     };
51 
52     // The following static constants don't have definitions, so they can only be used in constant expressions.
53     // Trying to bind a reference to these members will result in linking errors.
54     static const int x = 7;
55     static const my_enum y = one;
56 
unreferencable_data__anon4b7296e10111::unreferencable_data57     unreferencable_data()
58     {
59         m = 1;
60         n = 5;
61     }
62 };
63 
64 template< typename CharT >
65 struct test_impl
66 {
67     typedef CharT char_type;
68     typedef test_data< char_type > strings;
69     typedef std::basic_string< char_type > string_type;
70     typedef std::basic_ostringstream< char_type > ostream_type;
71     typedef logging::basic_record_ostream< char_type > record_ostream_type;
72 
73     template< typename StringT >
width_formatting__anon4b7296e10111::test_impl74     static void width_formatting()
75     {
76         // Check that widening works
77         {
78             logging::record rec = make_record();
79             BOOST_REQUIRE(!!rec);
80             record_ostream_type strm_fmt(rec);
81             strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
82             strm_fmt.flush();
83             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
84 
85             ostream_type strm_correct;
86             strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
87 
88             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
89         }
90 
91         // Check that the string is not truncated
92         {
93             logging::record rec = make_record();
94             BOOST_REQUIRE(!!rec);
95             record_ostream_type strm_fmt(rec);
96             strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
97             strm_fmt.flush();
98             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
99 
100             ostream_type strm_correct;
101             strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
102 
103             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
104         }
105     }
106 
107     template< typename StringT >
fill_formatting__anon4b7296e10111::test_impl108     static void fill_formatting()
109     {
110         logging::record rec = make_record();
111         BOOST_REQUIRE(!!rec);
112         record_ostream_type strm_fmt(rec);
113         strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
114         strm_fmt.flush();
115         string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
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(rec_message, strm_correct.str()));
121     }
122 
123     template< typename StringT >
alignment__anon4b7296e10111::test_impl124     static void alignment()
125     {
126         // Left alignment
127         {
128             logging::record rec = make_record();
129             BOOST_REQUIRE(!!rec);
130             record_ostream_type strm_fmt(rec);
131             strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
132             strm_fmt.flush();
133             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
134 
135             ostream_type strm_correct;
136             strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
137 
138             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
139         }
140 
141         // Right alignment
142         {
143             logging::record rec = make_record();
144             BOOST_REQUIRE(!!rec);
145             record_ostream_type strm_fmt(rec);
146             strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
147             strm_fmt.flush();
148             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
149 
150             ostream_type strm_correct;
151             strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
152 
153             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
154         }
155     }
156 
157 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
158     template< typename StringT >
rvalue_stream__anon4b7296e10111::test_impl159     static void rvalue_stream()
160     {
161         logging::record rec = make_record();
162         BOOST_REQUIRE(!!rec);
163         record_ostream_type(rec) << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC() << std::flush;
164         string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
165 
166         ostream_type strm_correct;
167         strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
168 
169         BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
170     }
171 #endif
172 
output_unreferencable_data__anon4b7296e10111::test_impl173     static void output_unreferencable_data()
174     {
175         unreferencable_data data;
176         {
177             logging::record rec = make_record();
178             BOOST_REQUIRE(!!rec);
179             record_ostream_type strm_fmt(rec);
180             strm_fmt << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y;
181             strm_fmt.flush();
182             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
183 
184             ostream_type strm_correct;
185             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);
186 
187             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
188         }
189 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
190         {
191             logging::record rec = make_record();
192             BOOST_REQUIRE(!!rec);
193             record_ostream_type(rec) << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y << std::flush;
194             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
195 
196             ostream_type strm_correct;
197             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);
198 
199             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
200         }
201 #endif
202     }
203 
formatting_params_restoring__anon4b7296e10111::test_impl204     static void formatting_params_restoring()
205     {
206         record_ostream_type strm_fmt;
207         {
208             logging::record rec = make_record();
209             BOOST_REQUIRE(!!rec);
210             strm_fmt.attach_record(rec);
211             strm_fmt << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15;
212             strm_fmt.flush();
213             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
214             strm_fmt.detach_from_record();
215 
216             ostream_type strm_correct;
217             strm_correct << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15;
218 
219             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
220         }
221 
222         // Check that the formatting flags are reset for the next record
223         {
224             logging::record rec = make_record();
225             BOOST_REQUIRE(!!rec);
226             strm_fmt.attach_record(rec);
227             strm_fmt << 15;
228             strm_fmt.flush();
229             string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
230             strm_fmt.detach_from_record();
231 
232             ostream_type strm_correct;
233             strm_correct << 15;
234 
235             BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
236         }
237     }
238 };
239 
240 } // namespace
241 
242 // Test support for width formatting
BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting,CharT,char_types)243 BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types)
244 {
245     typedef test_impl< CharT > test;
246     test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >();
247     test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >();
248     test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >();
249 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
250     test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >();
251 #endif
252 }
253 
254 // Test support for filler character setup
BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting,CharT,char_types)255 BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types)
256 {
257     typedef test_impl< CharT > test;
258     test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >();
259     test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >();
260     test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >();
261 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
262     test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >();
263 #endif
264 }
265 
266 // Test support for text alignment
BOOST_AUTO_TEST_CASE_TEMPLATE(alignment,CharT,char_types)267 BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types)
268 {
269     typedef test_impl< CharT > test;
270     test::BOOST_NESTED_TEMPLATE alignment< const CharT* >();
271     test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >();
272     test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >();
273 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
274     test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >();
275 #endif
276 }
277 
278 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
279 // Test support for rvalue stream objects
BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream,CharT,char_types)280 BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types)
281 {
282     typedef test_impl< CharT > test;
283     test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >();
284     test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >();
285     test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >();
286 #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
287     test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >();
288 #endif
289 }
290 #endif
291 
292 // Test output of data to which a reference cannot be bound
BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data,CharT,char_types)293 BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data, CharT, char_types)
294 {
295     typedef test_impl< CharT > test;
296     test::output_unreferencable_data();
297 }
298 
299 // Test that formatting settings are reset for new log records
BOOST_AUTO_TEST_CASE_TEMPLATE(formatting_params_restoring,CharT,char_types)300 BOOST_AUTO_TEST_CASE_TEMPLATE(formatting_params_restoring, CharT, char_types)
301 {
302     typedef test_impl< CharT > test;
303     test::formatting_params_restoring();
304 }
305 
306 namespace my_namespace {
307 
308 class A {};
309 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,A const &)310 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, A const&)
311 {
312     strm << "A";
313     return strm;
314 }
315 
316 class B {};
317 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,B &)318 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B&)
319 {
320     strm << "B";
321     return strm;
322 }
323 
324 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,B *)325 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*)
326 {
327     strm << "B*";
328     return strm;
329 }
330 
331 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,const B *)332 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*)
333 {
334     strm << "const B*";
335     return strm;
336 }
337 
338 class C {};
339 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,C const &)340 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&)
341 {
342     strm << "C";
343     return strm;
344 }
345 
346 enum E { eee };
347 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,E)348 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, E)
349 {
350     strm << "E";
351     return strm;
352 }
353 
354 } // namespace my_namespace
355 
356 // Test operator forwarding
BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding,CharT,char_types)357 BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types)
358 {
359     typedef CharT char_type;
360     typedef std::basic_string< char_type > string_type;
361     typedef std::basic_ostringstream< char_type > ostream_type;
362     typedef logging::basic_record_ostream< char_type > record_ostream_type;
363 
364     logging::record rec = make_record();
365     BOOST_REQUIRE(!!rec);
366     record_ostream_type strm_fmt(rec);
367 
368     const my_namespace::A a = my_namespace::A(); // const lvalue
369     my_namespace::B b; // lvalue
370     strm_fmt << a << b << my_namespace::C(); // rvalue
371     strm_fmt << my_namespace::eee;
372     strm_fmt << &b << (my_namespace::B const*)&b;
373     strm_fmt.flush();
374     string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
375 
376     ostream_type strm_correct;
377     strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b;
378 
379     BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
380 }
381 
382 namespace my_namespace2 {
383 
384 class A {};
385 template< typename CharT >
operator <<(logging::basic_record_ostream<CharT> & strm,A const &)386 inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, A const&)
387 {
388     strm << "A";
389     return strm;
390 }
391 
392 class B {};
393 template< typename CharT >
operator <<(logging::basic_record_ostream<CharT> & strm,B &)394 inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, B&)
395 {
396     strm << "B";
397     return strm;
398 }
399 
400 class C {};
401 template< typename CharT >
operator <<(logging::basic_record_ostream<CharT> & strm,C const &)402 inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, C const&)
403 {
404     strm << "C";
405     return strm;
406 }
407 
408 class D {};
409 template< typename CharT >
operator <<(logging::basic_record_ostream<CharT> & strm,D &&)410 inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm,
411 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
412     D&&
413 #else
414     D const&
415 #endif
416     )
417 {
418     strm << "D";
419     return strm;
420 }
421 
422 enum E { eee };
423 template< typename CharT >
operator <<(logging::basic_record_ostream<CharT> & strm,E)424 inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, E)
425 {
426     strm << "E";
427     return strm;
428 }
429 
430 } // namespace my_namespace2
431 
432 // Test operator overriding
BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding,CharT,char_types)433 BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding, CharT, char_types)
434 {
435     typedef CharT char_type;
436     typedef std::basic_string< char_type > string_type;
437     typedef std::basic_ostringstream< char_type > ostream_type;
438     typedef logging::basic_record_ostream< char_type > record_ostream_type;
439 
440     logging::record rec = make_record();
441     BOOST_REQUIRE(!!rec);
442     record_ostream_type strm_fmt(rec);
443 
444     const my_namespace2::A a = my_namespace2::A(); // const lvalue
445     my_namespace2::B b; // lvalue
446     strm_fmt << a << b << my_namespace2::C() << my_namespace2::D(); // rvalue
447     strm_fmt << my_namespace2::eee;
448     strm_fmt.flush();
449     string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
450 
451     ostream_type strm_correct;
452     strm_correct << "ABCDE";
453 
454     BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
455 }
456 
457 // Test that operator<< returns a record_ostream
BOOST_AUTO_TEST_CASE_TEMPLATE(operator_return_type,CharT,char_types)458 BOOST_AUTO_TEST_CASE_TEMPLATE(operator_return_type, CharT, char_types)
459 {
460     typedef CharT char_type;
461     typedef std::basic_string< char_type > string_type;
462     typedef std::basic_ostringstream< char_type > ostream_type;
463     typedef logging::basic_record_ostream< char_type > record_ostream_type;
464 
465     logging::record rec = make_record();
466     BOOST_REQUIRE(!!rec);
467     record_ostream_type strm_fmt(rec);
468 
469     // The test here verifies that the result of << "Hello" is a record_ostream, not std::ostream or logging::formatting_ostream.
470     // The subsequent << A() will only compile if the stream is record_ostream.
471     strm_fmt << "Hello " << my_namespace2::A();
472     strm_fmt.flush();
473     string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
474 
475     ostream_type strm_correct;
476     strm_correct << "Hello A";
477 
478     BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
479 }
480