cppcoro/test/file_tests.cpp
2018-08-20 21:07:17 -07:00

214 lines
4.6 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#include <cppcoro/io_service.hpp>
#include <cppcoro/read_only_file.hpp>
#include <cppcoro/write_only_file.hpp>
#include <cppcoro/read_write_file.hpp>
#include <cppcoro/task.hpp>
#include <cppcoro/sync_wait.hpp>
#include <cppcoro/when_all.hpp>
#include <cppcoro/cancellation_source.hpp>
#include <cppcoro/on_scope_exit.hpp>
#include <random>
#include <thread>
#include <cassert>
#include "io_service_fixture.hpp"
#include <ostream>
#include "doctest/doctest.h"
TEST_SUITE_BEGIN("file");
namespace fs = std::experimental::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 std::experimental::filesystem::path& temp_dir()
{
return m_path;
}
private:
std::experimental::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<int>
{
auto stopOnExit = cppcoro::on_scope_exit([&] { ioService.stop(); });
co_await write(ioService);
co_await read(ioService);
co_return 0;
}(),
[&]() -> cppcoro::task<int>
{
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<int>
{
const auto fileSize = f.size();
const std::size_t bufferSize = 64 * 1024;
auto buffer = std::make_unique<std::uint8_t[]>(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<int>
{
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();