| // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
| // (C) Copyright 2004-2007 Jonathan Turkanis |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) |
| |
| // See http://www.boost.org/libs/iostreams for documentation. |
| |
| #include <fstream> |
| #include <boost/iostreams/compose.hpp> |
| #include <boost/iostreams/device/file.hpp> |
| #include <boost/iostreams/filtering_stream.hpp> |
| #include <boost/iostreams/tee.hpp> |
| #include <boost/test/test_tools.hpp> |
| #include <boost/test/unit_test.hpp> |
| #include "detail/closable.hpp" |
| #include "detail/operation_sequence.hpp" |
| #include "detail/temp_file.hpp" |
| #include "detail/verification.hpp" |
| |
| using namespace std; |
| using namespace boost; |
| using namespace boost::iostreams; |
| using namespace boost::iostreams::test; |
| using boost::unit_test::test_suite; |
| |
| void read_write_test() |
| { |
| { |
| test_file src1, src2; |
| temp_file dest; |
| filtering_istream first, second; |
| first.push(tee(file_sink(dest.name(), out_mode))); |
| first.push(file_source(src1.name(), in_mode)); |
| second.push(file_source(src2.name(), in_mode)); |
| compare_streams_in_chars(first, second); // ignore return value |
| first.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest.name(), src1.name()), |
| "failed reading from a tee_filter in chars" |
| ); |
| } |
| |
| { |
| test_file src1, src2; |
| temp_file dest; |
| filtering_istream first, second; |
| first.push(tee(file_sink(dest.name(), out_mode))); |
| first.push(file_source(src1.name(), in_mode)); |
| second.push(file_source(src2.name(), in_mode)); |
| compare_streams_in_chunks(first, second); // ignore return value |
| first.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest.name(), src1.name()), |
| "failed reading from a tee_filter in chunks" |
| ); |
| } |
| |
| { |
| temp_file dest1; |
| temp_file dest2; |
| filtering_ostream out; |
| out.push(tee(file_sink(dest1.name(), out_mode))); |
| out.push(file_sink(dest2.name(), out_mode)); |
| write_data_in_chars(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest1.name(), dest2.name()), |
| "failed writing to a tee_filter in chars" |
| ); |
| } |
| |
| { |
| temp_file dest1; |
| temp_file dest2; |
| filtering_ostream out; |
| out.push(tee(file_sink(dest1.name(), out_mode))); |
| out.push(file_sink(dest2.name(), out_mode)); |
| write_data_in_chunks(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest1.name(), dest2.name()), |
| "failed writing to a tee_filter in chunks" |
| ); |
| } |
| |
| { |
| test_file src1, src2; |
| temp_file dest; |
| filtering_istream first, second; |
| first.push( tee( file_source(src1.name(), in_mode), |
| file_sink(dest.name(), out_mode) ) ); |
| second.push(file_source(src2.name(), in_mode)); |
| compare_streams_in_chars(first, second); // ignore return value |
| first.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest.name(), src1.name()), |
| "failed reading from a tee_device in chars" |
| ); |
| } |
| |
| { |
| test_file src1, src2; |
| temp_file dest; |
| filtering_istream first, second; |
| first.push( tee( file_source(src1.name(), in_mode), |
| file_sink(dest.name(), out_mode) ) ); |
| second.push(file_source(src2.name(), in_mode)); |
| compare_streams_in_chunks(first, second); // ignore return value |
| first.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest.name(), src1.name()), |
| "failed reading from a tee_device in chunks" |
| ); |
| } |
| |
| { |
| temp_file dest1; |
| temp_file dest2; |
| filtering_ostream out; |
| out.push( tee( file_sink(dest1.name(), out_mode), |
| file_sink(dest2.name(), out_mode) ) ); |
| write_data_in_chars(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest1.name(), dest2.name()), |
| "failed writing to a tee_device in chars" |
| ); |
| } |
| |
| { |
| temp_file dest1; |
| temp_file dest2; |
| filtering_ostream out; |
| out.push( tee( file_sink(dest1.name(), out_mode), |
| file_sink(dest2.name(), out_mode) ) ); |
| write_data_in_chunks(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| compare_files(dest1.name(), dest2.name()), |
| "failed writing to a tee_device in chunks" |
| ); |
| } |
| } |
| |
| void close_test() |
| { |
| // Note: The implementation of tee_device closes the first |
| // sink before the second |
| |
| // Tee two sinks (Borland <= 5.8.2 needs a little help compiling this case, |
| // but it executes the closing algorithm correctly) |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| closable_device<output>(seq.new_operation(1)), |
| closable_device< |
| #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
| borland_output |
| #else |
| output |
| #endif |
| >(seq.new_operation(2)) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee two bidirectional devices |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| closable_device<bidirectional>( |
| seq.new_operation(1), |
| seq.new_operation(2) |
| ), |
| closable_device<bidirectional>( |
| seq.new_operation(3), |
| seq.new_operation(4) |
| ) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee two seekable devices |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| closable_device<seekable>(seq.new_operation(1)), |
| closable_device<seekable>(seq.new_operation(2)) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push(boost::iostreams::tee(closable_device<output>(seq.new_operation(1)))); |
| ch.push(closable_device<output>(seq.new_operation(2))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a bidirectional device |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| closable_device<bidirectional>( |
| seq.new_operation(1), |
| seq.new_operation(2) |
| ) |
| ) |
| ); |
| ch.push(closable_device<output>(seq.new_operation(3))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a seekable device |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push(boost::iostreams::tee(closable_device<seekable>(seq.new_operation(1)))); |
| ch.push(closable_device<seekable>(seq.new_operation(2))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| } |
| |
| void tee_composite_test() |
| { |
| // This test is probably redundant, given the above test and the tests in |
| // compose_test.cpp, but it verifies that ticket #1002 is fixed |
| |
| // Tee a composite sink with a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<output>(seq.new_operation(1)), |
| closable_device<output>(seq.new_operation(2)) |
| ), |
| closable_device<output>(seq.new_operation(3)) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a composite bidirectional device with a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<bidirectional>( |
| seq.new_operation(2), |
| seq.new_operation(3) |
| ), |
| closable_device<bidirectional>( |
| seq.new_operation(1), |
| seq.new_operation(4) |
| ) |
| ), |
| closable_device<output>(seq.new_operation(5)) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a composite composite seekable device with a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<seekable>(seq.new_operation(1)), |
| closable_device<seekable>(seq.new_operation(2)) |
| ), |
| closable_device<output>(seq.new_operation(3)) |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| |
| // Tee a composite sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<output>(seq.new_operation(1)), |
| closable_device<output>(seq.new_operation(2)) |
| ) |
| ) |
| ); |
| ch.push(closable_device<output>(seq.new_operation(3))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a composite bidirectional device with a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<bidirectional>( |
| seq.new_operation(2), |
| seq.new_operation(3) |
| ), |
| closable_device<bidirectional>( |
| seq.new_operation(1), |
| seq.new_operation(4) |
| ) |
| ) |
| ) |
| ); |
| ch.push(closable_device<output>(seq.new_operation(5))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Tee a composite composite seekable device with a sink |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| ch.push( |
| boost::iostreams::tee( |
| boost::iostreams::compose( |
| closable_filter<seekable>(seq.new_operation(1)), |
| closable_device<seekable>(seq.new_operation(2)) |
| ) |
| ) |
| ); |
| ch.push(closable_device<output>(seq.new_operation(3))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| } |
| |
| test_suite* init_unit_test_suite(int, char* []) |
| { |
| test_suite* test = BOOST_TEST_SUITE("tee test"); |
| test->add(BOOST_TEST_CASE(&read_write_test)); |
| test->add(BOOST_TEST_CASE(&close_test)); |
| return test; |
| } |