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; |
131 | string_source(const std::string& src) |
132 | : pbase_type(src), base_type(member.data(), member.size()) |
133 | { } |
134 | |
135 | string_source(const string_source& src) |
136 | : pbase_type(src.member), base_type(member.data(), member.size()) |
137 | { } |
138 | }; |
139 | |
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> |
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 | |
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 | |
220 | void test_input_against_flags(int flags, const std::string& input, bool read) |
221 | { |
222 | if (read) { |
223 | io::copy( |
224 | src: io::compose( |
225 | filter: io::newline_checker(flags), |
226 | fod: string_source(input) |
227 | ), |
228 | snk: io::null_sink() |
229 | ); |
230 | } else { |
231 | io::copy( |
232 | src: string_source(input), |
233 | snk: io::compose(filter: io::newline_checker(flags), fod: io::null_sink()) |
234 | ); |
235 | } |
236 | } |
237 | |
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(t: io::newline_checker(io::newline::posix)); |
246 | in.push(t: 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(t: io::newline_checker(io::newline::dos)); |
259 | in.push(t: string_source(::dos)); |
260 | try { |
261 | io::copy(src&: in, snk: 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(t: io::newline_checker(io::newline::mac)); |
278 | in.push(t: 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(t: io::newline_checker(io::newline::posix)); |
291 | in.push(t: 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(t: io::newline_checker()); |
304 | in.push(t: 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 | |
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(t: io::newline_checker(io::newline::posix)); |
369 | out.push(t: 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(t: io::newline_checker(io::newline::dos)); |
382 | out.push(t: 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(t: io::newline_checker(io::newline::mac)); |
395 | out.push(t: 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(t: io::newline_checker(io::newline::posix)); |
408 | out.push(t: 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(t: io::newline_checker()); |
421 | out.push(t: 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 | |
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 | |