blob: 4e4816b6a8821f31595938ee5be5d2bcda6ab4c9 [file] [log] [blame]
// Copyright 2018 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/fpromise/promise.h>
#include <zxtest/zxtest.h>
#include "unittest_utils.h"
namespace {
class fake_context : public fpromise::context {
public:
fpromise::executor* executor() const override { ASSERT_CRITICAL(false); }
fpromise::suspended_task suspend_task() override { ASSERT_CRITICAL(false); }
};
TEST(FutureTests, empty_future) {
fake_context context;
{
fpromise::future<> nihil;
EXPECT_EQ(fpromise::future_state::empty, nihil.state());
EXPECT_FALSE(nihil);
EXPECT_TRUE(nihil.is_empty());
EXPECT_FALSE(nihil.is_pending());
EXPECT_FALSE(nihil.is_ok());
EXPECT_FALSE(nihil.is_error());
EXPECT_FALSE(nihil.is_ready());
EXPECT_FALSE(nihil(context));
EXPECT_TRUE(nihil == nullptr);
EXPECT_TRUE(nullptr == nihil);
EXPECT_FALSE(nihil != nullptr);
EXPECT_FALSE(nullptr != nihil);
}
{
fpromise::future<> nihil(nullptr);
EXPECT_EQ(fpromise::future_state::empty, nihil.state());
EXPECT_FALSE(nihil);
EXPECT_TRUE(nihil.is_empty());
EXPECT_FALSE(nihil.is_pending());
EXPECT_FALSE(nihil.is_ok());
EXPECT_FALSE(nihil.is_error());
EXPECT_FALSE(nihil.is_ready());
EXPECT_FALSE(nihil(context));
}
{
fpromise::future<> nihil(fpromise::promise<>(nullptr));
EXPECT_EQ(fpromise::future_state::empty, nihil.state());
EXPECT_FALSE(nihil);
EXPECT_TRUE(nihil.is_empty());
EXPECT_FALSE(nihil.is_pending());
EXPECT_FALSE(nihil.is_ok());
EXPECT_FALSE(nihil.is_error());
EXPECT_FALSE(nihil.is_ready());
EXPECT_FALSE(nihil(context));
}
{
fpromise::future<> nihil(fpromise::pending());
EXPECT_EQ(fpromise::future_state::empty, nihil.state());
EXPECT_FALSE(nihil);
EXPECT_TRUE(nihil.is_empty());
EXPECT_FALSE(nihil.is_pending());
EXPECT_FALSE(nihil.is_ok());
EXPECT_FALSE(nihil.is_error());
EXPECT_FALSE(nihil.is_ready());
EXPECT_FALSE(nihil(context));
}
}
TEST(FutureTests, pending_future) {
fake_context context;
uint64_t run_count = 0;
fpromise::future<int, int> fut(
fpromise::make_promise([&](fpromise::context& context) -> fpromise::result<int, int> {
if (++run_count == 3)
return fpromise::ok(42);
return fpromise::pending();
}));
EXPECT_EQ(fpromise::future_state::pending, fut.state());
EXPECT_TRUE(fut);
EXPECT_FALSE(fut.is_empty());
EXPECT_TRUE(fut.is_pending());
EXPECT_FALSE(fut.is_ok());
EXPECT_FALSE(fut.is_error());
EXPECT_FALSE(fut.is_ready());
EXPECT_FALSE(fut == nullptr);
EXPECT_FALSE(nullptr == fut);
EXPECT_TRUE(fut != nullptr);
EXPECT_TRUE(nullptr != fut);
// evaluate the future
EXPECT_FALSE(fut(context));
EXPECT_EQ(1, run_count);
EXPECT_FALSE(fut(context));
EXPECT_EQ(2, run_count);
EXPECT_TRUE(fut(context));
EXPECT_EQ(3, run_count);
// check the result
EXPECT_EQ(fpromise::future_state::ok, fut.state());
EXPECT_EQ(fpromise::result_state::ok, fut.result().state());
EXPECT_EQ(42, fut.result().value());
// do something similar but this time produce an error to ensure
// that this state change works as expected too
fut = fpromise::make_promise([&](fpromise::context& context) -> fpromise::result<int, int> {
if (++run_count == 5)
return fpromise::error(42);
return fpromise::pending();
});
EXPECT_EQ(fpromise::future_state::pending, fut.state());
EXPECT_FALSE(fut(context));
EXPECT_EQ(4, run_count);
EXPECT_TRUE(fut(context));
EXPECT_EQ(5, run_count);
EXPECT_EQ(fpromise::future_state::error, fut.state());
EXPECT_EQ(fpromise::result_state::error, fut.result().state());
EXPECT_EQ(42, fut.result().error());
}
TEST(FutureTests, ok_future) {
fake_context context;
fpromise::future<int> fut(fpromise::ok(42));
EXPECT_EQ(fpromise::future_state::ok, fut.state());
EXPECT_TRUE(fut);
EXPECT_FALSE(fut.is_empty());
EXPECT_FALSE(fut.is_pending());
EXPECT_TRUE(fut.is_ok());
EXPECT_FALSE(fut.is_error());
EXPECT_TRUE(fut.is_ready());
EXPECT_TRUE(fut(context));
EXPECT_FALSE(fut == nullptr);
EXPECT_FALSE(nullptr == fut);
EXPECT_TRUE(fut != nullptr);
EXPECT_TRUE(nullptr != fut);
// non-destructive access
EXPECT_EQ(fpromise::result_state::ok, fut.result().state());
EXPECT_EQ(42, fut.result().value());
EXPECT_EQ(42, fut.value());
// destructive access
fut = fpromise::ok(43);
EXPECT_EQ(fpromise::future_state::ok, fut.state());
EXPECT_EQ(43, fut.take_result().value());
EXPECT_EQ(fpromise::future_state::empty, fut.state());
fut = fpromise::ok(44);
EXPECT_EQ(fpromise::future_state::ok, fut.state());
EXPECT_EQ(44, fut.take_value());
EXPECT_EQ(fpromise::future_state::empty, fut.state());
fut = fpromise::ok(45);
EXPECT_EQ(fpromise::future_state::ok, fut.state());
EXPECT_EQ(45, fut.take_ok_result().value);
EXPECT_EQ(fpromise::future_state::empty, fut.state());
}
TEST(FutureTests, error_future) {
fake_context context;
fpromise::future<void, int> fut(fpromise::error(42));
EXPECT_EQ(fpromise::future_state::error, fut.state());
EXPECT_TRUE(fut);
EXPECT_FALSE(fut.is_empty());
EXPECT_FALSE(fut.is_pending());
EXPECT_FALSE(fut.is_ok());
EXPECT_TRUE(fut.is_error());
EXPECT_TRUE(fut.is_ready());
EXPECT_TRUE(fut(context));
EXPECT_FALSE(fut == nullptr);
EXPECT_FALSE(nullptr == fut);
EXPECT_TRUE(fut != nullptr);
EXPECT_TRUE(nullptr != fut);
// non-destructive access
EXPECT_EQ(fpromise::result_state::error, fut.result().state());
EXPECT_EQ(42, fut.result().error());
EXPECT_EQ(42, fut.error());
// destructive access
fut = fpromise::error(43);
EXPECT_EQ(fpromise::future_state::error, fut.state());
EXPECT_EQ(43, fut.take_result().error());
EXPECT_EQ(fpromise::future_state::empty, fut.state());
fut = fpromise::error(44);
EXPECT_EQ(fpromise::future_state::error, fut.state());
EXPECT_EQ(44, fut.take_error());
EXPECT_EQ(fpromise::future_state::empty, fut.state());
fut = fpromise::error(45);
EXPECT_EQ(fpromise::future_state::error, fut.state());
EXPECT_EQ(45, fut.take_error_result().error);
EXPECT_EQ(fpromise::future_state::empty, fut.state());
}
TEST(FutureTests, assignment_and_swap) {
fpromise::future<> x;
EXPECT_EQ(fpromise::future_state::empty, x.state());
x = fpromise::ok();
EXPECT_EQ(fpromise::future_state::ok, x.state());
x = fpromise::error();
EXPECT_EQ(fpromise::future_state::error, x.state());
x = fpromise::pending();
EXPECT_EQ(fpromise::future_state::empty, x.state());
x = nullptr;
EXPECT_EQ(fpromise::future_state::empty, x.state());
x = fpromise::promise<>();
EXPECT_EQ(fpromise::future_state::empty, x.state());
x = fpromise::make_promise([] {});
EXPECT_EQ(fpromise::future_state::pending, x.state());
fpromise::future<> y(std::move(x));
EXPECT_EQ(fpromise::future_state::pending, y.state());
EXPECT_EQ(fpromise::future_state::empty, x.state());
x.swap(y);
EXPECT_EQ(fpromise::future_state::pending, x.state());
EXPECT_EQ(fpromise::future_state::empty, y.state());
x.swap(x);
EXPECT_EQ(fpromise::future_state::pending, x.state());
}
TEST(FutureTests, make_future) {
fake_context context;
uint64_t run_count = 0;
auto fut = fpromise::make_future(fpromise::make_promise([&] {
run_count++;
return fpromise::ok(42);
}));
EXPECT_TRUE(fut(context));
EXPECT_EQ(42, fut.value());
}
// Ensure that fpromise::future is considered nullable so that there is
// consistency with the fact that it can be initialized and assigned from
// nullptr similar to fit::function.
static_assert(fit::is_nullable<fpromise::future<>>::value, "");
} // namespace