blob: fbc724fce3f8b3e3a38344bc84ab0d6fccf435f3 [file] [log] [blame]
// (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;
}