/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2015 Cloudius Systems, Ltd. */ #include #include #include "core/reactor.hh" #include "core/fstream.hh" #include "core/shared_ptr.hh" #include "core/app-template.hh" #include "core/do_with.hh" #include "core/seastar.hh" #include "test-utils.hh" struct writer { output_stream out; writer(file f) : out(make_file_output_stream( make_lw_shared(std::move(f)))) {} }; struct reader { input_stream in; reader(file f) : in(make_file_input_stream( make_lw_shared(std::move(f)))) {} }; SEASTAR_TEST_CASE(test_fstream) { auto sem = make_lw_shared(0); engine().open_file_dma("testfile.tmp", open_flags::rw | open_flags::create | open_flags::truncate).then([sem] (file f) { auto w = make_shared(std::move(f)); auto buf = static_cast(::malloc(4096)); memset(buf, 0, 4096); buf[0] = '['; buf[1] = 'A'; buf[4095] = ']'; w->out.write(buf, 4096).then([buf, w] { ::free(buf); return make_ready_future<>(); }).then([w] { auto buf = static_cast(::malloc(8192)); memset(buf, 0, 8192); buf[0] = '['; buf[1] = 'B'; buf[8191] = ']'; return w->out.write(buf, 8192).then([buf, w] { ::free(buf); return w->out.close().then([w] {}); }); }).then([] { return engine().open_file_dma("testfile.tmp", open_flags::ro); }).then([] (file f) { /* file content after running the above: * 00000000 5b 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |[A..............| * 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * * * 00000ff0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5d |...............]| * 00001000 5b 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |[B..............| * 00001010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * * * 00002ff0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5d |...............]| * 00003000 */ auto r = make_shared(std::move(f)); return r->in.read_exactly(4096 + 8192).then([r] (temporary_buffer buf) { auto p = buf.get(); BOOST_REQUIRE(p[0] == '[' && p[1] == 'A' && p[4095] == ']'); BOOST_REQUIRE(p[4096] == '[' && p[4096 + 1] == 'B' && p[4096 + 8191] == ']'); return make_ready_future<>(); }); }).finally([sem] () { sem->signal(); }); }); return sem->wait(); } SEASTAR_TEST_CASE(test_fstream_unaligned) { auto sem = make_lw_shared(0); engine().open_file_dma("testfile.tmp", open_flags::rw | open_flags::create | open_flags::truncate).then([sem] (file f) { auto w = make_shared(std::move(f)); auto buf = static_cast(::malloc(40)); memset(buf, 0, 40); buf[0] = '['; buf[1] = 'A'; buf[39] = ']'; w->out.write(buf, 40).then([buf, w] { ::free(buf); return w->out.close().then([w] {}); }).then([] { return engine().open_file_dma("testfile.tmp", open_flags::ro); }).then([] (file f) { return do_with(std::move(f), [] (file& f) { return f.size().then([] (size_t size) { // assert that file was indeed truncated to the amount of bytes written. BOOST_REQUIRE(size == 40); return make_ready_future<>(); }); }); }).then([] { return engine().open_file_dma("testfile.tmp", open_flags::ro); }).then([] (file f) { auto r = make_shared(std::move(f)); return r->in.read_exactly(40).then([r] (temporary_buffer buf) { auto p = buf.get(); BOOST_REQUIRE(p[0] == '[' && p[1] == 'A' && p[39] == ']'); return make_ready_future<>(); }); }).finally([sem] () { sem->signal(); }); }); return sem->wait(); } future<> test_consume_until_end(uint64_t size) { return open_file_dma("testfile.tmp", open_flags::rw | open_flags::create | open_flags::truncate).then([size] (file f) { return do_with(make_lw_shared(std::move(f)), [size] (lw_shared_ptr f) { return do_with(make_file_output_stream(f), [size] (output_stream& out) { std::vector buf(size); std::iota(buf.begin(), buf.end(), 0); return out.write(buf.data(), buf.size()).then([&out] { return out.flush(); }); }).then([f] { return f->size(); }).then([size, f] (size_t real_size) { BOOST_REQUIRE_EQUAL(size, real_size); }).then([size, f] { auto consumer = [offset = uint64_t(0), size] (temporary_buffer buf) mutable -> future::unconsumed_remainder> { if (!buf) { return make_ready_future::unconsumed_remainder>(temporary_buffer()); } BOOST_REQUIRE(offset + buf.size() <= size); std::vector expected(buf.size()); std::iota(expected.begin(), expected.end(), offset); offset += buf.size(); BOOST_REQUIRE(std::equal(buf.begin(), buf.end(), expected.begin())); return make_ready_future::unconsumed_remainder>(std::experimental::nullopt); }; return do_with(make_file_input_stream(f), std::move(consumer), [size] (input_stream& in, auto& consumer) { return in.consume(consumer); }); }); }); }); } SEASTAR_TEST_CASE(test_consume_aligned_file) { return test_consume_until_end(4096); } SEASTAR_TEST_CASE(test_consume_empty_file) { return test_consume_until_end(0); } SEASTAR_TEST_CASE(test_consume_unaligned_file) { return test_consume_until_end(1); } SEASTAR_TEST_CASE(test_consume_unaligned_file_large) { return test_consume_until_end((1 << 20) + 1); }