blob: 8c54f21d23234d3df142b059b5e88381eada2f95 [file] [log] [blame]
#include <lib/async/cpp/future.h>
#include <string>
#include <lib/async-testutils/test_loop.h>
#include <src/lib/fxl/logging.h>
#include "gtest/gtest-spi.h"
#include "gtest/gtest.h"
#include <memory>
#include <string>
namespace modular {
namespace {
// https://www.reddit.com/r/Stargate/comments/114165/stargate_sg_1_episode_200_what_is_your_favorite/c6j8ryy
// https://en.wikiquote.org/wiki/Stargate_SG-1/Season_10#200_[10.6]
// (The actual duration is immaterial because we control time in unit tests.)
constexpr zx::duration kPause = zx::min(38);
// This is a simple class to enable asynchronous callbacks to be tested. In each
// test:
//
// 1. Instantiate an AsyncExceptions object,
// 2. Call Signal() in each async callback to indicate that an async callback
// was successfully called. Note that doesn't mean that the callback itself
// was passed the correct parameters or was successful: to ensure that, your
// callback should call still EXPECT_* for its expected results.
// 3. Call Verify(n) at the end of your test method to ensure that the number of
// expected async callbacks is correct. |n| should be the number of Signal()
// calls that were expected. Verify() will fail if Signal() hasn't been
// called exactly |n| times.
//
// Note that AsyncExpectations does _not_ integrate with the message loop, since
// its one use case has no need to do that.
//
// THIS CLASS IS NOT THREAD-SAFE.
//
// Example:
//
// TEST(FooTest, FooTestCase) {
// AsyncExpectations async_expectations;
// async_function_1([&] {
// async_expectations.Signal();
// });
// async_function_2([&] {
// async_expectations.Signal();
// });
// async_expectations.Verify(2);
// }
class AsyncExpectations {
public:
void Signal() { ++count_; }
void Reset() { count_ = 0; }
int count() const { return count_; }
private:
int count_ = 0;
};
TEST(AsyncExpectationTest, TestLessThanExpectedCountFails) {
AsyncExpectations async_expectations;
async_expectations.Signal();
EXPECT_NONFATAL_FAILURE({ EXPECT_EQ(2, async_expectations.count()); },
"Expected equality");
}
TEST(AsyncExpectationTest, TestExpectedCountSucceeds) {
AsyncExpectations async_expectations;
async_expectations.Signal();
EXPECT_EQ(1, async_expectations.count());
}
TEST(AsyncExpectationTest, TestMoreThanExpectedCountFails) {
AsyncExpectations async_expectations;
async_expectations.Signal();
async_expectations.Signal();
EXPECT_NONFATAL_FAILURE({ EXPECT_EQ(1, async_expectations.count()); },
"Expected equality");
}
} // namespace
// FutureTest must be in the ::modular namespace (not in an anonymous namespace)
// so that the "friend class FutureTest;" declaration in the Future class can
// properly befriend this test.
class FutureTest : public ::testing::Test {
protected:
template <typename... Result>
const std::tuple<Result...>& get(const FuturePtr<Result...>& future) const {
return future->get();
}
template <typename... Result>
fxl::WeakPtrFactory<Future<Result...>>& weak_factory(
const FuturePtr<Result...>& future) const {
return future->weak_factory_;
}
};
namespace {
TEST_F(FutureTest, Create) {
auto f = Future<>::Create(__PRETTY_FUNCTION__);
EXPECT_NE(f.get(), nullptr);
}
TEST_F(FutureTest, CompleteAndGet) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
f->Complete(10);
ASSERT_EQ(get(f), std::tuple<int>(10));
}
TEST_F(FutureTest, ThenWith0Argument) {
auto f = Future<>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Then([&]() { async_expectations.Signal(); });
f->Complete();
ASSERT_EQ(get(f), std::tuple<>());
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, ThenWith1Argument) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Then([&](const int& value) {
EXPECT_EQ(value, 10);
async_expectations.Signal();
});
f->Complete(10);
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, ThenWith2Argument) {
auto f = Future<int, float>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Then([&](const int& i, const float& f) {
EXPECT_EQ(i, 10);
EXPECT_FLOAT_EQ(f, 3.14f);
async_expectations.Signal();
});
f->Complete(10, 3.14f);
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, CompleteBeforeThen) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
f->Complete(10);
AsyncExpectations async_expectations;
f->Then([&](const int& value) {
EXPECT_EQ(value, 10);
async_expectations.Signal();
});
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, ThenCorrectlyMoves) {
std::shared_ptr<int> p(new int(10));
auto f = Future<std::shared_ptr<int>>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Then([&](std::shared_ptr<int> p2) {
EXPECT_EQ(p.get(), nullptr);
EXPECT_NE(p2.get(), nullptr);
EXPECT_EQ(*p2, 10);
async_expectations.Signal();
});
f->Complete(std::move(p));
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, ConstThen) {
std::shared_ptr<int> p(new int(10));
auto f = Future<std::shared_ptr<int>>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->ConstThen([&](const std::shared_ptr<int>& p2) {
EXPECT_NE(p2.get(), nullptr);
EXPECT_EQ(p.get(), nullptr);
EXPECT_EQ(*p2, 10);
async_expectations.Signal();
});
f->ConstThen([&](const std::shared_ptr<int>& p3) {
EXPECT_NE(p3.get(), nullptr);
EXPECT_EQ(p.get(), nullptr);
EXPECT_EQ(*p3, 10);
async_expectations.Signal();
});
f->Complete(std::move(p));
EXPECT_EQ(2, async_expectations.count());
}
TEST_F(FutureTest, ConstThenAfterComplete) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
bool first_const_then{};
f->ConstThen([&](const int& i) {
EXPECT_FALSE(first_const_then);
first_const_then = true;
async_expectations.Signal();
});
f->Complete(10);
bool second_const_then{};
f->ConstThen([&](const int& i) {
EXPECT_FALSE(second_const_then);
second_const_then = true;
async_expectations.Signal();
});
EXPECT_EQ(2, async_expectations.count());
}
TEST_F(FutureTest, ConstThenAfterThen) {
std::shared_ptr<int> p(new int(10));
auto f = Future<std::shared_ptr<int>>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->ConstThen([&](const std::shared_ptr<int>& p2) {
EXPECT_NE(p2.get(), nullptr);
EXPECT_EQ(p.get(), nullptr);
EXPECT_EQ(*p2, 10);
async_expectations.Signal();
});
f->Then([&](std::shared_ptr<int> p4) {
EXPECT_NE(p4.get(), nullptr);
EXPECT_EQ(p.get(), nullptr);
EXPECT_EQ(*p4, 10);
async_expectations.Signal();
});
f->ConstThen([&](const std::shared_ptr<int>& p3) {
EXPECT_NE(p3.get(), nullptr);
EXPECT_EQ(p.get(), nullptr);
EXPECT_EQ(*p3, 10);
async_expectations.Signal();
});
f->Complete(std::move(p));
EXPECT_EQ(3, async_expectations.count());
}
TEST_F(FutureTest, WeakThen) {
// WeakThen() will break an execution chain if its weakptr is invalidated.
int data = 0;
fxl::WeakPtrFactory<int> factory(&data);
AsyncExpectations async_expectations;
// This time we expect all three to run because we haven't invalidated any
// WeakPtrs.
auto f =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
f->Then([&] { async_expectations.Signal(); })
->WeakThen(factory.GetWeakPtr(), [&] { async_expectations.Signal(); })
->Then([&] { async_expectations.Signal(); });
f->Complete();
EXPECT_EQ(3, async_expectations.count());
// But this time we'll invalidate WeakPtrs in the first Then(). Only the first
// Then() will run.
async_expectations.Reset();
f = Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("2"));
f->Then([&] {
async_expectations.Signal();
factory.InvalidateWeakPtrs();
})
->WeakThen(factory.GetWeakPtr(), [&] { async_expectations.Signal(); })
->Then([&] { async_expectations.Signal(); });
f->Complete();
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WeakConstThen) {
// WeakConstThen() will not run if its weakptr is invalidated.
int data = 0;
fxl::WeakPtrFactory<int> factory(&data);
AsyncExpectations async_expectations;
auto f =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
f->WeakConstThen(factory.GetWeakPtr(), [&] { async_expectations.Signal(); });
f->Complete();
EXPECT_EQ(1, async_expectations.count());
async_expectations.Reset();
f = Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("2"));
f->WeakConstThen(factory.GetWeakPtr(), [&] { async_expectations.Signal(); });
factory.InvalidateWeakPtrs();
f->Complete();
EXPECT_EQ(0, async_expectations.count());
}
TEST_F(FutureTest, WeakMap) {
// Similar to WeakThen(), but using Map calls instead.
int data = 0;
fxl::WeakPtrFactory<int> factory(&data);
auto f =
Future<int>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
// First let everything run.
AsyncExpectations async_expectations;
f->Map([&](int i) {
async_expectations.Signal();
return 42;
})
->WeakMap(factory.GetWeakPtr(),
[&](int i) {
async_expectations.Signal();
return 10;
})
->Then([&](int i) { async_expectations.Signal(); });
f->Complete(10);
EXPECT_EQ(3, async_expectations.count());
// Now invalidate it.
f = Future<int>::Create(std::string(__PRETTY_FUNCTION__) + std::string("2"));
async_expectations.Reset();
f->Map([&](int i) {
async_expectations.Signal();
factory.InvalidateWeakPtrs();
return 42;
})
->WeakMap(factory.GetWeakPtr(),
[&](int i) {
async_expectations.Signal();
return 10;
})
->Then([&](int i) { async_expectations.Signal(); });
f->Complete(10);
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WeakAsyncMap) {
// Similar to WeakThen(), but using Map calls instead.
int data = 0;
fxl::WeakPtrFactory<int> factory(&data);
auto f =
Future<int>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
// First let everything run.
AsyncExpectations async_expectations;
f->Map([&](int i) {
async_expectations.Signal();
return 42;
})
->WeakAsyncMap(factory.GetWeakPtr(),
[&](int i) {
async_expectations.Signal();
return Future<int>::CreateCompleted(
std::string(__PRETTY_FUNCTION__) + std::string("2"),
10);
})
->Then([&](int i) { async_expectations.Signal(); });
f->Complete(10);
EXPECT_EQ(3, async_expectations.count());
// Now invalidate it.
f = Future<int>::Create(std::string(__PRETTY_FUNCTION__) + std::string("3"));
async_expectations.Reset();
f->Map([&](int i) {
async_expectations.Signal();
factory.InvalidateWeakPtrs();
return 42;
})
->WeakAsyncMap(factory.GetWeakPtr(),
[&](int i) {
async_expectations.Signal();
return Future<int>::CreateCompleted(
std::string(__PRETTY_FUNCTION__) + std::string("4"),
10);
})
->Then([&](int i) { async_expectations.Signal(); });
f->Complete(10);
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, CreateCompleted) {
auto f = Future<>::CreateCompleted(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Then([&]() { async_expectations.Signal(); });
ASSERT_EQ(get(f), std::tuple<>());
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, Wait) {
auto f1 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("1"));
auto f2 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("2"));
auto f3 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("3"));
AsyncExpectations async_expectations;
auto f =
Wait(std::string(__PRETTY_FUNCTION__) + std::string("4"), {f1, f2, f3});
f->Then([&](std::vector<std::tuple<int, std::string>> v) {
EXPECT_EQ(v.size(), 3ul);
EXPECT_EQ(v[0], std::make_tuple(10, "10"));
EXPECT_EQ(v[1], std::make_tuple(20, "20"));
EXPECT_EQ(v[2], std::make_tuple(30, "30"));
async_expectations.Signal();
});
// Go ahead and insert out of order to also ensure that Wait produces results
// in insertion order rather than completion order.
f2->Complete(20, "20");
EXPECT_EQ(0, async_expectations.count());
f1->Complete(10, "10");
EXPECT_EQ(0, async_expectations.count());
f3->Complete(30, "30");
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitOnZeroFutures) {
auto f = Wait(__PRETTY_FUNCTION__, std::vector<FuturePtr<int>>{});
AsyncExpectations async_expectations;
f->Then([&](std::vector<int> v) {
EXPECT_EQ(v.size(), 0ul);
async_expectations.Signal();
});
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(
FutureTest,
WaitReleasesReturnedFutureIfAllComponentFuturesHaveBeenCompletedOrDestroyed) {
fxl::WeakPtr<Future<std::vector<int>>> weak_f;
auto f1 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("1"));
{
auto f2 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("2"));
weak_f =
weak_factory(Wait(__PRETTY_FUNCTION__ + std::string("Wait"), {f1, f2}))
.GetWeakPtr();
}
ASSERT_TRUE(weak_f);
weak_f->Then([&](std::vector<int>) { FAIL(); });
f1->Complete(42);
EXPECT_FALSE(weak_f);
}
// This test is the happy case, more or less identical to |Wait|.
TEST_F(FutureTest, WaitWithTimeout) {
auto f1 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("1"));
auto f2 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("2"));
auto f3 = Future<int, std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("3"));
AsyncExpectations async_expectations;
async::TestLoop loop;
auto f = WaitWithTimeout(
std::string(__PRETTY_FUNCTION__) + std::string("4"), loop.dispatcher(),
kPause, [](const std::string& msg) { FAIL() << msg; }, {f1, f2, f3});
f->Then([&](std::vector<std::tuple<int, std::string>> v) {
EXPECT_EQ(v.size(), 3ul);
EXPECT_EQ(v[0], std::make_tuple(10, "10"));
EXPECT_EQ(v[1], std::make_tuple(20, "20"));
EXPECT_EQ(v[2], std::make_tuple(30, "30"));
async_expectations.Signal();
});
loop.RunUntilIdle(); // ensure the timeout does not occur
// Go ahead and insert out of order to also ensure that Wait produces results
// in insertion order rather than completion order.
f2->Complete(20, "20");
EXPECT_EQ(0, async_expectations.count());
f1->Complete(10, "10");
EXPECT_EQ(0, async_expectations.count());
f3->Complete(30, "30");
EXPECT_EQ(1, async_expectations.count());
}
// Zero futures should instantly complete regardless of timeout.
TEST_F(FutureTest, WaitWithTimeoutOnZeroFutures) {
AsyncExpectations async_expectations;
async::TestLoop loop;
auto f = WaitWithTimeout(
__PRETTY_FUNCTION__, loop.dispatcher(), {},
[](const std::string& msg) { FAIL() << msg; },
std::vector<FuturePtr<int>>{});
loop.RunUntilIdle();
f->Then([&](std::vector<int> v) {
EXPECT_EQ(v.size(), 0ul);
async_expectations.Signal();
});
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitWithTimeoutCallsCallbackOnTimeout) {
AsyncExpectations async_expectations;
async::TestLoop loop;
auto f1 =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
auto f = WaitWithTimeout(std::string(__PRETTY_FUNCTION__) + std::string("2"),
loop.dispatcher(), {},
[&](const std::string& msg) {
EXPECT_EQ('1', msg[msg.size() - 1]);
async_expectations.Signal();
},
{f1});
loop.RunUntilIdle();
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitWithTimeoutWithDefaultDispatcher) {
AsyncExpectations async_expectations;
async::TestLoop loop;
async_set_default_dispatcher(loop.dispatcher());
auto f1 =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
auto f =
WaitWithTimeout(std::string(__PRETTY_FUNCTION__) + std::string("2"), {},
[&](const std::string& msg) {
EXPECT_EQ('1', msg[msg.size() - 1]);
async_expectations.Signal();
},
{f1});
loop.RunUntilIdle();
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitWithTimeoutDoesNotCompleteAfterTimeout) {
async::TestLoop loop;
auto f1 =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
auto f = WaitWithTimeout(std::string(__PRETTY_FUNCTION__) + std::string("2"),
loop.dispatcher(), {}, [](const auto&) {}, {f1});
f->Then([] { FAIL(); });
loop.RunUntilIdle();
f1->Complete();
}
// The following several tests also test additional Wait functionality, since
// WaitWithTimeout is implemented in terms of Wait.
TEST_F(FutureTest, WaitWithTimeoutOnMoveOnlyType) {
AsyncExpectations async_expectations;
async::TestLoop loop;
auto f1 = Future<std::unique_ptr<int>>::Create(
std::string(__PRETTY_FUNCTION__) + std::string("1"));
auto f = WaitWithTimeout(std::string(__PRETTY_FUNCTION__) + std::string("2"),
loop.dispatcher(), kPause,
[](const std::string& msg) { FAIL() << msg; }, {f1});
f->Then([&](std::vector<std::unique_ptr<int>> v) {
EXPECT_EQ(v.size(), 1u);
EXPECT_EQ(*v[0], 42);
async_expectations.Signal();
});
f1->Complete(std::make_unique<int>(42));
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitWithTimeoutNoTypeStreamlining) {
// This test contains no assertions as it is a compile-time test.
async::TestLoop loop;
auto f1 =
Future<>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
auto f = WaitWithTimeout<Future<std::vector<std::tuple<>>>>(
std::string(__PRETTY_FUNCTION__) + std::string("2"), loop.dispatcher(),
kPause, [](const auto&) {}, {f1});
f->Then([](std::vector<std::tuple<>> v) {});
}
// This tests a specific bug case where some futures started completed and some
// did not. In this bug, WaitWithTimeout added its notification const callbacks
// after invoking Wait. The futures that were completed would invoke the
// callback added by Wait immediately, making the const callbacks then added by
// WaitWithTimeout illegal. This did not manifest when all futures started
// completed due to an optimization in WaitWithTimeout that returns immediately
// if the Wait future starts complete.
TEST_F(FutureTest, WaitWithTimeoutOnPartiallyCompletedFutures) {
AsyncExpectations async_expectations;
async::TestLoop loop;
auto f1 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("1"));
auto f2 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("2"));
f1->Complete(42);
auto f =
WaitWithTimeout(__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), kPause, [](const auto&) {}, {f1, f2})
->Then([&](const std::vector<int>& results) {
ASSERT_EQ(2u, results.size());
EXPECT_EQ(42, results[0]);
EXPECT_EQ(54, results[1]);
async_expectations.Signal();
});
f2->Complete(54); // http://hitchhikers.wikia.com/wiki/42
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, WaitWithTimeoutReleasesFuturesOnCompletion) {
async::TestLoop loop;
fxl::WeakPtr<Future<>> weak_f1;
{
auto f1 = Future<>::Create(__PRETTY_FUNCTION__ + std::string("1"));
weak_f1 = weak_factory(f1).GetWeakPtr();
auto f =
WaitWithTimeout(__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), kPause, [](const auto&) {}, {f1});
f1->Complete();
}
EXPECT_FALSE(weak_f1);
}
TEST_F(FutureTest, WaitWithTimeoutReleasesFuturesOnTimeout) {
async::TestLoop loop;
fxl::WeakPtr<Future<>> weak_f1;
{
auto f1 = Future<>::Create(__PRETTY_FUNCTION__ + std::string("1"));
weak_f1 = weak_factory(f1).GetWeakPtr();
WaitWithTimeout(__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), {}, [](const auto&) {}, {f1});
loop.RunUntilIdle();
}
EXPECT_FALSE(weak_f1);
}
TEST_F(FutureTest,
WaitWithTimeoutCallsCallbackOnTimeoutEvenIfFuturesAreDestroyed) {
AsyncExpectations async_expectations;
async::TestLoop loop;
{
auto f1 = Future<>::Create(__PRETTY_FUNCTION__ + std::string("1"));
WaitWithTimeout(__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), {},
[&](const auto&) { async_expectations.Signal(); }, {f1});
}
loop.RunUntilIdle();
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(
FutureTest,
WaitWithTimeoutReleasesReturnedFutureIfAllComponentFuturesHaveBeenCompletedOrDestroyed) {
async::TestLoop loop;
fxl::WeakPtr<Future<std::vector<int>>> weak_f;
auto f1 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("1"));
{
auto f2 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("2"));
weak_f =
weak_factory(WaitWithTimeout(__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), kPause,
[](const auto&) {}, {f1, f2}))
.GetWeakPtr();
}
ASSERT_TRUE(weak_f);
weak_f->Then([&](std::vector<int>) { FAIL(); });
f1->Complete(42);
EXPECT_FALSE(weak_f);
}
TEST_F(FutureTest, WaitWithTimeoutReleasesReturnedFutureOnTimeout) {
async::TestLoop loop;
fxl::WeakPtr<Future<std::vector<int>>> weak_f;
{
auto f1 = Future<int>::Create(__PRETTY_FUNCTION__ + std::string("1"));
weak_f = weak_factory(WaitWithTimeout(
__PRETTY_FUNCTION__ + std::string("Wait"),
loop.dispatcher(), {}, [](const auto&) {}, {f1}))
.GetWeakPtr();
}
loop.RunUntilIdle();
EXPECT_FALSE(weak_f);
}
TEST_F(FutureTest, Completer) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
auto completer = f->Completer();
completer(5);
AsyncExpectations async_expectations;
f->Then([&](int i) {
EXPECT_EQ(i, 5);
async_expectations.Signal();
});
EXPECT_EQ(1, async_expectations.count());
}
TEST_F(FutureTest, AsyncMap) {
auto future =
Future<int>::Create(std::string(__PRETTY_FUNCTION__) + std::string("1"));
AsyncExpectations async_expectations;
future
->AsyncMap([&](int i) {
EXPECT_EQ(i, 10);
async_expectations.Signal();
auto f = Future<std::string>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("2"));
f->Complete(std::to_string(i * 2));
return f;
})
->AsyncMap([&](std::string s) {
EXPECT_EQ(s, "20");
async_expectations.Signal();
auto f = Future<int>::Create(std::string(__PRETTY_FUNCTION__) +
std::string("3"));
f->Complete(std::stoi(s));
return f;
})
->Then([&](int i) {
EXPECT_EQ(i, 20);
async_expectations.Signal();
});
future->Complete(10);
EXPECT_EQ(3, async_expectations.count());
}
TEST_F(FutureTest, Map) {
auto f = Future<int>::Create(__PRETTY_FUNCTION__);
AsyncExpectations async_expectations;
f->Map([&](int i) {
EXPECT_EQ(i, 10);
async_expectations.Signal();
return std::to_string(i * 2);
})
->Map([&](std::string s) {
EXPECT_EQ(s, "20");
async_expectations.Signal();
return std::stoi(s);
})
->Then([&](int i) {
EXPECT_EQ(i, 20);
async_expectations.Signal();
});
f->Complete(10);
EXPECT_EQ(3, async_expectations.count());
}
TEST_F(FutureTest, MapToTuple) {
float fresult;
std::string sresult;
auto f1 = Future<int>::Create(__PRETTY_FUNCTION__);
FuturePtr<float, std::string> f2 = f1->Map(
[](int i) { return std::make_tuple(i / 2.f, std::to_string(i)); });
f2->Then([&](float fvalue, const std::string& svalue) {
fresult = fvalue;
sresult = std::move(svalue);
});
f1->Complete(3);
EXPECT_EQ(1.5f, fresult);
EXPECT_EQ("3", sresult);
}
// compile-time test
namespace MapToTupleTuple {
FuturePtr<std::tuple<float, std::string>> f =
Future<int>::Create("MapToTupleTuple")->Map([](int i) {
return std::make_tuple(std::make_tuple(i / 2.f, std::to_string(i)));
});
}
TEST_F(FutureTest, trace_name) {
auto f = Future<>::Create("test");
EXPECT_EQ(f->trace_name(), "test");
}
TEST_F(FutureTest, ThenCallbackDestroysFuture) {
struct S {
FuturePtr<> f;
};
S* s = new S;
AsyncExpectations async_expectations;
s->f = Future<>::Create(__PRETTY_FUNCTION__);
// Explicitly retain the subfuture returned by Then() to keep it alive. This
// tests whether the original future will still complete its subfuture if the
// original future is destroyed, but the subfuture is still kept alive. (That
// should not happen, as per documentation for Then().)
auto subfuture = s->f->Then([&] {
delete s;
async_expectations.Signal();
});
subfuture->Then([&] {
// This lambda shouldn't be executed because s--and therefore s->f--was
// deleted in the Then() callback above.
async_expectations.Signal();
});
s->f->Complete();
EXPECT_EQ(1, async_expectations.count()); // not 2!
}
} // namespace
} // namespace modular