| // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
| // (C) Copyright 2005-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. |
| |
| // Allow this file to be used by slice_test.hpp. It is important not to |
| // replace BOOST_RESTRICT with BOOST_IOSTREAMS_RESTRICT here, since that |
| // would interfere with the oepration of the header |
| // <boost/iostreams/restrict.hpp> |
| #include <iostream> |
| |
| #if defined(BOOST_RESTRICT_USE_SLICE) |
| # include <boost/iostreams/slice.hpp> |
| # define BOOST_RESTRICT slice |
| #else |
| # include <boost/iostreams/restrict.hpp> |
| # define BOOST_RESTRICT restrict |
| #endif |
| |
| #include <algorithm> // equal. |
| #include <cctype> |
| #include <iterator> // back_inserter. |
| #include <vector> |
| #include <boost/iostreams/copy.hpp> |
| #include <boost/iostreams/device/file.hpp> |
| #include <boost/iostreams/device/null.hpp> |
| #include <boost/iostreams/filtering_stream.hpp> |
| #include <boost/test/test_tools.hpp> |
| #include <boost/test/unit_test.hpp> |
| #include "detail/closable.hpp" |
| #include "detail/constants.hpp" |
| #include "detail/filters.hpp" |
| #include "detail/operation_sequence.hpp" |
| #include "detail/sequence.hpp" |
| #include "detail/temp_file.hpp" |
| #include "detail/verification.hpp" |
| |
| using namespace std; |
| using namespace boost::iostreams; |
| using namespace boost::iostreams::test; |
| using boost::unit_test::test_suite; |
| namespace io = boost::iostreams; |
| |
| const char pad_char = '\n'; |
| const int small_padding = 50; |
| const int large_padding = default_device_buffer_size + 50; |
| |
| void write_padding(std::ofstream& out, int len) |
| { |
| for (int z = 0; z < len; ++z) |
| out.put(pad_char); |
| } |
| |
| struct restricted_test_file : public temp_file { |
| restricted_test_file(int padding, bool half_open = false) |
| { |
| BOOST_IOS::openmode mode = |
| BOOST_IOS::out | BOOST_IOS::binary; |
| ::std::ofstream f(name().c_str(), mode); |
| write_padding(f, padding); |
| const char* buf = narrow_data(); |
| for (int z = 0; z < data_reps; ++z) |
| f.write(buf, data_length()); |
| if (!half_open) |
| write_padding(f, padding); |
| } |
| }; |
| |
| struct restricted_test_sequence : public std::vector<char> { |
| restricted_test_sequence(int padding, bool half_open = false) |
| { |
| for (int z = 0; z < padding; ++z) |
| push_back(pad_char); |
| const char* buf = narrow_data(); |
| for (int w = 0; w < data_reps; ++w) |
| insert(end(), buf, buf + data_length()); |
| if (!half_open) |
| for (int x = 0; x < padding; ++x) |
| push_back(pad_char); |
| } |
| }; |
| |
| struct restricted_uppercase_file : public temp_file { |
| restricted_uppercase_file(int padding, bool half_open = false) |
| { |
| BOOST_IOS::openmode mode = |
| BOOST_IOS::out | BOOST_IOS::binary; |
| ::std::ofstream f(name().c_str(), mode); |
| write_padding(f, padding); |
| const char* buf = narrow_data(); |
| for (int z = 0; z < data_reps; ++z) |
| for (int w = 0; w < data_length(); ++w) |
| f.put((char) std::toupper(buf[w])); |
| if (!half_open) |
| write_padding(f, padding); |
| } |
| }; |
| |
| struct restricted_lowercase_file : public temp_file { |
| restricted_lowercase_file(int padding, bool half_open = false) |
| { |
| BOOST_IOS::openmode mode = |
| BOOST_IOS::out | BOOST_IOS::binary; |
| ::std::ofstream f(name().c_str(), mode); |
| write_padding(f, padding); |
| const char* buf = narrow_data(); |
| for (int z = 0; z < data_reps; ++z) |
| for (int w = 0; w < data_length(); ++w) |
| f.put((char) std::tolower(buf[w])); |
| if (!half_open) |
| write_padding(f, padding); |
| } |
| }; |
| |
| // Can't have a restricted view of a non-seekble output filter. |
| struct tolower_seekable_filter : public seekable_filter { |
| typedef char char_type; |
| struct category |
| : output_seekable, |
| filter_tag |
| { }; |
| template<typename Sink> |
| bool put(Sink& s, char c) |
| { return boost::iostreams::put(s, (char) std::tolower(c)); } |
| |
| template<typename Sink> |
| std::streampos seek(Sink& s, stream_offset off, BOOST_IOS::seekdir way) |
| { return boost::iostreams::seek(s, off, way); } |
| }; |
| |
| void read_device() |
| { |
| { |
| restricted_test_file src1(small_padding); |
| test_file src2; |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| filtering_istream first( |
| BOOST_RESTRICT(file_source(src1.name(), in_mode), off, len)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from restriction<Device> with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(large_padding); |
| test_file src2; |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_istream first( |
| BOOST_RESTRICT(file_source(src1.name(), in_mode), off, len)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from restriction<Device> with large padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(small_padding, true); |
| test_file src2; |
| stream_offset off = small_padding; |
| filtering_istream first( |
| BOOST_RESTRICT(file_source(src1.name(), in_mode), off)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from half-open restriction<Device> " |
| "with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(large_padding, true); |
| test_file src2; |
| stream_offset off = large_padding; |
| filtering_istream first( |
| BOOST_RESTRICT(file_source(src1.name(), in_mode), off)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from half-open restriction<Device> " |
| "with large padding" |
| ); |
| } |
| } |
| |
| void read_direct_device() |
| { |
| { |
| test_sequence<char> first; |
| restricted_test_sequence src(small_padding); |
| array_source array_src(&src[0], &src[0] + src.size()); |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| filtering_istream second(BOOST_RESTRICT(array_src, off, len)); |
| BOOST_CHECK_MESSAGE( |
| compare_container_and_stream(first, second), |
| "failed reading from restriction<Direct>" |
| ); |
| } |
| |
| { |
| test_sequence<char> first; |
| restricted_test_sequence src(small_padding, true); |
| array_source array_src(&src[0], &src[0] + src.size()); |
| stream_offset off = small_padding; |
| filtering_istream second(BOOST_RESTRICT(array_src, off)); |
| BOOST_CHECK_MESSAGE( |
| compare_container_and_stream(first, second), |
| "failed reading from half-open restriction<Direct>" |
| ); |
| } |
| } |
| |
| void read_filter() |
| { |
| { |
| restricted_test_file src1(small_padding); |
| uppercase_file src2; |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| filtering_istream first; |
| first.push(BOOST_RESTRICT(toupper_filter(), off, len)); |
| first.push(file_source(src1.name(), in_mode)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from restriction<Filter> with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(large_padding); |
| uppercase_file src2; |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_istream first; |
| first.push(BOOST_RESTRICT(toupper_filter(), off, len)); |
| first.push(file_source(src1.name(), in_mode)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from restriction<Filter> with large padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(small_padding, true); |
| uppercase_file src2; |
| stream_offset off = small_padding; |
| filtering_istream first; |
| first.push(BOOST_RESTRICT(toupper_filter(), off)); |
| first.push(file_source(src1.name(), in_mode)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from half-open restriction<Filter> " |
| "with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file src1(large_padding, true); |
| uppercase_file src2; |
| stream_offset off = large_padding; |
| filtering_istream first; |
| first.push(BOOST_RESTRICT(toupper_filter(), off)); |
| first.push(file_source(src1.name(), in_mode)); |
| ifstream second(src2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed reading from half-open restriction<Filter> " |
| "with large padding" |
| ); |
| } |
| } |
| |
| void write_device() |
| { |
| { |
| restricted_uppercase_file dest1(small_padding); |
| restricted_test_file dest2(small_padding); |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| filtering_ostream out( |
| BOOST_RESTRICT(file(dest1.name(), BOOST_IOS::binary), off, len)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Device> with small padding" |
| ); |
| } |
| |
| { |
| restricted_uppercase_file dest1(large_padding); |
| restricted_test_file dest2(large_padding); |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_ostream out |
| (BOOST_RESTRICT(file(dest1.name(), BOOST_IOS::binary), off, len)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Device> with large padding" |
| ); |
| } |
| |
| { |
| restricted_uppercase_file dest1(small_padding, true); |
| restricted_test_file dest2(small_padding, true); |
| stream_offset off = small_padding; |
| filtering_ostream out |
| (BOOST_RESTRICT(file(dest1.name(), BOOST_IOS::binary), off)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to half-open restriction<Device> " |
| "with small padding" |
| ); |
| } |
| |
| { |
| restricted_uppercase_file dest1(large_padding, true); |
| restricted_test_file dest2(large_padding, true); |
| stream_offset off = large_padding; |
| filtering_ostream out |
| (BOOST_RESTRICT(file(dest1.name(), BOOST_IOS::binary), off)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to half-open restriction<Device> " |
| "with large padding" |
| ); |
| } |
| } |
| |
| void write_direct_device() |
| { |
| { |
| vector<char> dest1( data_reps * data_length() + |
| 2 * small_padding, |
| '\n' ); |
| restricted_test_sequence dest2(small_padding); |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| array_sink array(&dest1[0], &dest1[0] + dest1.size()); |
| filtering_ostream out(BOOST_RESTRICT(array, off, len)); |
| write_data_in_chunks(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| std::equal(dest1.begin(), dest1.end(), dest2.begin()), |
| "failed writing to restriction<Direct>" |
| ); |
| } |
| |
| { |
| vector<char> dest1( |
| data_reps * data_length() + small_padding, '\n'); |
| restricted_test_sequence dest2(small_padding, true); |
| stream_offset off = small_padding; |
| array_sink array(&dest1[0], &dest1[0] + dest1.size()); |
| filtering_ostream out(BOOST_RESTRICT(array, off)); |
| write_data_in_chunks(out); |
| out.reset(); |
| BOOST_CHECK_MESSAGE( |
| std::equal(dest1.begin(), dest1.end(), dest2.begin()), |
| "failed writing to half-open restriction<Direct>" |
| ); |
| } |
| } |
| |
| void write_filter() |
| { |
| { |
| restricted_test_file dest1(small_padding); |
| restricted_lowercase_file dest2(small_padding); |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| filtering_ostream out; |
| out.push(BOOST_RESTRICT(tolower_seekable_filter(), off, len)); |
| out.push(file(dest1.name(), BOOST_IOS::binary)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Filter> with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file dest1(large_padding); |
| restricted_lowercase_file dest2(large_padding); |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_ostream out; |
| out.push(BOOST_RESTRICT(tolower_seekable_filter(), off, len)); |
| out.push(file(dest1.name(), BOOST_IOS::binary)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Filter> with large padding" |
| ); |
| } |
| |
| { |
| restricted_test_file dest1(small_padding, true); |
| restricted_lowercase_file dest2(small_padding, true); |
| stream_offset off = small_padding; |
| filtering_ostream out; |
| out.push(BOOST_RESTRICT(tolower_seekable_filter(), off)); |
| out.push(file(dest1.name(), BOOST_IOS::binary)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Filter> with small padding" |
| ); |
| } |
| |
| { |
| restricted_test_file dest1(large_padding, true); |
| restricted_lowercase_file dest2(large_padding, true); |
| stream_offset off = large_padding; |
| filtering_ostream out; |
| out.push(BOOST_RESTRICT(tolower_seekable_filter(), off)); |
| out.push(file(dest1.name(), BOOST_IOS::binary)); |
| write_data_in_chunks(out); |
| out.reset(); |
| ifstream first(dest1.name().c_str(), in_mode); |
| ifstream second(dest2.name().c_str(), in_mode); |
| BOOST_CHECK_MESSAGE( |
| compare_streams_in_chunks(first, second), |
| "failed writing to restriction<Filter> with large padding" |
| ); |
| } |
| } |
| |
| void seek_device() |
| { |
| { |
| restricted_test_file src(large_padding); |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_stream<seekable> io( |
| BOOST_RESTRICT(file(src.name(), BOOST_IOS::binary), off, len)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chunks(io), |
| "failed seeking within restriction<Device>" |
| ); |
| } |
| |
| { |
| restricted_test_file src(large_padding, true); |
| stream_offset off = large_padding; |
| filtering_stream<seekable> io( |
| BOOST_RESTRICT(file(src.name(), BOOST_IOS::binary), off)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chunks(io), |
| "failed seeking within half-open restriction<Device>" |
| ); |
| } |
| } |
| |
| void seek_direct_device() |
| { |
| { |
| vector<char> src( |
| data_reps * data_length() + 2 * small_padding, '\n'); |
| stream_offset off = small_padding, |
| len = data_reps * data_length(); |
| array ar(&src[0], &src[0] + src.size()); |
| filtering_stream<seekable> io(BOOST_RESTRICT(ar, off, len)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chars(io), |
| "failed seeking within restriction<Direct> with small padding" |
| ); |
| } |
| |
| { |
| vector<char> src( |
| data_reps * data_length() + small_padding, '\n'); |
| stream_offset off = small_padding; |
| array ar(&src[0], &src[0] + src.size()); |
| filtering_stream<seekable> io(BOOST_RESTRICT(ar, off)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chars(io), |
| "failed seeking within half-open restriction<Direct> " |
| "with small padding" |
| ); |
| } |
| } |
| |
| void seek_filter() |
| { |
| { |
| restricted_test_file src(small_padding); |
| stream_offset off = large_padding, |
| len = data_reps * data_length(); |
| filtering_stream<seekable> io; |
| io.push(BOOST_RESTRICT(identity_seekable_filter(), off, len)); |
| io.push(file(src.name(), BOOST_IOS::binary)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chars(io), |
| "failed seeking within restriction<Device>" |
| ); |
| } |
| |
| { |
| restricted_test_file src(small_padding, true); |
| stream_offset off = large_padding; |
| filtering_stream<seekable> io; |
| io.push(BOOST_RESTRICT(identity_seekable_filter(), off)); |
| io.push(file(src.name(), BOOST_IOS::binary)); |
| BOOST_CHECK_MESSAGE( |
| test_seekable_in_chars(io), |
| "failed seeking within half-open restriction<Device>" |
| ); |
| } |
| } |
| |
| void close_device() |
| { |
| // Restrict a source |
| { |
| operation_sequence seq; |
| chain<input> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_device<input>(seq.new_operation(1)), |
| 0 |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a seekable device |
| { |
| operation_sequence seq; |
| chain<seekable> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_device<seekable>(seq.new_operation(1)), |
| 0 |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a direct source |
| { |
| operation_sequence seq; |
| chain<input> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_device<direct_input>(seq.new_operation(1)), |
| 0 |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a direct seekable device |
| { |
| operation_sequence seq; |
| chain<seekable> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_device<direct_seekable>(seq.new_operation(1)), |
| 0 |
| ) |
| ); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| } |
| |
| void close_filter() |
| { |
| // Restrict an input filter |
| { |
| operation_sequence seq; |
| chain<input> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_filter<input>(seq.new_operation(2)), |
| 0 |
| ) |
| ); |
| ch.push(closable_device<input>(seq.new_operation(1))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a seekable filter |
| { |
| operation_sequence seq; |
| chain<seekable> ch; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_filter<seekable>(seq.new_operation(1)), |
| 0 |
| ) |
| ); |
| ch.push(closable_device<seekable>(seq.new_operation(2))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a dual_use filter for input |
| { |
| operation_sequence seq; |
| chain<input> ch; |
| operation dummy; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_filter<dual_use>( |
| seq.new_operation(2), |
| dummy |
| ), |
| 0 |
| ) |
| ); |
| ch.push(closable_device<input>(seq.new_operation(1))); |
| BOOST_CHECK_NO_THROW(ch.reset()); |
| BOOST_CHECK_OPERATION_SEQUENCE(seq); |
| } |
| |
| // Restrict a dual_use filter for output |
| { |
| operation_sequence seq; |
| chain<output> ch; |
| operation dummy; |
| ch.push( |
| io::BOOST_RESTRICT( |
| closable_filter<dual_use>( |
| dummy, |
| seq.new_operation(1) |
| ), |
| 0 |
| ) |
| ); |
| ch.push(closable_device<output>(seq.new_operation(2))); |
| 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(BOOST_STRINGIZE(BOOST_RESTRICT) " test"); |
| test->add(BOOST_TEST_CASE(&read_device)); |
| test->add(BOOST_TEST_CASE(&read_direct_device)); |
| test->add(BOOST_TEST_CASE(&read_filter)); |
| test->add(BOOST_TEST_CASE(&write_device)); |
| test->add(BOOST_TEST_CASE(&write_direct_device)); |
| test->add(BOOST_TEST_CASE(&write_filter)); |
| test->add(BOOST_TEST_CASE(&seek_device)); |
| test->add(BOOST_TEST_CASE(&seek_direct_device)); |
| test->add(BOOST_TEST_CASE(&close_device)); |
| test->add(BOOST_TEST_CASE(&close_filter)); |
| return test; |
| } |