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
20using namespace std;
21using namespace boost;
22using namespace boost::iostreams;
23using namespace boost::iostreams::test;
24using boost::unit_test::test_suite;
25
26void 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
145void 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
243void 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
299void 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
420test_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

source code of boost/libs/iostreams/test/tee_test.cpp