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 
read_write_test()26 void read_write_test()
27 {
28     {
29         test_file          src1, src2;
30         temp_file          dest;
31         filtering_istream  first, second;
32         first.push(tee(file_sink(dest.name(), out_mode)));
33         first.push(file_source(src1.name(), in_mode));
34         second.push(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(tee(file_sink(dest.name(), out_mode)));
48         first.push(file_source(src1.name(), in_mode));
49         second.push(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(tee(file_sink(dest1.name(), out_mode)));
63         out.push(file_sink(dest2.name(), out_mode));
64         write_data_in_chars(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(tee(file_sink(dest1.name(), out_mode)));
77         out.push(file_sink(dest2.name(), out_mode));
78         write_data_in_chunks(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( tee( file_source(src1.name(), in_mode),
91                          file_sink(dest.name(), out_mode) ) );
92         second.push(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( tee( file_source(src1.name(), in_mode),
106                          file_sink(dest.name(), out_mode) ) );
107         second.push(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( tee( file_sink(dest1.name(), out_mode),
121                        file_sink(dest2.name(), out_mode) ) );
122         write_data_in_chars(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( tee( file_sink(dest1.name(), out_mode),
135                        file_sink(dest2.name(), out_mode) ) );
136         write_data_in_chunks(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 
close_test()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             boost::iostreams::tee(
157                 closable_device<output>(seq.new_operation(1)),
158                 closable_device<
159                     #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
160                         borland_output
161                     #else
162                         output
163                     #endif
164                 >(seq.new_operation(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             boost::iostreams::tee(
177                 closable_device<bidirectional>(
178                     seq.new_operation(1),
179                     seq.new_operation(2)
180                 ),
181                 closable_device<bidirectional>(
182                     seq.new_operation(3),
183                     seq.new_operation(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             boost::iostreams::tee(
197                 closable_device<seekable>(seq.new_operation(1)),
198                 closable_device<seekable>(seq.new_operation(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(boost::iostreams::tee(closable_device<output>(seq.new_operation(1))));
210         ch.push(closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
221                 closable_device<bidirectional>(
222                     seq.new_operation(1),
223                     seq.new_operation(2)
224                 )
225             )
226         );
227         ch.push(closable_device<output>(seq.new_operation(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(boost::iostreams::tee(closable_device<seekable>(seq.new_operation(1))));
237         ch.push(closable_device<seekable>(seq.new_operation(2)));
238         BOOST_CHECK_NO_THROW(ch.reset());
239         BOOST_CHECK_OPERATION_SEQUENCE(seq);
240     }
241 }
242 
test_std_io()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(tee(stream1));
251         out.push(file_sink(dest2.name(), out_mode));
252         write_data_in_chunks(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( tee ( stream1,
264                         file_sink(dest2.name(), out_mode) ) );
265         write_data_in_chunks(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( tee ( file_sink(dest1.name(), out_mode),
277                         stream2 ) );
278         write_data_in_chunks(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(tee(stream1, stream2));
291         write_data_in_chunks(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 
tee_composite_test()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             boost::iostreams::tee(
310                 boost::iostreams::compose(
311                     closable_filter<output>(seq.new_operation(1)),
312                     closable_device<output>(seq.new_operation(2))
313                 ),
314                 closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
327                 boost::iostreams::compose(
328                     closable_filter<bidirectional>(
329                         seq.new_operation(2),
330                         seq.new_operation(3)
331                     ),
332                     closable_device<bidirectional>(
333                         seq.new_operation(1),
334                         seq.new_operation(4)
335                     )
336                 ),
337                 closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
350                 boost::iostreams::compose(
351                     closable_filter<seekable>(seq.new_operation(1)),
352                     closable_device<seekable>(seq.new_operation(2))
353                 ),
354                 closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
368                 boost::iostreams::compose(
369                     closable_filter<output>(seq.new_operation(1)),
370                     closable_device<output>(seq.new_operation(2))
371                 )
372             )
373         );
374         ch.push(closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
385                 boost::iostreams::compose(
386                     closable_filter<bidirectional>(
387                         seq.new_operation(2),
388                         seq.new_operation(3)
389                     ),
390                     closable_device<bidirectional>(
391                         seq.new_operation(1),
392                         seq.new_operation(4)
393                     )
394                 )
395             )
396         );
397         ch.push(closable_device<output>(seq.new_operation(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             boost::iostreams::tee(
408                 boost::iostreams::compose(
409                     closable_filter<seekable>(seq.new_operation(1)),
410                     closable_device<seekable>(seq.new_operation(2))
411                 )
412             )
413         );
414         ch.push(closable_device<output>(seq.new_operation(3)));
415         BOOST_CHECK_NO_THROW(ch.reset());
416         BOOST_CHECK_OPERATION_SEQUENCE(seq);
417     }
418 }
419 
init_unit_test_suite(int,char * [])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