src_record_ostream.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. #define BOOST_TEST_MODULE src_record_ostream
  15. #include <locale>
  16. #include <string>
  17. #include <iomanip>
  18. #include <sstream>
  19. #include <iostream>
  20. #include <algorithm>
  21. #include <boost/config.hpp>
  22. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  23. #include <string_view>
  24. #endif
  25. #include <boost/test/unit_test.hpp>
  26. #include <boost/utility/string_view.hpp>
  27. #include <boost/log/core/record.hpp>
  28. #include <boost/log/sources/record_ostream.hpp>
  29. #include <boost/log/expressions/message.hpp>
  30. #include <boost/log/attributes/value_extraction.hpp>
  31. #include "char_definitions.hpp"
  32. #include "make_record.hpp"
  33. namespace logging = boost::log;
  34. namespace expr = boost::log::expressions;
  35. namespace {
  36. struct unreferencable_data
  37. {
  38. unsigned int m : 2;
  39. unsigned int n : 6;
  40. enum my_enum
  41. {
  42. one = 1,
  43. two = 2
  44. };
  45. // The following static constants don't have definitions, so they can only be used in constant expressions.
  46. // Trying to bind a reference to these members will result in linking errors.
  47. static const int x = 7;
  48. static const my_enum y = one;
  49. unreferencable_data()
  50. {
  51. m = 1;
  52. n = 5;
  53. }
  54. };
  55. template< typename CharT >
  56. struct test_impl
  57. {
  58. typedef CharT char_type;
  59. typedef test_data< char_type > strings;
  60. typedef std::basic_string< char_type > string_type;
  61. typedef std::basic_ostringstream< char_type > ostream_type;
  62. typedef logging::basic_record_ostream< char_type > record_ostream_type;
  63. template< typename StringT >
  64. static void width_formatting()
  65. {
  66. // Check that widening works
  67. {
  68. logging::record rec = make_record();
  69. BOOST_REQUIRE(!!rec);
  70. record_ostream_type strm_fmt(rec);
  71. strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
  72. strm_fmt.flush();
  73. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  74. ostream_type strm_correct;
  75. strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
  76. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  77. }
  78. // Check that the string is not truncated
  79. {
  80. logging::record rec = make_record();
  81. BOOST_REQUIRE(!!rec);
  82. record_ostream_type strm_fmt(rec);
  83. strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
  84. strm_fmt.flush();
  85. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  86. ostream_type strm_correct;
  87. strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
  88. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  89. }
  90. }
  91. template< typename StringT >
  92. static void fill_formatting()
  93. {
  94. logging::record rec = make_record();
  95. BOOST_REQUIRE(!!rec);
  96. record_ostream_type strm_fmt(rec);
  97. strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
  98. strm_fmt.flush();
  99. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  100. ostream_type strm_correct;
  101. strm_correct << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
  102. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  103. }
  104. template< typename StringT >
  105. static void alignment()
  106. {
  107. // Left alignment
  108. {
  109. logging::record rec = make_record();
  110. BOOST_REQUIRE(!!rec);
  111. record_ostream_type strm_fmt(rec);
  112. strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
  113. strm_fmt.flush();
  114. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  115. ostream_type strm_correct;
  116. strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
  117. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  118. }
  119. // Right alignment
  120. {
  121. logging::record rec = make_record();
  122. BOOST_REQUIRE(!!rec);
  123. record_ostream_type strm_fmt(rec);
  124. strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
  125. strm_fmt.flush();
  126. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  127. ostream_type strm_correct;
  128. strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
  129. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  130. }
  131. }
  132. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  133. template< typename StringT >
  134. static void rvalue_stream()
  135. {
  136. logging::record rec = make_record();
  137. BOOST_REQUIRE(!!rec);
  138. record_ostream_type(rec) << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC() << std::flush;
  139. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  140. ostream_type strm_correct;
  141. strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
  142. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  143. }
  144. #endif
  145. static void output_unreferencable_data()
  146. {
  147. unreferencable_data data;
  148. {
  149. logging::record rec = make_record();
  150. BOOST_REQUIRE(!!rec);
  151. record_ostream_type strm_fmt(rec);
  152. strm_fmt << data.m << static_cast< char_type >(' ') << data.n << static_cast< char_type >(' ') << unreferencable_data::x << static_cast< char_type >(' ') << unreferencable_data::y;
  153. strm_fmt.flush();
  154. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  155. ostream_type strm_correct;
  156. 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);
  157. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  158. }
  159. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  160. {
  161. logging::record rec = make_record();
  162. BOOST_REQUIRE(!!rec);
  163. 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;
  164. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  165. ostream_type strm_correct;
  166. 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);
  167. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  168. }
  169. #endif
  170. }
  171. static void formatting_params_restoring()
  172. {
  173. record_ostream_type strm_fmt;
  174. {
  175. logging::record rec = make_record();
  176. BOOST_REQUIRE(!!rec);
  177. strm_fmt.attach_record(rec);
  178. strm_fmt << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15;
  179. strm_fmt.flush();
  180. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  181. strm_fmt.detach_from_record();
  182. ostream_type strm_correct;
  183. strm_correct << std::setw(8) << std::setfill(static_cast< char_type >('x')) << std::hex << 15;
  184. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  185. }
  186. // Check that the formatting flags are reset for the next record
  187. {
  188. logging::record rec = make_record();
  189. BOOST_REQUIRE(!!rec);
  190. strm_fmt.attach_record(rec);
  191. strm_fmt << 15;
  192. strm_fmt.flush();
  193. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  194. strm_fmt.detach_from_record();
  195. ostream_type strm_correct;
  196. strm_correct << 15;
  197. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  198. }
  199. }
  200. };
  201. } // namespace
  202. // Test support for width formatting
  203. BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types)
  204. {
  205. typedef test_impl< CharT > test;
  206. test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >();
  207. test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >();
  208. test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >();
  209. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  210. test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >();
  211. #endif
  212. }
  213. // Test support for filler character setup
  214. BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types)
  215. {
  216. typedef test_impl< CharT > test;
  217. test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >();
  218. test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >();
  219. test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >();
  220. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  221. test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >();
  222. #endif
  223. }
  224. // Test support for text alignment
  225. BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types)
  226. {
  227. typedef test_impl< CharT > test;
  228. test::BOOST_NESTED_TEMPLATE alignment< const CharT* >();
  229. test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >();
  230. test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >();
  231. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  232. test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >();
  233. #endif
  234. }
  235. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  236. // Test support for rvalue stream objects
  237. BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types)
  238. {
  239. typedef test_impl< CharT > test;
  240. test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >();
  241. test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >();
  242. test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >();
  243. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  244. test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >();
  245. #endif
  246. }
  247. #endif
  248. // Test output of data to which a reference cannot be bound
  249. BOOST_AUTO_TEST_CASE_TEMPLATE(output_unreferencable_data, CharT, char_types)
  250. {
  251. typedef test_impl< CharT > test;
  252. test::output_unreferencable_data();
  253. }
  254. // Test that formatting settings are reset for new log records
  255. BOOST_AUTO_TEST_CASE_TEMPLATE(formatting_params_restoring, CharT, char_types)
  256. {
  257. typedef test_impl< CharT > test;
  258. test::formatting_params_restoring();
  259. }
  260. namespace my_namespace {
  261. class A {};
  262. template< typename CharT, typename TraitsT >
  263. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, A const&)
  264. {
  265. strm << "A";
  266. return strm;
  267. }
  268. class B {};
  269. template< typename CharT, typename TraitsT >
  270. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B&)
  271. {
  272. strm << "B";
  273. return strm;
  274. }
  275. template< typename CharT, typename TraitsT >
  276. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*)
  277. {
  278. strm << "B*";
  279. return strm;
  280. }
  281. template< typename CharT, typename TraitsT >
  282. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*)
  283. {
  284. strm << "const B*";
  285. return strm;
  286. }
  287. class C {};
  288. template< typename CharT, typename TraitsT >
  289. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&)
  290. {
  291. strm << "C";
  292. return strm;
  293. }
  294. enum E { eee };
  295. template< typename CharT, typename TraitsT >
  296. inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, E)
  297. {
  298. strm << "E";
  299. return strm;
  300. }
  301. } // namespace my_namespace
  302. // Test operator forwarding
  303. BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types)
  304. {
  305. typedef CharT char_type;
  306. typedef std::basic_string< char_type > string_type;
  307. typedef std::basic_ostringstream< char_type > ostream_type;
  308. typedef logging::basic_record_ostream< char_type > record_ostream_type;
  309. logging::record rec = make_record();
  310. BOOST_REQUIRE(!!rec);
  311. record_ostream_type strm_fmt(rec);
  312. const my_namespace::A a = my_namespace::A(); // const lvalue
  313. my_namespace::B b; // lvalue
  314. strm_fmt << a << b << my_namespace::C(); // rvalue
  315. strm_fmt << my_namespace::eee;
  316. strm_fmt << &b << (my_namespace::B const*)&b;
  317. strm_fmt.flush();
  318. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  319. ostream_type strm_correct;
  320. strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b;
  321. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  322. }
  323. namespace my_namespace2 {
  324. class A {};
  325. template< typename CharT >
  326. inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, A const&)
  327. {
  328. strm << "A";
  329. return strm;
  330. }
  331. class B {};
  332. template< typename CharT >
  333. inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, B&)
  334. {
  335. strm << "B";
  336. return strm;
  337. }
  338. class C {};
  339. template< typename CharT >
  340. inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, C const&)
  341. {
  342. strm << "C";
  343. return strm;
  344. }
  345. class D {};
  346. template< typename CharT >
  347. inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm,
  348. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  349. D&&
  350. #else
  351. D const&
  352. #endif
  353. )
  354. {
  355. strm << "D";
  356. return strm;
  357. }
  358. enum E { eee };
  359. template< typename CharT >
  360. inline logging::basic_record_ostream< CharT >& operator<< (logging::basic_record_ostream< CharT >& strm, E)
  361. {
  362. strm << "E";
  363. return strm;
  364. }
  365. } // namespace my_namespace2
  366. // Test operator overriding
  367. BOOST_AUTO_TEST_CASE_TEMPLATE(operator_overriding, CharT, char_types)
  368. {
  369. typedef CharT char_type;
  370. typedef std::basic_string< char_type > string_type;
  371. typedef std::basic_ostringstream< char_type > ostream_type;
  372. typedef logging::basic_record_ostream< char_type > record_ostream_type;
  373. logging::record rec = make_record();
  374. BOOST_REQUIRE(!!rec);
  375. record_ostream_type strm_fmt(rec);
  376. const my_namespace2::A a = my_namespace2::A(); // const lvalue
  377. my_namespace2::B b; // lvalue
  378. strm_fmt << a << b << my_namespace2::C() << my_namespace2::D(); // rvalue
  379. strm_fmt << my_namespace2::eee;
  380. strm_fmt.flush();
  381. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  382. ostream_type strm_correct;
  383. strm_correct << "ABCDE";
  384. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  385. }
  386. // Test that operator<< returns a record_ostream
  387. BOOST_AUTO_TEST_CASE_TEMPLATE(operator_return_type, CharT, char_types)
  388. {
  389. typedef CharT char_type;
  390. typedef std::basic_string< char_type > string_type;
  391. typedef std::basic_ostringstream< char_type > ostream_type;
  392. typedef logging::basic_record_ostream< char_type > record_ostream_type;
  393. logging::record rec = make_record();
  394. BOOST_REQUIRE(!!rec);
  395. record_ostream_type strm_fmt(rec);
  396. // The test here verifies that the result of << "Hello" is a record_ostream, not std::ostream or logging::formatting_ostream.
  397. // The subsequent << A() will only compile if the stream is record_ostream.
  398. strm_fmt << "Hello " << my_namespace2::A();
  399. strm_fmt.flush();
  400. string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec);
  401. ostream_type strm_correct;
  402. strm_correct << "Hello A";
  403. BOOST_CHECK(equal_strings(rec_message, strm_correct.str()));
  404. }