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