| // 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 |