aboutsummaryrefslogtreecommitdiff
path: root/ttwhy/routers/echo.tests.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-09-22 22:47:33 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-09-22 22:47:33 +0200
commitd05b9343f69a0841043963088e0003368e205bd9 (patch)
tree8cde07ff86d27cbc808a0a149b89d0024b4a586c /ttwhy/routers/echo.tests.cpp
parent22f6534081acfabdab4956d337604a21e7da5b64 (diff)
downloadttwhy-master.tar.xz
ttwhy-master.zip
lib: restructure into vertical slicesHEADmaster
Diffstat (limited to 'ttwhy/routers/echo.tests.cpp')
-rw-r--r--ttwhy/routers/echo.tests.cpp257
1 files changed, 0 insertions, 257 deletions
diff --git a/ttwhy/routers/echo.tests.cpp b/ttwhy/routers/echo.tests.cpp
deleted file mode 100644
index da2db40..0000000
--- a/ttwhy/routers/echo.tests.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-#include <asio.hpp>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <string>
-#include <string_view>
-#include <utility>
-
-import ttwhy.routers;
-import ttwhy.scanners;
-
-using namespace std::string_view_literals;
-
-namespace
-{
- struct mock_stream
- {
- using executor_type = asio::any_io_executor;
-
- asio::any_io_executor executor;
- std::string written{};
-
- explicit mock_stream(asio::any_io_executor exec)
- : executor{std::move(exec)}
- {}
-
- auto get_executor() const noexcept -> executor_type
- {
- return executor;
- }
-
- template<typename ConstBufferSequence, typename Token>
- auto async_write_some(ConstBufferSequence const & buffers, Token && token)
- {
- return asio::async_initiate<Token, void(asio::error_code, std::size_t)>(
- [this](auto handler, ConstBufferSequence bufs) {
- auto const total = asio::buffer_size(bufs);
- auto const offset = written.size();
- written.resize(offset + total);
- asio::buffer_copy(asio::buffer(written.data() + offset, total), bufs);
- asio::post(executor, [handler = std::move(handler), total]() mutable {
- std::move(handler)(asio::error_code{}, total);
- });
- },
- token, buffers);
- }
- };
-
- struct incomplete_router_no_process
- {
- };
-
- struct incomplete_router_wrong_return
- {
- auto process(ttwhy::scanners::input_event) -> void;
- };
-
- struct incomplete_router_wrong_event
- {
- auto process(int) -> asio::awaitable<void>;
- };
-} // namespace
-
-TEST_CASE("The router concept validates router candidate interfaces", "[router][concept]")
-{
- STATIC_REQUIRE(ttwhy::router<ttwhy::routers::echo<mock_stream>, ttwhy::scanners::input_event>);
- STATIC_REQUIRE_FALSE(ttwhy::router<incomplete_router_no_process, ttwhy::scanners::input_event>);
- STATIC_REQUIRE_FALSE(ttwhy::router<incomplete_router_wrong_return, ttwhy::scanners::input_event>);
- STATIC_REQUIRE_FALSE(ttwhy::router<incomplete_router_wrong_event, ttwhy::scanners::input_event>);
-}
-
-SCENARIO("The echo router handles terminal navigation within the valid printed area", "[router][echo]")
-{
- GIVEN("An initialized echo router with a mock stream")
- {
- auto context = asio::io_context{};
- auto stream = mock_stream{context.get_executor()};
- auto router = ttwhy::routers::echo{stream};
-
- auto run_coro = [&](asio::awaitable<void> coro) {
- asio::co_spawn(context, std::move(coro), asio::detached);
- context.restart();
- context.run();
- };
-
- WHEN("Typing characters")
- {
- run_coro(router.process(ttwhy::scanners::character_event{'A'}));
- run_coro(router.process(ttwhy::scanners::character_event{'B'}));
-
- THEN("Characters are echoed and positions advance")
- {
- CHECK(stream.written == "AB");
- CHECK(router.cursor_position() == 2);
- CHECK(router.max_position() == 2);
- }
-
- AND_WHEN("Navigating left")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::left}));
-
- THEN("The cursor moves left")
- {
- CHECK(stream.written == "\x1b[D");
- CHECK(router.cursor_position() == 1);
- CHECK(router.max_position() == 2);
- }
-
- AND_WHEN("Navigating left again to position 0")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::left}));
-
- THEN("The cursor reaches 0")
- {
- CHECK(stream.written == "\x1b[D");
- CHECK(router.cursor_position() == 0);
- CHECK(router.max_position() == 2);
- }
-
- AND_WHEN("Attempting to navigate left beyond 0")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::left}));
-
- THEN("Navigation is prevented and nothing is emitted")
- {
- CHECK(stream.written.empty());
- CHECK(router.cursor_position() == 0);
- CHECK(router.max_position() == 2);
- }
- }
- }
- }
-
- AND_WHEN("Attempting to navigate right beyond what was printed")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::right}));
-
- THEN("Navigation is prevented and nothing is emitted")
- {
- CHECK(stream.written.empty());
- CHECK(router.cursor_position() == 2);
- CHECK(router.max_position() == 2);
- }
- }
-
- AND_WHEN("Navigating left and then overwriting with new character")
- {
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::left}));
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::character_event{'X'}));
-
- THEN("The new character is written and max position is preserved")
- {
- CHECK(stream.written == "X");
- CHECK(router.cursor_position() == 2);
- CHECK(router.max_position() == 2);
- }
- }
-
- AND_WHEN("Using Home and End keys")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::home}));
-
- THEN("Home navigates to 0")
- {
- CHECK(stream.written == "\x1b[2D");
- CHECK(router.cursor_position() == 0);
- CHECK(router.max_position() == 2);
- }
-
- AND_WHEN("Pressing Home when already at 0")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::home}));
-
- THEN("Nothing is emitted")
- {
- CHECK(stream.written.empty());
- CHECK(router.cursor_position() == 0);
- }
- }
-
- AND_WHEN("Pressing End from 0")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::end}));
-
- THEN("End moves to max position")
- {
- CHECK(stream.written == "\x1b[2C");
- CHECK(router.cursor_position() == 2);
- CHECK(router.max_position() == 2);
- }
- }
- }
-
- AND_WHEN("Pressing Enter")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::control_event{ttwhy::scanners::control_key::enter}));
-
- THEN("Newline is emitted and positions reset")
- {
- CHECK(stream.written == "\r\n");
- CHECK(router.cursor_position() == 0);
- CHECK(router.max_position() == 0);
- }
- }
-
- AND_WHEN("Pressing Backspace at position 0 after reset")
- {
- run_coro(router.process(ttwhy::scanners::control_event{ttwhy::scanners::control_key::enter}));
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::control_event{ttwhy::scanners::control_key::backspace}));
-
- THEN("Backspace does nothing at position 0")
- {
- CHECK(stream.written.empty());
- CHECK(router.cursor_position() == 0);
- CHECK(router.max_position() == 0);
- }
- }
-
- AND_WHEN("Using Delete key")
- {
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::left}));
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::delete_key}));
-
- THEN("Delete character sequence is emitted and max position shrinks")
- {
- CHECK(stream.written == "\x1b[P");
- CHECK(router.cursor_position() == 1);
- CHECK(router.max_position() == 1);
- }
-
- AND_WHEN("Pressing Delete when cursor is at max position")
- {
- stream.written.clear();
- run_coro(router.process(ttwhy::scanners::navigation_event{ttwhy::scanners::navigation_key::delete_key}));
-
- THEN("Nothing is emitted")
- {
- CHECK(stream.written.empty());
- CHECK(router.cursor_position() == 1);
- CHECK(router.max_position() == 1);
- }
- }
- }
- }
- }
-}