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 <fstream> |
9 | #include <boost/iostreams/compose.hpp> |
10 | #include <boost/iostreams/device/file.hpp> |
11 | #include <boost/iostreams/filtering_stream.hpp> |
12 | #include <boost/iostreams/tee.hpp> |
13 | #include <boost/test/test_tools.hpp> |
14 | #include <boost/test/unit_test.hpp> |
15 | #include "detail/closable.hpp" |
16 | #include "detail/operation_sequence.hpp" |
17 | #include "detail/temp_file.hpp" |
18 | #include "detail/verification.hpp" |
19 | |
20 | using namespace std; |
21 | using namespace boost; |
22 | using namespace boost::iostreams; |
23 | using namespace boost::iostreams::test; |
24 | using boost::unit_test::test_suite; |
25 | |
26 | void read_write_test() |
27 | { |
28 | { |
29 | test_file src1, src2; |
30 | temp_file dest; |
31 | filtering_istream first, second; |
32 | first.push(t: tee(snk: file_sink(dest.name(), out_mode))); |
33 | first.push(t: file_source(src1.name(), in_mode)); |
34 | second.push(t: file_source(src2.name(), in_mode)); |
35 | compare_streams_in_chars(first, second); // ignore return value |
36 | first.reset(); |
37 | BOOST_CHECK_MESSAGE( |
38 | compare_files(dest.name(), src1.name()), |
39 | "failed reading from a tee_filter in chars" |
40 | ); |
41 | } |
42 | |
43 | { |
44 | test_file src1, src2; |
45 | temp_file dest; |
46 | filtering_istream first, second; |
47 | first.push(t: tee(snk: file_sink(dest.name(), out_mode))); |
48 | first.push(t: file_source(src1.name(), in_mode)); |
49 | second.push(t: file_source(src2.name(), in_mode)); |
50 | compare_streams_in_chunks(first, second); // ignore return value |
51 | first.reset(); |
52 | BOOST_CHECK_MESSAGE( |
53 | compare_files(dest.name(), src1.name()), |
54 | "failed reading from a tee_filter in chunks" |
55 | ); |
56 | } |
57 | |
58 | { |
59 | temp_file dest1; |
60 | temp_file dest2; |
61 | filtering_ostream out; |
62 | out.push(t: tee(snk: file_sink(dest1.name(), out_mode))); |
63 | out.push(t: file_sink(dest2.name(), out_mode)); |
64 | write_data_in_chars(os&: out); |
65 | out.reset(); |
66 | BOOST_CHECK_MESSAGE( |
67 | compare_files(dest1.name(), dest2.name()), |
68 | "failed writing to a tee_filter in chars" |
69 | ); |
70 | } |
71 | |
72 | { |
73 | temp_file dest1; |
74 | temp_file dest2; |
75 | filtering_ostream out; |
76 | out.push(t: tee(snk: file_sink(dest1.name(), out_mode))); |
77 | out.push(t: file_sink(dest2.name(), out_mode)); |
78 | write_data_in_chunks(os&: out); |
79 | out.reset(); |
80 | BOOST_CHECK_MESSAGE( |
81 | compare_files(dest1.name(), dest2.name()), |
82 | "failed writing to a tee_filter in chunks" |
83 | ); |
84 | } |
85 | |
86 | { |
87 | test_file src1, src2; |
88 | temp_file dest; |
89 | filtering_istream first, second; |
90 | first.push( t: tee( dev: file_source(src1.name(), in_mode), |
91 | sink: file_sink(dest.name(), out_mode) ) ); |
92 | second.push(t: file_source(src2.name(), in_mode)); |
93 | compare_streams_in_chars(first, second); // ignore return value |
94 | first.reset(); |
95 | BOOST_CHECK_MESSAGE( |
96 | compare_files(dest.name(), src1.name()), |
97 | "failed reading from a tee_device in chars" |
98 | ); |
99 | } |
100 | |
101 | { |
102 | test_file src1, src2; |
103 | temp_file dest; |
104 | filtering_istream first, second; |
105 | first.push( t: tee( dev: file_source(src1.name(), in_mode), |
106 | sink: file_sink(dest.name(), out_mode) ) ); |
107 | second.push(t: file_source(src2.name(), in_mode)); |
108 | compare_streams_in_chunks(first, second); // ignore return value |
109 | first.reset(); |
110 | BOOST_CHECK_MESSAGE( |
111 | compare_files(dest.name(), src1.name()), |
112 | "failed reading from a tee_device in chunks" |
113 | ); |
114 | } |
115 | |
116 | { |
117 | temp_file dest1; |
118 | temp_file dest2; |
119 | filtering_ostream out; |
120 | out.push( t: tee( dev: file_sink(dest1.name(), out_mode), |
121 | sink: file_sink(dest2.name(), out_mode) ) ); |
122 | write_data_in_chars(os&: out); |
123 | out.reset(); |
124 | BOOST_CHECK_MESSAGE( |
125 | compare_files(dest1.name(), dest2.name()), |
126 | "failed writing to a tee_device in chars" |
127 | ); |
128 | } |
129 | |
130 | { |
131 | temp_file dest1; |
132 | temp_file dest2; |
133 | filtering_ostream out; |
134 | out.push( t: tee( dev: file_sink(dest1.name(), out_mode), |
135 | sink: file_sink(dest2.name(), out_mode) ) ); |
136 | write_data_in_chunks(os&: out); |
137 | out.reset(); |
138 | BOOST_CHECK_MESSAGE( |
139 | compare_files(dest1.name(), dest2.name()), |
140 | "failed writing to a tee_device in chunks" |
141 | ); |
142 | } |
143 | } |
144 | |
145 | void close_test() |
146 | { |
147 | // Note: The implementation of tee_device closes the first |
148 | // sink before the second |
149 | |
150 | // Tee two sinks (Borland <= 5.8.2 needs a little help compiling this case, |
151 | // but it executes the closing algorithm correctly) |
152 | { |
153 | operation_sequence seq; |
154 | chain<output> ch; |
155 | ch.push( |
156 | t: boost::iostreams::tee( |
157 | dev: closable_device<output>(seq.new_operation(id: 1)), |
158 | sink: closable_device< |
159 | #if BOOST_WORKAROUND(BOOST_BORLANDC, BOOST_TESTED_AT(0x582)) |
160 | borland_output |
161 | #else |
162 | output |
163 | #endif |
164 | >(seq.new_operation(id: 2)) |
165 | ) |
166 | ); |
167 | BOOST_CHECK_NO_THROW(ch.reset()); |
168 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
169 | } |
170 | |
171 | // Tee two bidirectional devices |
172 | { |
173 | operation_sequence seq; |
174 | chain<output> ch; |
175 | ch.push( |
176 | t: boost::iostreams::tee( |
177 | dev: closable_device<bidirectional>( |
178 | seq.new_operation(id: 1), |
179 | seq.new_operation(id: 2) |
180 | ), |
181 | sink: closable_device<bidirectional>( |
182 | seq.new_operation(id: 3), |
183 | seq.new_operation(id: 4) |
184 | ) |
185 | ) |
186 | ); |
187 | BOOST_CHECK_NO_THROW(ch.reset()); |
188 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
189 | } |
190 | |
191 | // Tee two seekable devices |
192 | { |
193 | operation_sequence seq; |
194 | chain<output> ch; |
195 | ch.push( |
196 | t: boost::iostreams::tee( |
197 | dev: closable_device<seekable>(seq.new_operation(id: 1)), |
198 | sink: closable_device<seekable>(seq.new_operation(id: 2)) |
199 | ) |
200 | ); |
201 | BOOST_CHECK_NO_THROW(ch.reset()); |
202 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
203 | } |
204 | |
205 | // Tee a sink |
206 | { |
207 | operation_sequence seq; |
208 | chain<output> ch; |
209 | ch.push(t: boost::iostreams::tee(snk: closable_device<output>(seq.new_operation(id: 1)))); |
210 | ch.push(t: closable_device<output>(seq.new_operation(id: 2))); |
211 | BOOST_CHECK_NO_THROW(ch.reset()); |
212 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
213 | } |
214 | |
215 | // Tee a bidirectional device |
216 | { |
217 | operation_sequence seq; |
218 | chain<output> ch; |
219 | ch.push( |
220 | t: boost::iostreams::tee( |
221 | snk: closable_device<bidirectional>( |
222 | seq.new_operation(id: 1), |
223 | seq.new_operation(id: 2) |
224 | ) |
225 | ) |
226 | ); |
227 | ch.push(t: closable_device<output>(seq.new_operation(id: 3))); |
228 | BOOST_CHECK_NO_THROW(ch.reset()); |
229 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
230 | } |
231 | |
232 | // Tee a seekable device |
233 | { |
234 | operation_sequence seq; |
235 | chain<output> ch; |
236 | ch.push(t: boost::iostreams::tee(snk: closable_device<seekable>(seq.new_operation(id: 1)))); |
237 | ch.push(t: closable_device<seekable>(seq.new_operation(id: 2))); |
238 | BOOST_CHECK_NO_THROW(ch.reset()); |
239 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
240 | } |
241 | } |
242 | |
243 | void test_std_io() |
244 | { |
245 | { |
246 | temp_file dest1; |
247 | temp_file dest2; |
248 | filtering_ostream out; |
249 | std::ofstream stream1(dest1.name().c_str(), out_mode); |
250 | out.push(t: tee(snk&: stream1)); |
251 | out.push(t: file_sink(dest2.name(), out_mode)); |
252 | write_data_in_chunks(os&: out); |
253 | BOOST_CHECK_MESSAGE( |
254 | compare_files(dest1.name(), dest2.name()), |
255 | "failed writing to a tee_device in chunks" |
256 | ); |
257 | } |
258 | { |
259 | temp_file dest1; |
260 | temp_file dest2; |
261 | filtering_ostream out; |
262 | std::ofstream stream1(dest1.name().c_str(), out_mode); |
263 | out.push( t: tee ( dev&: stream1, |
264 | sink: file_sink(dest2.name(), out_mode) ) ); |
265 | write_data_in_chunks(os&: out); |
266 | BOOST_CHECK_MESSAGE( |
267 | compare_files(dest1.name(), dest2.name()), |
268 | "failed writing to a tee_device in chunks" |
269 | ); |
270 | } |
271 | { |
272 | temp_file dest1; |
273 | temp_file dest2; |
274 | filtering_ostream out; |
275 | std::ofstream stream2(dest2.name().c_str(), out_mode); |
276 | out.push( t: tee ( dev: file_sink(dest1.name(), out_mode), |
277 | sink&: stream2 ) ); |
278 | write_data_in_chunks(os&: out); |
279 | BOOST_CHECK_MESSAGE( |
280 | compare_files(dest1.name(), dest2.name()), |
281 | "failed writing to a tee_device in chunks" |
282 | ); |
283 | } |
284 | { |
285 | temp_file dest1; |
286 | temp_file dest2; |
287 | filtering_ostream out; |
288 | std::ofstream stream1(dest1.name().c_str(), out_mode); |
289 | std::ofstream stream2(dest2.name().c_str(), out_mode); |
290 | out.push(t: tee(dev&: stream1, sink&: stream2)); |
291 | write_data_in_chunks(os&: out); |
292 | BOOST_CHECK_MESSAGE( |
293 | compare_files(dest1.name(), dest2.name()), |
294 | "failed writing to a tee_device in chunks" |
295 | ); |
296 | } |
297 | } |
298 | |
299 | void tee_composite_test() |
300 | { |
301 | // This test is probably redundant, given the above test and the tests in |
302 | // compose_test.cpp, but it verifies that ticket #1002 is fixed |
303 | |
304 | // Tee a composite sink with a sink |
305 | { |
306 | operation_sequence seq; |
307 | chain<output> ch; |
308 | ch.push( |
309 | t: boost::iostreams::tee( |
310 | dev: boost::iostreams::compose( |
311 | filter: closable_filter<output>(seq.new_operation(id: 1)), |
312 | fod: closable_device<output>(seq.new_operation(id: 2)) |
313 | ), |
314 | sink: closable_device<output>(seq.new_operation(id: 3)) |
315 | ) |
316 | ); |
317 | BOOST_CHECK_NO_THROW(ch.reset()); |
318 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
319 | } |
320 | |
321 | // Tee a composite bidirectional device with a sink |
322 | { |
323 | operation_sequence seq; |
324 | chain<output> ch; |
325 | ch.push( |
326 | t: boost::iostreams::tee( |
327 | dev: boost::iostreams::compose( |
328 | filter: closable_filter<bidirectional>( |
329 | seq.new_operation(id: 2), |
330 | seq.new_operation(id: 3) |
331 | ), |
332 | fod: closable_device<bidirectional>( |
333 | seq.new_operation(id: 1), |
334 | seq.new_operation(id: 4) |
335 | ) |
336 | ), |
337 | sink: closable_device<output>(seq.new_operation(id: 5)) |
338 | ) |
339 | ); |
340 | BOOST_CHECK_NO_THROW(ch.reset()); |
341 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
342 | } |
343 | |
344 | // Tee a composite composite seekable device with a sink |
345 | { |
346 | operation_sequence seq; |
347 | chain<output> ch; |
348 | ch.push( |
349 | t: boost::iostreams::tee( |
350 | dev: boost::iostreams::compose( |
351 | filter: closable_filter<seekable>(seq.new_operation(id: 1)), |
352 | fod: closable_device<seekable>(seq.new_operation(id: 2)) |
353 | ), |
354 | sink: closable_device<output>(seq.new_operation(id: 3)) |
355 | ) |
356 | ); |
357 | BOOST_CHECK_NO_THROW(ch.reset()); |
358 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
359 | } |
360 | |
361 | |
362 | // Tee a composite sink |
363 | { |
364 | operation_sequence seq; |
365 | chain<output> ch; |
366 | ch.push( |
367 | t: boost::iostreams::tee( |
368 | snk: boost::iostreams::compose( |
369 | filter: closable_filter<output>(seq.new_operation(id: 1)), |
370 | fod: closable_device<output>(seq.new_operation(id: 2)) |
371 | ) |
372 | ) |
373 | ); |
374 | ch.push(t: closable_device<output>(seq.new_operation(id: 3))); |
375 | BOOST_CHECK_NO_THROW(ch.reset()); |
376 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
377 | } |
378 | |
379 | // Tee a composite bidirectional device with a sink |
380 | { |
381 | operation_sequence seq; |
382 | chain<output> ch; |
383 | ch.push( |
384 | t: boost::iostreams::tee( |
385 | snk: boost::iostreams::compose( |
386 | filter: closable_filter<bidirectional>( |
387 | seq.new_operation(id: 2), |
388 | seq.new_operation(id: 3) |
389 | ), |
390 | fod: closable_device<bidirectional>( |
391 | seq.new_operation(id: 1), |
392 | seq.new_operation(id: 4) |
393 | ) |
394 | ) |
395 | ) |
396 | ); |
397 | ch.push(t: closable_device<output>(seq.new_operation(id: 5))); |
398 | BOOST_CHECK_NO_THROW(ch.reset()); |
399 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
400 | } |
401 | |
402 | // Tee a composite composite seekable device with a sink |
403 | { |
404 | operation_sequence seq; |
405 | chain<output> ch; |
406 | ch.push( |
407 | t: boost::iostreams::tee( |
408 | snk: boost::iostreams::compose( |
409 | filter: closable_filter<seekable>(seq.new_operation(id: 1)), |
410 | fod: closable_device<seekable>(seq.new_operation(id: 2)) |
411 | ) |
412 | ) |
413 | ); |
414 | ch.push(t: closable_device<output>(seq.new_operation(id: 3))); |
415 | BOOST_CHECK_NO_THROW(ch.reset()); |
416 | BOOST_CHECK_OPERATION_SEQUENCE(seq); |
417 | } |
418 | } |
419 | |
420 | test_suite* init_unit_test_suite(int, char* []) |
421 | { |
422 | test_suite* test = BOOST_TEST_SUITE("tee test" ); |
423 | test->add(BOOST_TEST_CASE(&read_write_test)); |
424 | test->add(BOOST_TEST_CASE(&close_test)); |
425 | return test; |
426 | } |
427 | |