1 // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2 // (C) Copyright 2004-2007 Jonathan Turkanis
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5 
6 // See http://www.boost.org/libs/iostreams for documentation.
7 
8 #include <string>
9 #include <boost/iostreams/compose.hpp>
10 #include <boost/iostreams/copy.hpp>
11 #include <boost/iostreams/device/back_inserter.hpp>
12 #include <boost/iostreams/device/null.hpp>
13 #include <boost/iostreams/filter/newline.hpp>
14 #include <boost/iostreams/filter/test.hpp>
15 #include <boost/iostreams/filtering_stream.hpp>
16 #include <boost/test/test_tools.hpp>
17 #include <boost/test/unit_test.hpp>
18 #include <boost/utility/base_from_member.hpp>
19 
20 namespace io = boost::iostreams;
21 using boost::unit_test::test_suite;
22 
23 const std::string posix =
24     "When I was one-and-twenty\n"
25     "I heard a wise man say,\n"
26     "'Give crowns and pounds and guineas\n"
27     "But not your heart away;\n"
28     "\n"
29     "Give pearls away and rubies\n"
30     "But keep your fancy free.'\n"
31     "But I was one-and-twenty,\n"
32     "No use to talk to me.\n"
33     "\n"
34     "When I was one-and-twenty\n"
35     "I heard him say again,\n"
36     "'The heart out of the bosom\n"
37     "Was never given in vain;\n"
38     "\n"
39     "'Tis paid with sighs a plenty\n"
40     "And sold for endless rue.'\n"
41     "And I am two-and-twenty,\n"
42     "And oh, 'tis true, 'tis true.\n";
43 
44 const std::string dos =
45     "When I was one-and-twenty\r\n"
46     "I heard a wise man say,\r\n"
47     "'Give crowns and pounds and guineas\r\n"
48     "But not your heart away;\r\n"
49     "\r\n"
50     "Give pearls away and rubies\r\n"
51     "But keep your fancy free.'\r\n"
52     "But I was one-and-twenty,\r\n"
53     "No use to talk to me.\r\n"
54     "\r\n"
55     "When I was one-and-twenty\r\n"
56     "I heard him say again,\r\n"
57     "'The heart out of the bosom\r\n"
58     "Was never given in vain;\r\n"
59     "\r\n"
60     "'Tis paid with sighs a plenty\r\n"
61     "And sold for endless rue.'\r\n"
62     "And I am two-and-twenty,\r\n"
63     "And oh, 'tis true, 'tis true.\r\n";
64 
65 const std::string mac =
66     "When I was one-and-twenty\r"
67     "I heard a wise man say,\r"
68     "'Give crowns and pounds and guineas\r"
69     "But not your heart away;\r"
70     "\r"
71     "Give pearls away and rubies\r"
72     "But keep your fancy free.'\r"
73     "But I was one-and-twenty,\r"
74     "No use to talk to me.\r"
75     "\r"
76     "When I was one-and-twenty\r"
77     "I heard him say again,\r"
78     "'The heart out of the bosom\r"
79     "Was never given in vain;\r"
80     "\r"
81     "'Tis paid with sighs a plenty\r"
82     "And sold for endless rue.'\r"
83     "And I am two-and-twenty,\r"
84     "And oh, 'tis true, 'tis true.\r";
85 
86 const std::string no_final_newline =
87     "When I was one-and-twenty\n"
88     "I heard a wise man say,\n"
89     "'Give crowns and pounds and guineas\n"
90     "But not your heart away;\n"
91     "\n"
92     "Give pearls away and rubies\n"
93     "But keep your fancy free.'\n"
94     "But I was one-and-twenty,\n"
95     "No use to talk to me.\n"
96     "\n"
97     "When I was one-and-twenty\n"
98     "I heard him say again,\n"
99     "'The heart out of the bosom\n"
100     "Was never given in vain;\n"
101     "\n"
102     "'Tis paid with sighs a plenty\n"
103     "And sold for endless rue.'\n"
104     "And I am two-and-twenty,\n"
105     "And oh, 'tis true, 'tis true.";
106 
107 const std::string mixed =
108     "When I was one-and-twenty\n"
109     "I heard a wise man say,\r\n"
110     "'Give crowns and pounds and guineas\r"
111     "But not your heart away;\n"
112     "\r\n"
113     "Give pearls away and rubies\r"
114     "But keep your fancy free.'\n"
115     "But I was one-and-twenty,\r\n"
116     "No use to talk to me.\r"
117     "\r"
118     "When I was one-and-twenty\r\n"
119     "I heard him say again,\r"
120     "'The heart out of the bosom\n"
121     "Was never given in vain;\r\n"
122     "\r"
123     "'Tis paid with sighs a plenty\n"
124     "And sold for endless rue.'\r\n"
125     "And I am two-and-twenty,\r"
126     "And oh, 'tis true, 'tis true.\n";
127 
128 struct string_source : boost::base_from_member<std::string>, io::array_source {
129     typedef io::array_source                      base_type;
130     typedef boost::base_from_member<std::string>  pbase_type;
string_sourcestring_source131     string_source(const std::string& src)
132         : pbase_type(src), base_type(member.data(), member.size())
133         { }
134 
string_sourcestring_source135     string_source(const string_source& src)
136         : pbase_type(src.member), base_type(member.data(), member.size())
137         { }
138 };
139 
read_newline_filter()140 void read_newline_filter()
141 {
142     using namespace io;
143 
144         // Test converting to posix format.
145 
146     BOOST_CHECK(test_input_filter(newline_filter(newline::posix), posix, posix));
147     BOOST_CHECK(test_input_filter(newline_filter(newline::posix), dos, posix));
148     BOOST_CHECK(test_input_filter(newline_filter(newline::posix), mac, posix));
149     BOOST_CHECK(test_input_filter(newline_filter(newline::posix), mixed, posix));
150 
151         // Test converting to dos format.
152 
153     BOOST_CHECK(test_input_filter(newline_filter(newline::dos), posix, dos));
154     BOOST_CHECK(test_input_filter(newline_filter(newline::dos), dos, dos));
155     BOOST_CHECK(test_input_filter(newline_filter(newline::dos), mac, dos));
156     BOOST_CHECK(test_input_filter(newline_filter(newline::dos), mixed, dos));
157 
158         // Test converting to mac format.
159 
160     BOOST_CHECK(test_input_filter(newline_filter(newline::mac), posix, mac));
161     BOOST_CHECK(test_input_filter(newline_filter(newline::mac), dos, mac));
162     BOOST_CHECK(test_input_filter(newline_filter(newline::mac), mac, mac));
163     BOOST_CHECK(test_input_filter(newline_filter(newline::mac), mixed, mac));
164 }
165 
166 // Verify that a filter works as expected with both a non-blocking sink
167 // and a normal output stream.
168 //
169 // test_output_filter only tests for a non-blocking sink.
170 // TODO: Other tests should probably test with an output stream.
171 
172 template<typename Filter>
my_test_output_filter(Filter filter,const std::string & input,const std::string & output)173 bool my_test_output_filter(Filter filter,
174                          const std::string& input,
175                          const std::string& output)
176 {
177     const std::streamsize default_increment = 5;
178 
179     for ( int inc = default_increment;
180           inc < default_increment * 40;
181           inc += default_increment )
182     {
183         io::array_source src(input.data(), input.data() + input.size());
184 
185         std::ostringstream stream;
186         io::copy(src, compose(filter, stream));
187         if (stream.str() != output )
188             return false;
189 
190     }
191     return test_output_filter(filter, input, output);
192 }
193 
write_newline_filter()194 void write_newline_filter()
195 {
196     using namespace io;
197 
198         // Test converting to posix format.
199 
200     BOOST_CHECK(my_test_output_filter(newline_filter(newline::posix), posix, posix));
201     BOOST_CHECK(my_test_output_filter(newline_filter(newline::posix), dos, posix));
202     BOOST_CHECK(my_test_output_filter(newline_filter(newline::posix), mac, posix));
203     BOOST_CHECK(my_test_output_filter(newline_filter(newline::posix), mixed, posix));
204 
205         // Test converting to dos format.
206 
207     BOOST_CHECK(my_test_output_filter(newline_filter(newline::dos), posix, dos));
208     BOOST_CHECK(my_test_output_filter(newline_filter(newline::dos), dos, dos));
209     BOOST_CHECK(my_test_output_filter(newline_filter(newline::dos), mac, dos));
210     BOOST_CHECK(my_test_output_filter(newline_filter(newline::dos), mixed, dos));
211 
212         // Test converting to mac format.
213 
214     BOOST_CHECK(my_test_output_filter(newline_filter(newline::mac), posix, mac));
215     BOOST_CHECK(my_test_output_filter(newline_filter(newline::mac), dos, mac));
216     BOOST_CHECK(my_test_output_filter(newline_filter(newline::mac), mac, mac));
217     BOOST_CHECK(my_test_output_filter(newline_filter(newline::mac), mixed, mac));
218 }
219 
test_input_against_flags(int flags,const std::string & input,bool read)220 void test_input_against_flags(int flags, const std::string& input, bool read)
221 {
222     if (read) {
223         io::copy(
224             io::compose(
225                 io::newline_checker(flags),
226                 string_source(input)
227             ),
228             io::null_sink()
229         );
230     } else {
231         io::copy(
232             string_source(input),
233             io::compose(io::newline_checker(flags), io::null_sink())
234         );
235     }
236 }
237 
read_newline_checker()238 void read_newline_checker()
239 {
240     io::filtering_istream in;
241     io::newline_checker* checker = 0;
242 
243         // Verify properties of ::posix.
244 
245     in.push(io::newline_checker(io::newline::posix));
246     in.push(string_source(::posix));
247     BOOST_CHECK_NO_THROW(io::copy(in, io::null_sink()));
248     checker = BOOST_IOSTREAMS_COMPONENT(in, 0, io::newline_checker);
249     BOOST_CHECK(checker->is_posix());
250     BOOST_CHECK(!checker->is_dos());
251     BOOST_CHECK(!checker->is_mac());
252     BOOST_CHECK(!checker->is_mixed());
253     BOOST_CHECK(checker->has_final_newline());
254     in.pop(); // pop checker.
255 
256         // Verify properties of ::dos.
257 
258     in.push(io::newline_checker(io::newline::dos));
259     in.push(string_source(::dos));
260     try {
261         io::copy(in, io::null_sink());
262     } catch (io::newline_error&) {
263         BOOST_CHECK_MESSAGE(
264             false, "failed checking for dos line endings"
265         );
266     }
267     checker = BOOST_IOSTREAMS_COMPONENT(in, 0, io::newline_checker);
268     BOOST_CHECK(!checker->is_posix());
269     BOOST_CHECK(checker->is_dos());
270     BOOST_CHECK(!checker->is_mac());
271     BOOST_CHECK(!checker->is_mixed());
272     BOOST_CHECK(checker->has_final_newline());
273     in.pop(); // pop checker.
274 
275         // Verify properties of ::mac.
276 
277     in.push(io::newline_checker(io::newline::mac));
278     in.push(string_source(::mac));
279     BOOST_CHECK_NO_THROW(io::copy(in, io::null_sink()));
280     checker = BOOST_IOSTREAMS_COMPONENT(in, 0, io::newline_checker);
281     BOOST_CHECK(!checker->is_posix());
282     BOOST_CHECK(!checker->is_dos());
283     BOOST_CHECK(checker->is_mac());
284     BOOST_CHECK(!checker->is_mixed());
285     BOOST_CHECK(checker->has_final_newline());
286     in.pop(); // pop checker.
287 
288         // Verify properties of no_final_newline.
289 
290     in.push(io::newline_checker(io::newline::posix));
291     in.push(string_source(::no_final_newline));
292     BOOST_CHECK_NO_THROW(io::copy(in, io::null_sink()));
293     checker = BOOST_IOSTREAMS_COMPONENT(in, 0, io::newline_checker);
294     BOOST_CHECK(checker->is_posix());
295     BOOST_CHECK(!checker->is_dos());
296     BOOST_CHECK(!checker->is_mac());
297     BOOST_CHECK(!checker->is_mixed());
298     BOOST_CHECK(!checker->has_final_newline());
299     in.pop(); // pop checker.
300 
301         // Verify properties of mixed.
302 
303     in.push(io::newline_checker());
304     in.push(string_source(::mixed));
305     BOOST_CHECK_NO_THROW(io::copy(in, io::null_sink()));
306     checker = BOOST_IOSTREAMS_COMPONENT(in, 0, io::newline_checker);
307     BOOST_CHECK(!checker->is_posix());
308     BOOST_CHECK(!checker->is_dos());
309     BOOST_CHECK(!checker->is_mac());
310     BOOST_CHECK(checker->is_mixed_posix());
311     BOOST_CHECK(checker->is_mixed_dos());
312     BOOST_CHECK(checker->is_mixed_mac());
313     BOOST_CHECK(checker->is_mixed());
314     BOOST_CHECK(checker->has_final_newline());
315     in.pop(); // pop checker.
316 
317         // Verify exceptions when input does not satisfy target conditions.
318 
319     BOOST_CHECK_THROW(
320         test_input_against_flags(io::newline::dos, ::posix, true),
321         io::newline_error
322     );
323     BOOST_CHECK_THROW(
324         test_input_against_flags(io::newline::mac, ::posix, true),
325         io::newline_error
326     );
327     BOOST_CHECK_THROW(
328         test_input_against_flags(io::newline::posix, ::dos, true),
329         io::newline_error
330     );
331     BOOST_CHECK_THROW(
332         test_input_against_flags(io::newline::mac, ::dos, true),
333         io::newline_error
334     );
335     BOOST_CHECK_THROW(
336         test_input_against_flags(io::newline::posix, ::mac, true),
337         io::newline_error
338     );
339     BOOST_CHECK_THROW(
340         test_input_against_flags(io::newline::dos, ::mac, true),
341         io::newline_error
342     );
343     BOOST_CHECK_THROW(
344         test_input_against_flags(io::newline::final_newline, ::no_final_newline, true),
345         io::newline_error
346     );
347     BOOST_CHECK_THROW(
348         test_input_against_flags(io::newline::posix, ::mixed, true),
349         io::newline_error
350     );
351     BOOST_CHECK_THROW(
352         test_input_against_flags(io::newline::dos, ::mixed, true),
353         io::newline_error
354     );
355     BOOST_CHECK_THROW(
356         test_input_against_flags(io::newline::mac, ::mixed, true),
357         io::newline_error
358     );
359 }
360 
write_newline_checker()361 void write_newline_checker()
362 {
363     io::filtering_ostream out;
364     io::newline_checker* checker = 0;
365 
366         // Verify properties of ::posix.
367 
368     out.push(io::newline_checker(io::newline::posix));
369     out.push(io::null_sink());
370     BOOST_CHECK_NO_THROW(io::copy(string_source(::posix), out));
371     checker = BOOST_IOSTREAMS_COMPONENT(out, 0, io::newline_checker);
372     BOOST_CHECK(checker->is_posix());
373     BOOST_CHECK(!checker->is_dos());
374     BOOST_CHECK(!checker->is_mac());
375     BOOST_CHECK(!checker->is_mixed());
376     BOOST_CHECK(checker->has_final_newline());
377     out.pop(); // pop checker.
378 
379         // Verify properties of ::dos.
380 
381     out.push(io::newline_checker(io::newline::dos));
382     out.push(io::null_sink());
383     BOOST_CHECK_NO_THROW(io::copy(string_source(::dos), out));
384     checker = BOOST_IOSTREAMS_COMPONENT(out, 0, io::newline_checker);
385     BOOST_CHECK(!checker->is_posix());
386     BOOST_CHECK(checker->is_dos());
387     BOOST_CHECK(!checker->is_mac());
388     BOOST_CHECK(!checker->is_mixed());
389     BOOST_CHECK(checker->has_final_newline());
390     out.pop(); // pop checker.
391 
392         // Verify properties of ::mac.
393 
394     out.push(io::newline_checker(io::newline::mac));
395     out.push(io::null_sink());
396     BOOST_CHECK_NO_THROW(io::copy(string_source(::mac), out));
397     checker = BOOST_IOSTREAMS_COMPONENT(out, 0, io::newline_checker);
398     BOOST_CHECK(!checker->is_posix());
399     BOOST_CHECK(!checker->is_dos());
400     BOOST_CHECK(checker->is_mac());
401     BOOST_CHECK(!checker->is_mixed());
402     BOOST_CHECK(checker->has_final_newline());
403     out.pop(); // pop checker.
404 
405         // Verify properties of no_final_newline.
406 
407     out.push(io::newline_checker(io::newline::posix));
408     out.push(io::null_sink());
409     BOOST_CHECK_NO_THROW(io::copy(string_source(::no_final_newline), out));
410     checker = BOOST_IOSTREAMS_COMPONENT(out, 0, io::newline_checker);
411     BOOST_CHECK(checker->is_posix());
412     BOOST_CHECK(!checker->is_dos());
413     BOOST_CHECK(!checker->is_mac());
414     BOOST_CHECK(!checker->is_mixed());
415     BOOST_CHECK(!checker->has_final_newline());
416     out.pop(); // pop checker.
417 
418         // Verify properties of mixed.
419 
420     out.push(io::newline_checker());
421     out.push(io::null_sink());
422     BOOST_CHECK_NO_THROW(io::copy(string_source(::mixed), out));
423     checker = BOOST_IOSTREAMS_COMPONENT(out, 0, io::newline_checker);
424     BOOST_CHECK(!checker->is_posix());
425     BOOST_CHECK(!checker->is_dos());
426     BOOST_CHECK(!checker->is_mac());
427     BOOST_CHECK(checker->is_mixed_posix());
428     BOOST_CHECK(checker->is_mixed_dos());
429     BOOST_CHECK(checker->is_mixed_mac());
430     BOOST_CHECK(checker->is_mixed());
431     BOOST_CHECK(checker->has_final_newline());
432     out.pop(); // pop checker.
433 
434         // Verify exceptions when input does not satisfy target conditions.
435 
436     BOOST_CHECK_THROW(
437         test_input_against_flags(io::newline::dos, ::posix, false),
438         io::newline_error
439     );
440     BOOST_CHECK_THROW(
441         test_input_against_flags(io::newline::mac, ::posix, false),
442         io::newline_error
443     );
444     BOOST_CHECK_THROW(
445         test_input_against_flags(io::newline::posix, ::dos, false),
446         io::newline_error
447     );
448     BOOST_CHECK_THROW(
449         test_input_against_flags(io::newline::mac, ::dos, false),
450         io::newline_error
451     );
452     BOOST_CHECK_THROW(
453         test_input_against_flags(io::newline::posix, ::mac, false),
454         io::newline_error
455     );
456     BOOST_CHECK_THROW(
457         test_input_against_flags(io::newline::dos, ::mac, false),
458         io::newline_error
459     );
460     BOOST_CHECK_THROW(
461         test_input_against_flags(io::newline::final_newline, ::no_final_newline, false),
462         io::newline_error
463     );
464     BOOST_CHECK_THROW(
465         test_input_against_flags(io::newline::posix, ::mixed, false),
466         io::newline_error
467     );
468     BOOST_CHECK_THROW(
469         test_input_against_flags(io::newline::dos, ::mixed, false),
470         io::newline_error
471     );
472     BOOST_CHECK_THROW(
473         test_input_against_flags(io::newline::mac, ::mixed, false),
474         io::newline_error
475     );
476 }
477 
init_unit_test_suite(int,char * [])478 test_suite* init_unit_test_suite(int, char* [])
479 {
480     test_suite* test = BOOST_TEST_SUITE("newline_filter test");
481     test->add(BOOST_TEST_CASE(&read_newline_filter));
482     test->add(BOOST_TEST_CASE(&write_newline_filter));
483     test->add(BOOST_TEST_CASE(&read_newline_checker));
484     test->add(BOOST_TEST_CASE(&write_newline_checker));
485     return test;
486 }
487