1 /*
2  *             Copyright Andrey Semashev 2016.
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   form_max_size_decor.cpp
9  * \author Andrey Semashev
10  * \date   09.08.2016
11  *
12  * \brief  This header contains tests for the \c max_size_decor formatter.
13  */
14 
15 #define BOOST_TEST_MODULE form_max_size_decor
16 
17 #include <string>
18 #include <locale>
19 #include <boost/test/unit_test.hpp>
20 #include <boost/log/attributes/constant.hpp>
21 #include <boost/log/attributes/attribute_set.hpp>
22 #include <boost/log/utility/type_dispatch/standard_types.hpp>
23 #include <boost/log/utility/formatting_ostream.hpp>
24 #include <boost/log/expressions/attr.hpp>
25 #include <boost/log/expressions/formatter.hpp>
26 #include <boost/log/expressions/formatters/max_size_decorator.hpp>
27 #include <boost/log/expressions/formatters/stream.hpp>
28 #include <boost/log/core/record.hpp>
29 #include <boost/phoenix/operator.hpp>
30 #include "char_definitions.hpp"
31 #include "make_record.hpp"
32 
33 #define BOOST_UTF8_DECL
34 #define BOOST_UTF8_BEGIN_NAMESPACE namespace {
35 #define BOOST_UTF8_END_NAMESPACE }
36 
37 #include <boost/detail/utf8_codecvt_facet.hpp>
38 #include <boost/detail/utf8_codecvt_facet.ipp>
39 
40 namespace logging = boost::log;
41 namespace attrs = logging::attributes;
42 namespace expr = logging::expressions;
43 
44 namespace {
45 
46 template< typename >
47 struct test_strings;
48 
49 #ifdef BOOST_LOG_USE_CHAR
50 template< >
51 struct test_strings< char > : public test_data< char >
52 {
printable_chars__anonecc869600111::test_strings53     static const char* printable_chars() { return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; }
overflow_marker__anonecc869600111::test_strings54     static const char* overflow_marker() { return ">>>"; }
55 };
56 #endif
57 
58 #ifdef BOOST_LOG_USE_WCHAR_T
59 template< >
60 struct test_strings< wchar_t > : public test_data< wchar_t >
61 {
printable_chars__anonecc869600111::test_strings62     static const wchar_t* printable_chars() { return L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; }
overflow_marker__anonecc869600111::test_strings63     static const wchar_t* overflow_marker() { return L">>>"; }
64 };
65 #endif
66 
67 } // namespace
68 
BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting,CharT,char_types)69 BOOST_AUTO_TEST_CASE_TEMPLATE(decorator_formatting, CharT, char_types)
70 {
71     typedef logging::record_view record_view;
72     typedef logging::attribute_set attr_set;
73     typedef std::basic_string< CharT > string;
74     typedef logging::basic_formatting_ostream< CharT > osstream;
75     typedef logging::basic_formatter< CharT > formatter;
76     typedef test_strings< CharT > data;
77 
78     attrs::constant< string > attr1(data::printable_chars());
79 
80     attr_set set1;
81     set1[data::attr1()] = attr1;
82 
83     record_view rec = make_record_view(set1);
84 
85     // Test output truncation
86     {
87         string str1, str2;
88         osstream strm1(str1), strm2(str2);
89         formatter f = expr::stream << expr::max_size_decor< CharT >(10)[ expr::stream << expr::attr< string >(data::attr1()) << data::some_test_string() << 1234 << data::abc() ];
90         f(rec, strm1);
91         strm2 << string(data::printable_chars(), 10);
92         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
93     }
94 
95     // Test output truncation with a marker
96     {
97         string str1, str2;
98         osstream strm1(str1), strm2(str2);
99         formatter f = expr::stream << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) << data::some_test_string() << 1234 << data::abc() ];
100         f(rec, strm1);
101         strm2 << string(data::printable_chars(), 7) << data::overflow_marker();
102         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
103     }
104 
105     // Test nested decorators, when the outer decorator enforces the limit that includes the inner decorator
106     {
107         string str1, str2;
108         osstream strm1(str1), strm2(str2);
109         formatter f = expr::stream << expr::max_size_decor(35, data::overflow_marker())[
110             expr::stream << data::abcdefg0123456789() << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ] << data::abcdefg0123456789()
111         ];
112         f(rec, strm1);
113         strm2 << data::abcdefg0123456789() << string(data::printable_chars(), 7) << data::overflow_marker() << string(data::abcdefg0123456789(), 5) << data::overflow_marker();
114         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
115     }
116 
117     // Test nested decorators, when the outer decorator enforces the limit that also limits the inner decorator
118     {
119         string str1, str2;
120         osstream strm1(str1), strm2(str2);
121         formatter f = expr::stream << expr::max_size_decor(25, data::overflow_marker())[
122             expr::stream << data::abcdefg0123456789() << expr::max_size_decor(10, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ] << data::abcdefg0123456789()
123         ];
124         f(rec, strm1);
125         strm2 << data::abcdefg0123456789() << string(data::printable_chars(), 5) << data::overflow_marker();
126         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
127     }
128 }
129 
130 namespace {
131 
132 const char narrow_chars[] = "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, \xd0\xbc\xd0\xb8\xd1\x80!";
133 
134 } // namespace
135 
BOOST_AUTO_TEST_CASE(character_boundary_maintenance)136 BOOST_AUTO_TEST_CASE(character_boundary_maintenance)
137 {
138     typedef logging::record_view record_view;
139     typedef logging::attribute_set attr_set;
140     typedef std::basic_string< char > string;
141     typedef logging::basic_formatting_ostream< char > osstream;
142     typedef logging::basic_formatter< char > formatter;
143     typedef test_strings< char > data;
144 
145     std::locale loc(std::locale::classic(), new utf8_codecvt_facet());
146 
147     attrs::constant< string > attr1(narrow_chars);
148 
149     attr_set set1;
150     set1[data::attr1()] = attr1;
151 
152     record_view rec = make_record_view(set1);
153 
154     // Test that output truncation does not happen in the middle of a multibyte character
155     {
156         string str1, str2;
157         osstream strm1(str1), strm2(str2);
158         strm1.imbue(loc);
159         strm2.imbue(loc);
160         formatter f = expr::stream << expr::max_size_decor< char >(7)[ expr::stream << expr::attr< string >(data::attr1()) ];
161         f(rec, strm1);
162         strm2 << string(narrow_chars, 6);
163         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
164     }
165 
166     // Test output truncation with a marker, when the marker length would have caused truncation in the middle of a multibyte character
167     {
168         string str1, str2;
169         osstream strm1(str1), strm2(str2);
170         strm1.imbue(loc);
171         strm2.imbue(loc);
172         formatter f = expr::stream << expr::max_size_decor(6, data::overflow_marker())[ expr::stream << expr::attr< string >(data::attr1()) ];
173         f(rec, strm1);
174         strm2 << string(narrow_chars, 2) << data::overflow_marker();
175         BOOST_CHECK(equal_strings(strm1.str(), strm2.str()));
176     }
177 }
178