You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trompeloeil/test/test_co_mock.cpp

202 lines
4.4 KiB
C++

/*
* Trompeloeil C++ mocking framework
*
* Copyright (C) Björn Fahller
*
* Use, modification and distribution is subject to the
* Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy atl
* http://www.boost.org/LICENSE_1_0.txt)
*
* Project home: https://github.com/rollbear/trompeloeil
*/
#ifdef __cpp_impl_coroutine
#include <trompeloeil/mock.hpp>
#include <trompeloeil/coro.hpp>
#if defined(CATCH2_VERSION) && CATCH2_VERSION == 3
#include <catch2/catch_test_macros.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include "micro_coro.hpp"
#include "test_reporter.hpp"
#include <optional>
using trompeloeil::_;
namespace {
using iptr = std::unique_ptr<int>;
struct co_mock {
MAKE_MOCK0 (intret, coro::task<int>());
MAKE_MOCK0 (voidret, coro::task<void>());
MAKE_MOCK1 (unique, coro::task<iptr>(iptr));
MAKE_MOCK0 (gen, coro::generator<int>());
};
}
TEST_CASE_METHOD(
Fixture,
"A CO_RETURNed value is obtained from co_await",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, intret()).CO_RETURN(3);
int x = 0;
std::invoke([&]()->coro::task<void>{ x = co_await m.intret();});
REQUIRE(x == 3);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"The coroutine can be executed after the expectation has been retired",
"[coro]")
{
co_mock m;
std::optional<coro::task<int>> t;
{
REQUIRE_CALL(m, intret()).CO_RETURN(3);
t.emplace(m.intret());
}
int x = 0;
std::invoke([&]()->coro::task<void>{ x = co_await *t;});
REQUIRE(x == 3);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"A void co_routine is CO_RETURNed",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, voidret()).CO_RETURN();
int x = 0;
std::invoke([&]()->coro::task<void>{ co_await m.voidret(); x = 3;});
REQUIRE(x == 3);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"If the CO_RETURN expression throws, the exception is thrown from co_await",
"[coro]")
{
co_mock m;
std::optional<coro::task<int>> t;
{
REQUIRE_CALL(m, intret()).CO_RETURN(false ? 0 : throw "foo");
t.emplace(m.intret());
}
int x = 0;
std::invoke([&]()->coro::task<void>{
REQUIRE_THROWS(co_await *t);
x = 1;
});
REQUIRE(x == 1);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"Exception from CO_THROW is thrown from co_await",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, intret()).CO_THROW("foo");
auto p = m.intret();
int x = 0;
std::invoke([&]()->coro::task<void>{
REQUIRE_THROWS(co_await p);
x = 1;
});
REQUIRE(x == 1);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"A move-only type can be CO_RETURNed from the argument",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, unique(_)).CO_RETURN(std::move(_1));
auto p = m.unique(std::make_unique<int>(3));
iptr x;
std::invoke([&]() -> coro::task<void> { x = co_await p; });
REQUIRE(x);
REQUIRE(*x == 3);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"SIDE_EFFECT runs on the call to co-routine function, not when the coro runs",
"[coro]")
{
co_mock m;
int x = 0;
int y = 0;
REQUIRE_CALL(m, intret()).LR_SIDE_EFFECT(x=1).CO_RETURN(5);
auto p = m.intret();
REQUIRE(x == 1);
REQUIRE(y == 0);
x = 0;
std::invoke([&]() -> coro::task<void> { y = co_await p;});
REQUIRE(x == 0);
REQUIRE(y == 5);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"CO_YIELDed values are co_await:ed in order with CO_RETURN last",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, intret()).CO_YIELD(1).CO_YIELD(2).CO_RETURN(0);
auto p = m.intret();
int v = 0;
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 1);
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 2);
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 0);
REQUIRE(reports.empty());
}
TEST_CASE_METHOD(
Fixture,
"Empty CO_RETURN can end a generator with yield and return_void",
"[coro]")
{
co_mock m;
REQUIRE_CALL(m, gen())
.CO_YIELD(5)
.CO_YIELD(8)
.CO_YIELD(3)
.CO_RETURN();
auto p = m.gen();
int v = 0;
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 5);
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 8);
std::invoke([&]() -> coro::task<void> { v = co_await p;});
REQUIRE(v == 3);
REQUIRE(reports.empty());
}
#endif