blob: 86010ff7dc632965a7c5c4cbe408f6763953d731 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fasync/future.h>
#include <lib/zx/status.h>
#include <deque>
#include <initializer_list>
#include <iostream>
#include <iterator>
#include <list>
#include <numeric>
#include <set>
#include <string>
#include <type_traits>
#include <zxtest/zxtest.h>
#include "lib/stdcompat/tuple.h"
#include "lib/stdcompat/type_traits.h"
#include "test_utils.h"
using namespace std::literals;
namespace {
struct aggregate {
int a;
int b;
};
TEST(FutureTests, InvokeHandler) {
fasync::testing::immediate_executor executor;
fasync::context& context = executor.context();
fasync::internal::invoke_handler(
[](std::pair<int, int> p) {
EXPECT_EQ(p.first, 1);
EXPECT_EQ(p.second, 2);
},
context, 1, 2);
fasync::internal::invoke_handler(
[](std::pair<int, std::string> p) {
EXPECT_EQ(p.first, 1);
EXPECT_STREQ(p.second, "asdf");
},
context, 1, "asdf");
fasync::internal::invoke_handler(
[](std::tuple<int, int> t) {
EXPECT_EQ(std::get<0>(t), 1);
EXPECT_EQ(std::get<1>(t), 2);
},
context, 1, 2);
fasync::internal::invoke_handler(
[](std::array<int, 2> a) {
EXPECT_EQ(a[0], 1);
EXPECT_EQ(a[1], 2);
},
context, 1, 2);
fasync::internal::invoke_handler(
[](aggregate a) {
EXPECT_EQ(a.a, 1);
EXPECT_EQ(a.b, 2);
},
context, 1, 2);
}
TEST(FutureTests, Map) {
{
[[maybe_unused]] auto pipe =
fasync::make_value_future<int>(2) | fasync::map([](int& i) { return i + 1; });
}
{
[[maybe_unused]] auto result_pipe =
fasync::make_try_future<int, int>(fitx::success(3)) |
fasync::map_ok([](int i) { return fitx::ok(std::to_string(i)); }) |
fasync::map_error([](int i) { return fitx::as_error(std::to_string(i) + " error"); });
using result_pipe_t = decltype(result_pipe);
static_assert(std::is_same_v<fasync::future_value_t<result_pipe_t>, std::string>);
static_assert(std::is_same_v<fasync::future_error_t<result_pipe_t>, std::string>);
}
{
// You don't have to immediately execute the pipeline; you can store it and move to the executor
// later.
// We'll do regular int for one since int is copyable.
auto pipe = fasync::make_value_future(2) | fasync::map([](int& i) { return i + 1; }) |
fasync::map([](int& i) { return i + 2; }) |
fasync::map([](int i) { return i + 3; }) |
fasync::inspect([](int i) { EXPECT_EQ(i, 8); });
EXPECT_EQ(std::move(pipe) | fasync::testing::invoke, 8);
}
{
auto pipe = fasync::make_value_future(27) |
fasync::map([](fasync::context& context, int i) { return i + 1; });
EXPECT_EQ(std::move(pipe) | fasync::testing::invoke, 28);
}
{
// Calling with an lvalue is kind of uncommon
auto pipe = fasync::make_value_future(23);
int result = pipe | fasync::map([](int i) { return i - 13; }) | fasync::testing::invoke;
EXPECT_EQ(result, 10);
auto mod = fasync::map([](int i) { return i % 7; });
result = pipe | mod | fasync::testing::invoke;
EXPECT_EQ(result, 2);
}
}
TEST(FutureTests, MapHandlers) {
{
auto x = fasync::make_value_future(0) | fasync::map([](fasync::context&) { return 42; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([](int i) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([](int& i) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([](auto i) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([](auto& i) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([](auto&& i) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) |
fasync::map([](fasync::context&, int i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) |
fasync::map([](fasync::context&, int& i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) |
fasync::map([](fasync::context&, auto i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) |
fasync::map([](fasync::context&, auto& i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(42) |
fasync::map([](fasync::context&, auto&& i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](std::tuple<int, int, int> t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](std::tuple<int, int, int>& t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](auto& t) { return std::get<0>(t) + std::get<1>(t) + std::get<2>(t); }) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x =
fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](auto&& t) { return std::get<0>(t) + std::get<1>(t) + std::get<2>(t); }) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, std::tuple<int, int, int> t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, std::tuple<int, int, int>& t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, auto& t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, auto&& t) {
return std::get<0>(t) + std::get<1>(t) + std::get<2>(t);
}) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](int i, int j, int k) { return i + j + k; }) | fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](int& i, int j, int k) { return i + j + k; }) | fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, int i, int j, int k) { return i + j + k; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, int& i, int j, int k) { return i + j + k; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 3);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, int i, auto...) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 0);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, int i, auto&...) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 0);
}
{
auto x = fasync::make_value_future(std::make_tuple(0, 1, 2)) |
fasync::map([](fasync::context&, int i, auto&&...) { return i; }) |
fasync::testing::invoke;
EXPECT_EQ(x, 0);
}
// Needs single_threaded_executor.h (coming)
#if 0
{
auto x =
fasync::make_value_future(0) |
fasync::map([slept = false](fasync::context& context, int i) mutable -> fasync::poll<int> {
if (!slept) {
slept = true;
context.suspend_task().resume();
return fasync::pending();
}
return fasync::done(i);
}) |
fasync::block;
EXPECT_EQ(x.value(), 0);
}
#endif
}
TEST(FutureTests, MapReturnTypes) {
{
// void
fasync::make_value_future(42) | fasync::map([] {}) | fasync::testing::invoke;
}
{
auto x =
fasync::make_value_future(0) | fasync::map([] { return 42; }) | fasync::testing::invoke;
EXPECT_EQ(x, 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map([]() -> fitx::result<int, std::string> { return fitx::ok("asdf"s); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
{
auto x = fasync::make_value_future(0) | fasync::map([] { return fitx::ok(42); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_value_future(0) | fasync::map([] { return fitx::as_error(42); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_value_future(42) | fasync::map([] { return fasync::pending(); }) |
fasync::testing::poll;
EXPECT_EQ(x, fasync::pending());
}
{
auto x = fasync::make_value_future(0) | fasync::map([] { return fasync::done(fitx::ok(42)); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x =
fasync::make_value_future(0) |
fasync::map([]() -> fasync::try_ready<int, int> { return fasync::done(fitx::ok(42)); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x =
fasync::make_value_future(0) |
fasync::map([]() -> fasync::try_poll<int, int> { return fasync::done(fitx::ok(42)); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
}
TEST(FutureTests, MapOkHandlers) {
{
auto x = fasync::make_ok_future(0) |
fasync::map_ok([](fasync::context&) { return fitx::ok(42); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([](int i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([](int& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([](auto i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([](auto& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([](auto&& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([](fasync::context&, int i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([](fasync::context&, int& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([](fasync::context&, auto i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([](fasync::context&, auto& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([](fasync::context&, auto&& i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](std::tuple<int, int, int> t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](std::tuple<int, int, int>& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](auto& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](auto&& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, std::tuple<int, int, int> t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, std::tuple<int, int, int>& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, auto& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, auto&& t) {
return fitx::ok(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](int i, int j, int k) { return fitx::ok(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](int& i, int j, int k) { return fitx::ok(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x =
fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, int i, int j, int k) { return fitx::ok(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x =
fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, int& i, int j, int k) { return fitx::ok(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 3);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, int i, auto...) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 0);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, int i, auto&...) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 0);
}
{
auto x = fasync::make_try_future<int, std::tuple<int, int, int>>(
fitx::ok(std::make_tuple(0, 1, 2))) |
fasync::map_ok([](fasync::context&, int i, auto&&...) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 0);
}
{
auto x = fasync::make_try_future<int, int>(fitx::ok(42)) |
fasync::map_ok([](int i) -> fitx::result<int, int> { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 42);
}
{
auto x =
fasync::make_try_future<int, int>(fitx::ok(42)) |
fasync::map_ok([](int i) -> fitx::result<int, std::string> { return fitx::ok("asdf"); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
// Needs single_threaded_executor.h (coming)
#if 0
{
auto x = fasync::make_try_future<int, int>(fitx::ok(0)) |
fasync::map_ok([slept = false](fasync::context& context,
int i) mutable -> fasync::try_poll<int, int> {
if (!slept) {
slept = true;
context.suspend_task().resume();
return fasync::pending();
}
return fasync::done(fitx::ok(i));
}) |
fasync::block;
EXPECT_EQ(x.value().value(), 0);
}
#endif
}
TEST(FutureTests, MapOkReturnTypes) {
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([] {}) | fasync::testing::invoke;
EXPECT_TRUE(x.is_ok());
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok(
[]() -> fitx::result<fitx::failed, std::string> { return fitx::ok("asdf"s); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([] { return fitx::ok("asdf"s); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([] { return fitx::failed(); }) |
fasync::testing::invoke;
EXPECT_TRUE(x.is_error());
}
{
auto x = fasync::make_ok_future(42) | fasync::map_ok([] { return fasync::pending(); }) |
fasync::testing::poll;
EXPECT_EQ(x, fasync::pending());
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([]() -> fasync::try_ready<fitx::failed, std::string> {
return fasync::done(fitx::ok("asdf"s));
}) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
{
auto x = fasync::make_ok_future(42) |
fasync::map_ok([]() -> fasync::try_poll<fitx::failed, std::string> {
return fasync::done(fitx::ok("asdf"s));
}) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "asdf");
}
}
TEST(FutureTests, MapErrorHandlers) {
{
auto x = fasync::make_error_future(0) |
fasync::map_error([](fasync::context&) { return fitx::as_error(42); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](int i) { return fitx::as_error(i); }) | fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](int& i) { return fitx::as_error(i); }) | fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](auto i) { return fitx::as_error(i); }) | fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](auto& i) { return fitx::as_error(i); }) | fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](auto&& i) { return fitx::ok(); }) | fasync::testing::invoke;
EXPECT_TRUE(x.is_ok());
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](auto&& i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](fasync::context&, int i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](fasync::context&, int& i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](fasync::context&, auto i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](fasync::context&, auto& i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([](fasync::context&, auto&& i) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](std::tuple<int, int, int> t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](std::tuple<int, int, int>& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](auto& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](auto&& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, std::tuple<int, int, int> t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, std::tuple<int, int, int>& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, auto& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, auto&& t) {
return fitx::as_error(std::get<0>(t) + std::get<1>(t) + std::get<2>(t));
}) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](int i, int j, int k) { return fitx::as_error(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](int& i, int j, int k) { return fitx::as_error(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error(
[](fasync::context&, int i, int j, int k) { return fitx::as_error(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error(
[](fasync::context&, int& i, int j, int k) { return fitx::as_error(i + j + k); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 3);
}
{
auto x = fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, int i, auto...) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 0);
}
{
auto x =
fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, int i, auto&...) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 0);
}
{
auto x =
fasync::make_try_future<std::tuple<int, int, int>>(
fitx::as_error(std::make_tuple(0, 1, 2))) |
fasync::map_error([](fasync::context&, int i, auto&&...) { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 0);
}
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(42)) |
fasync::map_error([](int i) -> fitx::result<int, int> { return fitx::as_error(i); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 42);
}
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(42)) |
fasync::map_error(
[](int i) -> fitx::result<std::string, int> { return fitx::as_error("asdf"); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "asdf");
}
// Needs single_threaded_executor.h (coming)
#if 0
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(0)) |
fasync::map_error([slept = false](fasync::context& context,
int i) mutable -> fasync::try_poll<int, int> {
if (!slept) {
slept = true;
context.suspend_task().resume();
return fasync::pending();
}
return fasync::done(fitx::as_error(i));
}) |
fasync::block;
EXPECT_EQ(x.value().error_value(), 0);
}
#endif
}
TEST(FutureTests, MapErrorReturnTypes) {
{
auto x = fasync::make_error_future(42) | fasync::map_error([] {}) | fasync::testing::invoke;
EXPECT_TRUE(x.is_ok());
}
{
auto x =
fasync::make_error_future(42) |
fasync::map_error([]() -> fitx::result<std::string> { return fitx::as_error("asdf"s); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "asdf");
}
{
auto x = fasync::make_error_future(42) | fasync::map_error([] { return fitx::ok(); }) |
fasync::testing::invoke;
EXPECT_TRUE(x.is_ok());
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([] { return fitx::as_error("asdf"s); }) | fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "asdf");
}
{
auto x = fasync::make_error_future(42) | fasync::map_error([] { return fasync::pending(); }) |
fasync::testing::poll;
EXPECT_EQ(x, fasync::pending());
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([]() -> fasync::try_ready<std::string> {
return fasync::done(fitx::as_error("asdf"s));
}) |
fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "asdf");
}
{
auto x = fasync::make_error_future(42) |
fasync::map_error([]() -> fasync::try_poll<std::string> {
return fasync::done(fitx::as_error("asdf"s));
}) |
fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "asdf");
}
}
TEST(FutureTests, MapOkError) {
{
auto x = fasync::make_try_future<int, int>(fitx::ok(42)) |
fasync::map_ok([](int i) { return fitx::ok(i + 1); }) |
fasync::map_error([](int i) { return fitx::as_error(i - 1); }) |
fasync::testing::invoke;
EXPECT_EQ(x.value(), 43);
}
{
auto x = fasync::make_try_future<int, int>(fitx::ok(42)) |
fasync::map_error([](int i) { return fitx::as_error(i - 1); }) |
fasync::map_ok([](int i) { return fitx::ok(i + 1); }) | fasync::testing::invoke;
EXPECT_EQ(x.value(), 43);
}
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(42)) |
fasync::map_ok([](int i) { return fitx::ok(i + 1); }) |
fasync::map_error([](int i) { return fitx::as_error(i - 1); }) |
fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 41);
}
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(42)) |
fasync::map_error([](int i) { return fitx::as_error(i - 1); }) |
fasync::map_ok([](int i) { return fitx::ok(i + 1); }) | fasync::testing::invoke;
EXPECT_EQ(x.error_value(), 41);
}
{
auto x = fasync::make_try_future<int, int>(fitx::ok(42)) |
fasync::map_ok([](int i) { return fitx::ok(std::to_string(i + 1)); }) |
fasync::map_error([](int i) { return fitx::as_error(i - 1); }) |
fasync::map_ok([](std::string& s) { return fitx::ok(s + "asdf"); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.value(), "43asdf");
}
{
auto x = fasync::make_try_future<int, int>(fitx::as_error(42)) |
fasync::map_error([](int i) { return fitx::as_error(std::to_string(i - 1)); }) |
fasync::map_ok([](int i) { return fitx::ok(std::to_string(i + 1)); }) |
fasync::map_error([](std::string& s) { return fitx::as_error(s + "jkl"); }) |
fasync::testing::invoke;
EXPECT_STREQ(x.error_value(), "41jkl");
}
}
TEST(FutureTests, Inspect) {
{
fitx::result result = fasync::make_try_future<int, int>(fitx::error(1)) |
fasync::inspect_ok([](const int& i) { FAIL(); }) |
fasync::inspect_error([](const int& i) { EXPECT_EQ(i, 1); }) |
fasync::testing::invoke;
EXPECT_EQ(result, (fitx::result<int, int>(fitx::error(1))));
}
}
template <size_t Levels, typename T,
fasync::internal::requires_conditions<cpp17::bool_constant<Levels == 0>> = true>
constexpr auto nest_futures(T&& value) {
return fasync::make_value_future(std::forward<T>(value));
}
template <size_t Levels, typename T,
fasync::internal::requires_conditions<cpp17::bool_constant<Levels != 0>> = true>
constexpr auto nest_futures(T&& value) {
return [value = std::move(value)](fasync::context&) mutable
-> fasync::poll<decltype(nest_futures<Levels - 1>(std::move(value)))> {
return fasync::ready(nest_futures<Levels - 1>(std::move(value)));
};
}
TEST(FutureTests, Flatten) {
{
auto unnested = nest_futures<0>(28);
static_assert(fasync::is_future_v<decltype(unnested)>);
EXPECT_EQ(fasync::testing::invoke(unnested), 28);
}
{
auto nested = nest_futures<2>(std::string_view("asdfjkl"));
auto flattened = nested | fasync::flatten | fasync::flatten;
static_assert(fasync::is_future_v<decltype(nested)>);
static_assert(fasync::is_future_v<decltype(flattened)>);
EXPECT_STREQ(fasync::testing::invoke(flattened), std::string_view("asdfjkl"));
}
}
TEST(FutureTests, FlattenAll) {
{
auto unnested = nest_futures<0>(28);
auto flattened = fasync::flatten_all(unnested);
static_assert(fasync::is_future_v<decltype(unnested)>);
static_assert(fasync::is_future_v<decltype(flattened)>);
EXPECT_EQ(fasync::testing::invoke(flattened), 28);
}
{
auto nested = nest_futures<10>(std::string_view("asdfjkl"));
auto flattened = nested | fasync::flatten_all;
static_assert(fasync::is_future_v<decltype(nested)>);
static_assert(fasync::is_future_v<decltype(flattened)>);
EXPECT_STREQ(fasync::testing::invoke(flattened), std::string_view("asdfjkl"));
}
}
TEST(FutureTests, Then) {
{
auto i = fasync::make_value_future(9) |
fasync::then([] { return fasync::make_value_future(10); }) | fasync::testing::invoke;
EXPECT_EQ(i, 10);
}
{
int i = fasync::make_value_future(0) |
fasync::then([](int i) { return fasync::make_value_future(i + 1); }) |
fasync::testing::invoke;
EXPECT_EQ(i, 1);
}
{
fasync::make_value_future(0) |
fasync::then([](int i) { return fasync::make_value_future(i + 1); }) |
fasync::then([](int i) { return i + 1; }) | fasync::then([] { return 3; }) |
fasync::then([] {}) | fasync::then([] {}) | fasync::testing::invoke;
}
{
fitx::result result = fasync::make_try_future<int, int>(fitx::error(1)) |
fasync::and_then([](int i) { return fitx::ok(2); }) |
fasync::or_else([](int i) { return fitx::as_error(3); }) |
fasync::testing::invoke;
EXPECT_EQ(result.error_value(), 3);
}
{
fitx::result result =
fasync::make_try_future<std::string, std::string>(fitx::ok("asdf"s)) |
fasync::and_then([](auto&& str) { return fitx::ok(std::make_tuple(str[0], str[1])); }) |
fasync::or_else([] {
ADD_FAILURE("Shouldn't be called; also can't return void.");
return fitx::as_error(nullptr);
}) |
fasync::and_then([](aggregate a) { return fitx::ok(a); }) | fasync::testing::invoke;
EXPECT_EQ(result->a, 'a');
EXPECT_EQ(result->b, 's');
}
{
auto result = fasync::make_try_future<int>(fitx::error(2)) | fasync::and_then([] {
ADD_FAILURE("and_then shouldn't be called here.");
return fitx::ok(""s);
}) |
fasync::or_else([](auto i) { return fitx::as_error(std::to_string(i)); }) |
fasync::then([](auto result) -> decltype(result) {
EXPECT_TRUE(result.is_error());
return fitx::ok(result.error_value() + "asdf");
}) |
fasync::testing::invoke;
// This is a different result type than the one we started with
static_assert(std::is_same_v<decltype(result), fitx::result<std::string, std::string>>);
EXPECT_STREQ(result.value(), "2asdf");
}
{
constexpr auto returns_future = [] {
return fasync::make_try_future<int, int>(fitx::error(1));
};
fitx::result<std::string, int> f =
returns_future() | fasync::or_else([](auto i) { return fitx::error(std::to_string(i)); }) |
fasync::testing::invoke;
EXPECT_TRUE(f.is_error());
EXPECT_STREQ(f.error_value(), "1");
fitx::result result = returns_future() | fasync::or_else(returns_future) |
fasync::or_else([](int i) { return fitx::error(i); }) |
fasync::or_else([](int i) { return fitx::ok(i); }) |
fasync::testing::invoke;
EXPECT_TRUE(result.is_ok());
EXPECT_EQ(result.value(), 1);
}
// Invoke a pre-composed pipeline, initially without a future.
{
auto f = fasync::make_ok_future();
auto pipeline = fasync::and_then([] {}) | fasync::or_else([] { ADD_FAILURE(); });
auto x = f | pipeline | fasync::testing::invoke;
EXPECT_TRUE(x.is_ok());
}
}
TEST(FutureTests, Join) {
{
std::tuple joined =
fasync::make_value_future(1) |
fasync::join_with(fasync::make_value_future(2), fasync::make_value_future(3)) |
fasync::testing::invoke;
EXPECT_EQ(joined, std::tuple(1, 2, 3));
}
{
int joined = fasync::join(fasync::make_value_future(0), fasync::make_value_future(1),
fasync::make_value_future(2)) |
fasync::then([](int i, int j, int k) { return i + j + k; }) |
fasync::testing::invoke;
EXPECT_EQ(joined, 3);
}
{
auto str = fasync::join(fasync::make_value_future("asdf"s), fasync::make_value_future("jkl"s),
fasync::make_value_future(0)) |
fasync::then([](auto&& str1, auto&& str2, int num) {
return str1 + str2 + std::to_string(num);
}) |
fasync::testing::invoke;
static_assert(std::is_same_v<decltype(str), std::string>);
EXPECT_STREQ(str, "asdfjkl0");
}
{
int n = fasync::make_value_future(std::make_tuple(fasync::make_value_future(1),
fasync::make_value_future(2),
fasync::make_value_future(3))) |
fasync::join | fasync::then([](int a, int b, int c) { return a + b + c; }) |
fasync::testing::invoke;
EXPECT_EQ(n, 6);
}
{
int j =
fasync::make_value_future("asdf"sv) |
fasync::join_with(fasync::make_value_future("jkl"sv), fasync::make_value_future(0)) |
fasync::then([](auto&& str1, auto&& str2, int n) {
EXPECT_EQ(n, 0);
// static_assert(cpp17::is_same_v<decltype(str1), std::string_view&>, "");
// static_assert(cpp17::is_same_v<decltype(str2), std::string_view&>, "");
return std::array{1, 2, 3, 4, 5};
}) |
fasync::then([](int i1, int i2, int i3, int i4, int i5) {
return std::array{i1, i1 * i2, i1 * i2 * i3, i1 * i2 * i3 * i4, i1 * i2 * i3 * i4 * i5};
}) |
fasync::then([](std::array<int, 5> arr) { return std::reduce(arr.begin(), arr.end()); }) |
fasync::then([](auto i) { return i; }) | fasync::testing::invoke;
EXPECT_EQ(j, 1 + 2 + 6 + 24 + 120); // adding up factorials
}
struct agg {
std::string str1;
std::string str2;
size_t s;
};
{
constexpr auto test = [](auto... is) -> std::array<size_t, sizeof...(is)> {
return std::array{is...};
};
constexpr auto test_ctad = [](auto... is) { return std::array{is...}; };
using arg = std::tuple<size_t, size_t, size_t>;
using ret = std::array<size_t, 3>;
static_assert(::fasync::internal::is_invocable_handler_internal_v<decltype(test), arg>);
static_assert(::fasync::internal::is_invocable_handler_internal_v<decltype(test_ctad), arg>);
static_assert(::fasync::internal::is_invocable_with_applicable_v<decltype(test), arg>);
static_assert(::fasync::internal::is_invocable_with_applicable_v<decltype(test_ctad), arg>);
static_assert(
cpp17::is_same_v<cpp17::invoke_result_t<decltype(test), size_t, size_t, size_t>, ret>);
static_assert(
cpp17::is_same_v<cpp17::invoke_result_t<decltype(test_ctad), size_t, size_t, size_t>, ret>);
static_assert(
cpp17::is_same_v<
decltype(::fasync::internal::invoke_handler_internal(test, std::declval<arg>())), ret>);
static_assert(cpp17::is_same_v<decltype(::fasync::internal::invoke_handler_internal(
test_ctad, std::declval<arg>())),
ret>);
int i = fasync::make_value_future("asdf"s) |
fasync::join_with(fasync::make_value_future("jkl"s), fasync::make_value_future(4u)) |
fasync::then([](agg&& a) {
EXPECT_STREQ(a.str1, "asdf");
EXPECT_STREQ(a.str2, "jkl");
EXPECT_EQ(a.s, 4);
return fasync::make_value_future(std::tuple(a.str1.size(), a.str2.size(), a.s));
}) |
fasync::then([](auto... is) { return std::array{is...}; }) | fasync::then(test_ctad) |
fasync::then(
[](std::array<size_t, 3> arr) { return std::reduce(arr.begin(), arr.end()); }) |
// TODO(schottm): figure out why this is necessary
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
fasync::then([](auto& i) { return i; }) | fasync::testing::invoke;
#pragma GCC diagnostic pop
EXPECT_EQ(i, 11);
}
{
std::vector<int> joined_vec =
fasync::join(fasync::make_value_future(1), fasync::make_value_future(2),
fasync::make_value_future(3)) |
fasync::then([](std::vector<int> vec) {
vec.push_back(4);
return vec;
}) |
fasync::testing::invoke;
EXPECT_EQ(joined_vec.size(), 4);
}
}
template <template <typename, typename> class C>
void test_join_sequence_container() {
C in = {fasync::make_value_future(0), fasync::make_value_future(1), fasync::make_value_future(2)};
using value_type = fasync::future_output_t<typename decltype(in)::value_type>;
using allocator_type = std::allocator<value_type>;
C<value_type, allocator_type> out = fasync::join(std::move(in)) | fasync::then([](auto&& v) {
v.push_back(3);
return std::move(v);
}) |
fasync::testing::invoke;
EXPECT_EQ(out.size(), 4);
EXPECT_EQ(out.front(), 0);
EXPECT_EQ(out.back(), 3);
}
template <template <typename, typename> class C>
void test_join_container_and_remove() {
C in = {fasync::make_try_future<int, int>(fitx::error(0)),
fasync::make_try_future<int, int>(fitx::ok(1)),
fasync::make_try_future<int, int>(fitx::ok(2))};
using value_type = fasync::future_output_t<typename decltype(in)::value_type>;
using allocator_type = std::allocator<value_type>;
C<value_type, allocator_type> out =
fasync::join(std::move(in)) | fasync::then([](auto&& v) {
// std::erase_if is C++20 so we have to do it manually
auto it = std::remove_if(std::begin(v), std::end(v),
[](auto&& result) { return result.is_error(); });
v.erase(it, std::end(v));
return std::move(v);
}) |
fasync::testing::invoke;
EXPECT_EQ(std::size(out), 2);
EXPECT_EQ(std::begin(out)->value(), 1);
EXPECT_EQ(std::prev(std::end(out))->value(), 2);
}
TEST(FutureTests, JoinContainer) {
test_join_sequence_container<std::vector>();
test_join_sequence_container<std::deque>();
test_join_sequence_container<std::list>();
test_join_container_and_remove<std::vector>();
test_join_container_and_remove<std::deque>();
test_join_container_and_remove<std::list>();
fasync::try_future<int, int> arr[] = {fasync::make_try_future<int, int>(fitx::error(0)),
fasync::make_try_future<int, int>(fitx::ok(1)),
fasync::make_try_future<int, int>(fitx::ok(2))};
auto a = fasync::join(std::move(arr)) | fasync::testing::invoke;
EXPECT_TRUE(a[0].is_error());
std::array stdarr = {fasync::make_try_future<int, int>(fitx::error(0)),
fasync::make_try_future<int, int>(fitx::ok(1)),
fasync::make_try_future<int, int>(fitx::ok(2))};
auto b = fasync::join(std::move(stdarr)) | fasync::testing::invoke;
EXPECT_TRUE(b[0].is_error());
cpp20::span<typename decltype(stdarr)::value_type, 3> static_span = stdarr;
auto d = fasync::join(static_span) | fasync::testing::invoke;
EXPECT_TRUE(d[0].is_error());
}
} // namespace