/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Lewis Baker // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include "io_service_fixture.hpp" #include #include "doctest/cppcoro_doctest.h" TEST_SUITE_BEGIN("file"); namespace fs = cppcoro::filesystem; namespace { class temp_dir_fixture { public: temp_dir_fixture() { auto tempDir = fs::temp_directory_path(); std::random_device random; for (int attempt = 1;; ++attempt) { m_path = tempDir / std::to_string(random()); try { fs::create_directories(m_path); return; } catch (const fs::filesystem_error&) { if (attempt == 10) { throw; } } } } ~temp_dir_fixture() { fs::remove_all(m_path); } const cppcoro::filesystem::path& temp_dir() { return m_path; } private: cppcoro::filesystem::path m_path; }; class temp_dir_with_io_service_fixture : public io_service_fixture, public temp_dir_fixture { }; } TEST_CASE_FIXTURE(temp_dir_fixture, "write a file") { auto filePath = temp_dir() / "foo"; cppcoro::io_service ioService; auto write = [&](cppcoro::io_service& ioService) -> cppcoro::task<> { std::printf(" starting write\n"); std::fflush(stdout); auto f = cppcoro::write_only_file::open(ioService, filePath); CHECK(f.size() == 0); char buffer[1024]; char c = 'a'; for (int i = 0; i < sizeof(buffer); ++i, c = (c == 'z' ? 'a' : c + 1)) { buffer[i] = c; } for (int chunk = 0; chunk < 10; ++chunk) { co_await f.write(chunk * sizeof(buffer), buffer, sizeof(buffer)); } }; auto read = [&](cppcoro::io_service& io) -> cppcoro::task<> { std::printf(" starting read\n"); std::fflush(stdout); auto f = cppcoro::read_only_file::open(io, filePath); const auto fileSize = f.size(); CHECK(fileSize == 10240); char buffer[20]; for (std::uint64_t i = 0; i < fileSize;) { auto bytesRead = co_await f.read(i, buffer, 20); for (size_t j = 0; j < bytesRead; ++j, ++i) { CHECK(buffer[j] == ('a' + ((i % 1024) % 26))); } } }; cppcoro::sync_wait(cppcoro::when_all( [&]() -> cppcoro::task { auto stopOnExit = cppcoro::on_scope_exit([&] { ioService.stop(); }); co_await write(ioService); co_await read(ioService); co_return 0; }(), [&]() -> cppcoro::task { ioService.process_events(); co_return 0; }())); } TEST_CASE_FIXTURE(temp_dir_with_io_service_fixture, "read write file") { auto run = [&]() -> cppcoro::task<> { cppcoro::io_work_scope ioScope{ io_service() }; auto f = cppcoro::read_write_file::open(io_service(), temp_dir() / "foo.txt"); char buffer1[100]; std::memset(buffer1, 0xAB, sizeof(buffer1)); co_await f.write(0, buffer1, sizeof(buffer1)); char buffer2[50]; std::memset(buffer2, 0xCC, sizeof(buffer2)); co_await f.read(0, buffer2, 50); CHECK(std::memcmp(buffer1, buffer2, 50) == 0); co_await f.read(50, buffer2, 50); CHECK(std::memcmp(buffer1 + 50, buffer2, 50) == 0); }; cppcoro::sync_wait(run()); } TEST_CASE_FIXTURE(temp_dir_with_io_service_fixture, "cancel read") { cppcoro::sync_wait([&]() -> cppcoro::task<> { cppcoro::io_work_scope ioScope{ io_service() }; auto f = cppcoro::read_write_file::open(io_service(), temp_dir() / "foo.txt"); f.set_size(20 * 1024 * 1024); cppcoro::cancellation_source canceller; try { (void)co_await cppcoro::when_all( [&]() -> cppcoro::task { const auto fileSize = f.size(); const std::size_t bufferSize = 64 * 1024; auto buffer = std::make_unique(bufferSize); std::uint64_t offset = 0; while (offset < fileSize) { auto bytesRead = co_await f.read(offset, buffer.get(), bufferSize, canceller.token()); offset += bytesRead; } WARN("should have been cancelled"); co_return 0; }(), [&]() -> cppcoro::task { using namespace std::chrono_literals; co_await io_service().schedule_after(1ms); canceller.request_cancellation(); co_return 0; }()); WARN("Expected exception to be thrown"); } catch (const cppcoro::operation_cancelled&) { } }()); } TEST_SUITE_END();