// (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 #include #include #include #include #include #include #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 ch; ch.push( boost::iostreams::tee( closable_device(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 ch; ch.push( boost::iostreams::tee( closable_device( seq.new_operation(1), seq.new_operation(2) ), closable_device( 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 ch; ch.push( boost::iostreams::tee( closable_device(seq.new_operation(1)), closable_device(seq.new_operation(2)) ) ); BOOST_CHECK_NO_THROW(ch.reset()); BOOST_CHECK_OPERATION_SEQUENCE(seq); } // Tee a sink { operation_sequence seq; chain ch; ch.push(boost::iostreams::tee(closable_device(seq.new_operation(1)))); ch.push(closable_device(seq.new_operation(2))); BOOST_CHECK_NO_THROW(ch.reset()); BOOST_CHECK_OPERATION_SEQUENCE(seq); } // Tee a bidirectional device { operation_sequence seq; chain ch; ch.push( boost::iostreams::tee( closable_device( seq.new_operation(1), seq.new_operation(2) ) ) ); ch.push(closable_device(seq.new_operation(3))); BOOST_CHECK_NO_THROW(ch.reset()); BOOST_CHECK_OPERATION_SEQUENCE(seq); } // Tee a seekable device { operation_sequence seq; chain ch; ch.push(boost::iostreams::tee(closable_device(seq.new_operation(1)))); ch.push(closable_device(seq.new_operation(2))); BOOST_CHECK_NO_THROW(ch.reset()); BOOST_CHECK_OPERATION_SEQUENCE(seq); } } void test_std_io() { { temp_file dest1; temp_file dest2; filtering_ostream out; std::ofstream stream1(dest1.name().c_str(), out_mode); out.push(tee(stream1)); out.push(file_sink(dest2.name(), out_mode)); write_data_in_chunks(out); BOOST_CHECK_MESSAGE( compare_files(dest1.name(), dest2.name()), "failed writing to a tee_device in chunks" ); } { temp_file dest1; temp_file dest2; filtering_ostream out; std::ofstream stream1(dest1.name().c_str(), out_mode); out.push( tee ( stream1, file_sink(dest2.name(), out_mode) ) ); write_data_in_chunks(out); BOOST_CHECK_MESSAGE( compare_files(dest1.name(), dest2.name()), "failed writing to a tee_device in chunks" ); } { temp_file dest1; temp_file dest2; filtering_ostream out; std::ofstream stream2(dest2.name().c_str(), out_mode); out.push( tee ( file_sink(dest1.name(), out_mode), stream2 ) ); write_data_in_chunks(out); BOOST_CHECK_MESSAGE( compare_files(dest1.name(), dest2.name()), "failed writing to a tee_device in chunks" ); } { temp_file dest1; temp_file dest2; filtering_ostream out; std::ofstream stream1(dest1.name().c_str(), out_mode); std::ofstream stream2(dest2.name().c_str(), out_mode); out.push(tee(stream1, stream2)); write_data_in_chunks(out); BOOST_CHECK_MESSAGE( compare_files(dest1.name(), dest2.name()), "failed writing to a tee_device in chunks" ); } } 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 ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter(seq.new_operation(1)), closable_device(seq.new_operation(2)) ), closable_device(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 ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter( seq.new_operation(2), seq.new_operation(3) ), closable_device( seq.new_operation(1), seq.new_operation(4) ) ), closable_device(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 ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter(seq.new_operation(1)), closable_device(seq.new_operation(2)) ), closable_device(seq.new_operation(3)) ) ); BOOST_CHECK_NO_THROW(ch.reset()); BOOST_CHECK_OPERATION_SEQUENCE(seq); } // Tee a composite sink { operation_sequence seq; chain ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter(seq.new_operation(1)), closable_device(seq.new_operation(2)) ) ) ); ch.push(closable_device(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 ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter( seq.new_operation(2), seq.new_operation(3) ), closable_device( seq.new_operation(1), seq.new_operation(4) ) ) ) ); ch.push(closable_device(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 ch; ch.push( boost::iostreams::tee( boost::iostreams::compose( closable_filter(seq.new_operation(1)), closable_device(seq.new_operation(2)) ) ) ); ch.push(closable_device(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; }