| // Copyright 2019 The Chromium 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 "base/task/promise/promise.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/run_loop.h" |
| #include "base/task/post_task.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/do_nothing_promise.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::ElementsAre; |
| |
| namespace base { |
| namespace { |
| |
| void RecordOrder(std::vector<int>* run_order, int order) { |
| run_order->push_back(order); |
| } |
| |
| class ObjectToDelete : public RefCounted<ObjectToDelete> { |
| public: |
| // |delete_flag| is set to true when this object is deleted |
| ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) { |
| EXPECT_FALSE(*delete_flag_); |
| } |
| |
| private: |
| friend class RefCounted<ObjectToDelete>; |
| ~ObjectToDelete() { *delete_flag_ = true; } |
| |
| bool* const delete_flag_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ObjectToDelete); |
| }; |
| |
| class MockObject { |
| public: |
| MockObject() = default; |
| |
| void Task(scoped_refptr<ObjectToDelete>) {} |
| void Reply(scoped_refptr<ObjectToDelete>) {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockObject); |
| }; |
| |
| struct DummyError {}; |
| |
| struct Cancelable { |
| Cancelable() {} |
| |
| void LogTask(std::vector<std::string>* log, std::string value) { |
| log->push_back(value); |
| } |
| |
| void NopTask() {} |
| |
| WeakPtrFactory<Cancelable> weak_ptr_factory{this}; |
| }; |
| |
| } // namespace |
| |
| class PromiseTest : public testing::Test { |
| public: |
| test::TaskEnvironment task_environment_; |
| }; |
| |
| TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) { |
| scoped_refptr<TestMockTimeTaskRunner> post_runner = |
| MakeRefCounted<TestMockTimeTaskRunner>(); |
| scoped_refptr<TestMockTimeTaskRunner> reply_runner = |
| MakeRefCounted<TestMockTimeTaskRunner>( |
| TestMockTimeTaskRunner::Type::kBoundToThread); |
| MockObject mock_object; |
| bool delete_task_flag = false; |
| bool delete_reply_flag = false; |
| |
| Promise<int>::CreateResolved(FROM_HERE, 42) |
| .ThenOn(post_runner, FROM_HERE, |
| BindOnce(&MockObject::Task, Unretained(&mock_object), |
| MakeRefCounted<ObjectToDelete>(&delete_task_flag))) |
| .ThenHere(FROM_HERE, |
| BindOnce(&MockObject::Reply, Unretained(&mock_object), |
| MakeRefCounted<ObjectToDelete>(&delete_reply_flag))); |
| |
| post_runner->ClearPendingTasks(); |
| |
| post_runner = nullptr; |
| reply_runner = nullptr; |
| |
| EXPECT_TRUE(delete_task_flag); |
| EXPECT_TRUE(delete_reply_flag); |
| } |
| |
| TEST_F(PromiseTest, GetResolveCallbackThen) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.GetResolveCallback().Run(123); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetResolveCallbackThenWithConstInt) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.GetResolveCallback().Run(123); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](const int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetResolveCallbackMultipleArgs) { |
| ManualPromiseResolver<std::tuple<int, bool, float>> p(FROM_HERE); |
| static_assert( |
| std::is_same<OnceCallback<void(int, bool, float)>, |
| decltype(p.GetResolveCallback<int, bool, float>())>::value, |
| ""); |
| p.GetResolveCallback<int, bool, float>().Run(123, true, 1.5f); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int a, bool b, float c) { |
| EXPECT_EQ(123, a); |
| EXPECT_TRUE(b); |
| EXPECT_EQ(1.5f, c); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverCallbackLifetimeCanOutliveParent) { |
| OnceCallback<void(int)> resolve_cb; |
| |
| RunLoop run_loop; |
| { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| resolve_cb = p.GetResolveCallback(); |
| |
| p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| } |
| |
| std::move(resolve_cb).Run(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveWithTuple) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| p.Resolve(); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindOnce([]() { return std::tuple<int, bool>(123, false); })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](const std::tuple<int, bool>& tuple) { |
| EXPECT_EQ(123, std::get<0>(tuple)); |
| EXPECT_FALSE(std::get<1>(tuple)); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveWithUnpackedTuple) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| p.Resolve(); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindOnce([]() { return std::tuple<int, bool>(123, false); })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int a, bool b) { |
| EXPECT_EQ(123, a); |
| EXPECT_FALSE(b); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveWithUnpackedTupleMoveOnlyTypes) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| p.Resolve(); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() { |
| return std::make_tuple(std::make_unique<int>(42), |
| std::make_unique<float>(4.2f)); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int> a, |
| std::unique_ptr<float> b) { |
| EXPECT_EQ(42, *a); |
| EXPECT_EQ(4.2f, *b); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetRejectCallbackCatch) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere( |
| FROM_HERE, BindLambdaForTesting([&](int result) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| run_loop.Quit(); |
| EXPECT_EQ("Oh no!", err); |
| })); |
| |
| p.GetRejectCallback().Run(std::string("Oh no!")); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetRepeatingResolveCallbackThen) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.GetRepeatingResolveCallback().Run(123); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetRepeatingRejectCallbackCatch) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere( |
| FROM_HERE, BindLambdaForTesting([&](int result) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| run_loop.Quit(); |
| EXPECT_EQ("Oh no!", err); |
| })); |
| |
| p.GetRepeatingRejectCallback().Run(std::string("Oh no!")); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CreateResolvedThen) { |
| RunLoop run_loop; |
| Promise<int>::CreateResolved(FROM_HERE, 123) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenRejectWithTuple) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| p.Resolve(); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() { |
| return Rejected<std::tuple<int, bool>>{123, false}; |
| })) |
| .CatchHere(FROM_HERE, |
| BindLambdaForTesting([&](const std::tuple<int, bool>& tuple) { |
| EXPECT_EQ(123, std::get<0>(tuple)); |
| EXPECT_FALSE(std::get<1>(tuple)); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, GetRejectCallbackMultipleArgs) { |
| ManualPromiseResolver<int, std::tuple<bool, std::string>> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere( |
| FROM_HERE, BindLambdaForTesting([&](int result) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| BindLambdaForTesting([&](const std::tuple<bool, std::string>& err) { |
| // NB we don't currently support tuple expansion for reject. |
| // Its not hard to add, but it's unclear if it will ever be used. |
| run_loop.Quit(); |
| EXPECT_FALSE(std::get<0>(err)); |
| EXPECT_EQ("Noes!", std::get<1>(err)); |
| })); |
| |
| p.GetRejectCallback<bool, std::string>().Run(false, "Noes!"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CatchHereReturnTypes) { |
| ManualPromiseResolver<int, void> p1(FROM_HERE); |
| |
| // Check CatchHere returns the expected return types for various |
| // return types. |
| Promise<int> r1 = |
| p1.promise().CatchHere(FROM_HERE, BindOnce([]() { return 123; })); |
| Promise<int> r2 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Resolved<int>(123); })); |
| Promise<int, int> r3 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Rejected<int>(123); })); |
| |
| Promise<int, void> r4 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); })); |
| Promise<int> r5 = p1.promise().CatchHere( |
| FROM_HERE, |
| BindOnce([]() { return PromiseResult<int, NoReject>(123.0); })); |
| Promise<int, int> r6 = p1.promise().CatchHere( |
| FROM_HERE, |
| BindOnce([]() { return PromiseResult<NoResolve, int>(123.0); })); |
| |
| Promise<int, void> r7 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Promise<int, void>(); })); |
| Promise<int> r8 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); })); |
| Promise<int, int> r9 = p1.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Promise<NoResolve, int>(); })); |
| |
| ManualPromiseResolver<NoResolve, void> p2(FROM_HERE); |
| Promise<int> r10 = |
| p2.promise().CatchHere(FROM_HERE, BindOnce([]() { return 123; })); |
| Promise<int> r11 = p2.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Resolved<int>(123); })); |
| Promise<NoResolve, int> r12 = p2.promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Rejected<int>(123); })); |
| } |
| |
| TEST_F(PromiseTest, ThenHereReturnTypes) { |
| ManualPromiseResolver<std::string, void> p1(FROM_HERE); |
| |
| // Check ThenHere returns the expected return types for various |
| // return types. |
| Promise<int, void> r1 = |
| p1.promise().ThenHere(FROM_HERE, BindOnce([]() { return 123; })); |
| Promise<int, void> r2 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<int>(123); })); |
| Promise<NoResolve, void> r3 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<void>(); })); |
| |
| Promise<int, void> r4 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); })); |
| Promise<int, void> r5 = p1.promise().ThenHere( |
| FROM_HERE, |
| BindOnce([]() { return PromiseResult<int, NoReject>(123.0); })); |
| Promise<NoResolve, void> r6 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return PromiseResult<NoResolve, void>(); })); |
| |
| Promise<int, void> r7 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Promise<int, void>(); })); |
| Promise<int, void> r8 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); })); |
| Promise<NoResolve, void> r9 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Promise<NoResolve, void>(); })); |
| |
| ManualPromiseResolver<std::string> p2(FROM_HERE); |
| Promise<int> r10 = |
| p2.promise().ThenHere(FROM_HERE, BindOnce([]() { return 123; })); |
| Promise<int> r11 = p2.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<int>(123); })); |
| Promise<NoResolve, int> r12 = p2.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<int>(123); })); |
| } |
| |
| TEST_F(PromiseTest, ThenAndCatchHereReturnTypes) { |
| struct A {}; |
| struct B {}; |
| struct C {}; |
| struct D {}; |
| |
| Promise<B, NoReject> p1 = |
| ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<B>(); })); |
| Promise<NoResolve, B> p2 = |
| ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<B>(); })); |
| Promise<B, C> p3 = |
| ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; })); |
| |
| Promise<B, NoReject> p4 = |
| ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Resolved<B>(); })); |
| Promise<NoResolve, B> p5 = |
| ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Rejected<B>(); })); |
| Promise<B, C> p6 = |
| ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; })); |
| |
| Promise<B, C> p7 = ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<B>(); })); |
| Promise<NoResolve, C> p8 = |
| ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<C>(); })); |
| Promise<B, C> p9 = ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; })); |
| |
| Promise<A, NoReject> p10 = |
| ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Resolved<A>(); })); |
| Promise<A, B> p11 = |
| ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() { return Rejected<B>(); })); |
| Promise<A, B> p12 = |
| ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<A, B> { return B{}; })); |
| |
| Promise<C, NoReject> p13 = |
| ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<C>(); }), |
| BindOnce([]() { return Resolved<C>(); })); |
| Promise<NoResolve, D> p14 = |
| ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<D>(); }), |
| BindOnce([]() { return Rejected<D>(); })); |
| Promise<C, D> p15 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<C>(); }), |
| BindOnce([]() { return Rejected<D>(); })); |
| Promise<C, D> p16 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<D>(); }), |
| BindOnce([]() { return Resolved<C>(); })); |
| |
| Promise<C, D> p17 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }), |
| BindOnce([]() { return Resolved<C>(); })); |
| Promise<C, D> p18 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }), |
| BindOnce([]() { return Rejected<D>(); })); |
| Promise<C, D> p19 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<C>(); }), |
| BindOnce([]() -> PromiseResult<C, D> { return C{}; })); |
| Promise<C, D> p20 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Rejected<D>(); }), |
| BindOnce([]() -> PromiseResult<C, D> { return C{}; })); |
| |
| Promise<C, D> p21 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere( |
| FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }), |
| BindOnce([]() -> PromiseResult<C, D> { return C{}; })); |
| } |
| |
| TEST_F(PromiseTest, UnsettledManualPromiseResolverCancelsChain) { |
| bool delete_flag = false; |
| Promise<void> p2; |
| |
| { |
| ManualPromiseResolver<int> p1(FROM_HERE); |
| p2 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([](scoped_refptr<ObjectToDelete> v) {}, |
| MakeRefCounted<ObjectToDelete>(&delete_flag))); |
| } |
| |
| EXPECT_TRUE(delete_flag); |
| EXPECT_TRUE(p2.IsCancelledForTesting()); |
| } |
| |
| TEST_F(PromiseTest, CancellationSpottedByExecute) { |
| bool delete_flag = false; |
| Promise<void> p3; |
| |
| { |
| Cancelable cancelable; |
| ManualPromiseResolver<void> p1(FROM_HERE); |
| Promise<void> p2 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce(&Cancelable::NopTask, |
| cancelable.weak_ptr_factory.GetWeakPtr())); |
| |
| p1.Resolve(); |
| cancelable.weak_ptr_factory.InvalidateWeakPtrs(); |
| |
| p3 = p2.ThenHere(FROM_HERE, |
| BindOnce([](scoped_refptr<ObjectToDelete> v) {}, |
| MakeRefCounted<ObjectToDelete>(&delete_flag))); |
| } |
| |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(delete_flag); |
| EXPECT_TRUE(p3.IsCancelledForTesting()); |
| } |
| |
| TEST_F(PromiseTest, RejectAndReReject) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .CatchHere( |
| FROM_HERE, |
| BindOnce([](const std::string& err) -> PromiseResult<int, int> { |
| EXPECT_EQ("Oh no!", err); |
| // Re-Reject with -1 this time. |
| return Rejected<int>(-1); |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) { |
| EXPECT_EQ(-1, err); |
| run_loop.Quit(); |
| return -1; |
| })); |
| |
| p.GetRejectCallback().Run("Oh no!"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectAndReRejectThenCatch) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .CatchHere(FROM_HERE, BindLambdaForTesting( |
| [](std::string) { return Rejected<int>(-1); })) |
| .CatchHere(FROM_HERE, |
| BindLambdaForTesting([&](int) { return Resolved<int>(1000); })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value) { |
| EXPECT_EQ(1000, value); |
| return Rejected<DummyError>(); |
| })) |
| .CatchHere(FROM_HERE, |
| BindLambdaForTesting([&](DummyError) { run_loop.Quit(); })); |
| |
| p.GetRejectCallback().Run("Oh no!"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenWhichAlwayResolves) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() -> Resolved<int> { |
| // Resolve |
| return 123; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value) { |
| EXPECT_EQ(123, value); |
| run_loop.Quit(); |
| })); |
| |
| p.GetResolveCallback().Run(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenWhichAlwayRejects) { |
| ManualPromiseResolver<void, int> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() -> Rejected<int> { |
| // Reject |
| return -1; |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) { |
| EXPECT_EQ(-1, err); |
| run_loop.Quit(); |
| })); |
| |
| p.GetResolveCallback().Run(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenWhichAlwayRejectsTypeTwo) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() -> Rejected<int> { |
| // Reject |
| return -1; |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) { |
| EXPECT_EQ(-1, err); |
| run_loop.Quit(); |
| })); |
| |
| p.GetResolveCallback().Run(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenWhichAlwayRejectsTypeThree) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| base::RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| return Rejected<std::string>(std::string("reject")); |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting( |
| [&](std::string result) { run_loop.Quit(); })); |
| |
| p.GetResolveCallback().Run(123); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ReferenceType) { |
| int a = 123; |
| int b = 456; |
| ManualPromiseResolver<const int&> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](const int& value) -> const int& { |
| EXPECT_EQ(123, value); |
| return b; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](const int& value) { |
| EXPECT_EQ(456, value); |
| run_loop.Quit(); |
| })); |
| |
| p.GetResolveCallback().Run(a); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, PromiseResultVoid) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { return PromiseResult<void>(); })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { run_loop.Quit(); })); |
| |
| p.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RefcountedType) { |
| scoped_refptr<internal::AbstractPromise> a = |
| DoNothingPromiseBuilder(FROM_HERE); |
| scoped_refptr<internal::AbstractPromise> b = |
| DoNothingPromiseBuilder(FROM_HERE); |
| ManualPromiseResolver<scoped_refptr<internal::AbstractPromise>> p(FROM_HERE); |
| RunLoop run_loop; |
| |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting( |
| [&](scoped_refptr<internal::AbstractPromise> value) { |
| EXPECT_EQ(a, value); |
| return b; |
| })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting( |
| [&](scoped_refptr<internal::AbstractPromise> value) { |
| EXPECT_EQ(b, value); |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(a); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveThenVoidFunction) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.Resolve(123); |
| |
| // You don't have to use the resolve (or reject) arguments from the |
| // previous promise. |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { run_loop.Quit(); })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveThenStdTupleUnpack) { |
| RunLoop run_loop; |
| Promise<std::tuple<int, std::string>>::CreateResolved(FROM_HERE, 10, |
| std::string("Hi")) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int a, std::string b) { |
| EXPECT_EQ(10, a); |
| EXPECT_EQ("Hi", b); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveAfterThen) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectOutsidePromiseAfterThen) { |
| ManualPromiseResolver<int, void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere( |
| FROM_HERE, BindLambdaForTesting([&](int result) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| run_loop.QuitClosure()); |
| |
| p.Reject(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenChainMoveOnlyType) { |
| ManualPromiseResolver<std::unique_ptr<int>> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> result) { return result; })) |
| .ThenHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> result) { return result; })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](std::unique_ptr<int> result) { |
| EXPECT_THAT(123, *result); |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(std::make_unique<int>(123)); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, MultipleMovesNotAllowed) { |
| ManualPromiseResolver<std::unique_ptr<int>> p(FROM_HERE); |
| |
| // The executor argument will be called with move semantics. |
| p.promise().ThenHere(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {})); |
| |
| // It's an error to do that twice. |
| EXPECT_DCHECK_DEATH({ |
| p.promise().ThenHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> result) {})); |
| }); |
| } |
| |
| TEST_F(PromiseTest, ThenChain) { |
| ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) { |
| result.push_back(1); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) { |
| result.push_back(2); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) { |
| result.push_back(3); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u)); |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(std::vector<size_t>{0}); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectionInThenChainDefaultVoid) { |
| ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) { |
| result.push_back(result.size()); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) { |
| result.push_back(result.size()); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, |
| BindOnce([](std::vector<size_t> result) |
| -> PromiseResult<std::vector<size_t>, void> { |
| return Rejected<void>(); |
| })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| BindLambdaForTesting([&]() { run_loop.Quit(); })); |
| |
| p.Resolve(std::vector<size_t>{0}); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectPropagation) { |
| ManualPromiseResolver<int, bool> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; })) |
| .ThenHere(FROM_HERE, |
| BindOnce([](int result) -> PromiseResult<int, std::string> { |
| return std::string("Fail shouldn't get here"); |
| }), |
| BindOnce([](bool value) -> PromiseResult<int, std::string> { |
| EXPECT_FALSE(value); |
| return std::string("Oh no!"); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Oh no!", err); |
| run_loop.Quit(); |
| })); |
| |
| p.Reject(false); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectPropagationThensAfterRejectSkipped) { |
| ManualPromiseResolver<int, bool> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; })) |
| .ThenHere(FROM_HERE, |
| BindOnce([](int result) -> PromiseResult<int, std::string> { |
| return std::string("Fail shouldn't get here"); |
| }), |
| BindOnce([](bool value) -> PromiseResult<int, std::string> { |
| EXPECT_FALSE(value); |
| return std::string("Oh no!"); // Reject |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| CHECK(false) << "Shouldn't get here"; |
| return result + 1; |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| CHECK(false) << "Shouldn't get here"; |
| return result + 1; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Oh no!", err); |
| run_loop.Quit(); |
| })); |
| |
| p.Reject(false); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes) { |
| ManualPromiseResolver<void, int> p(FROM_HERE); |
| |
| // Make sure ThenHere returns the expected type. |
| Promise<int, std::string> p2 = p.promise().ThenHere( |
| FROM_HERE, |
| BindOnce([]() -> PromiseResult<int, std::string> { return 123; }), |
| BindOnce([](int err) -> Resolved<int> { return 123; })); |
| } |
| |
| TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes2) { |
| ManualPromiseResolver<void, int> p(FROM_HERE); |
| |
| // Make sure ThenHere returns the expected type. |
| Promise<int, std::string> p2 = p.promise().ThenHere( |
| FROM_HERE, |
| BindOnce([]() -> PromiseResult<int, std::string> { return 123; }), |
| BindOnce([](int err) -> Rejected<std::string> { return "123"; })); |
| } |
| |
| TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes3) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| |
| // Make sure ThenHere returns the expected type. |
| Promise<void, bool> p2 = p.promise().ThenHere( |
| FROM_HERE, BindOnce([](int value) -> PromiseResult<void, bool> { |
| if (value % 2) { |
| return Resolved<void>(); |
| } else { |
| return true; |
| } |
| }), |
| BindOnce([](const std::string& err) -> Rejected<bool> { return false; })); |
| } |
| |
| TEST_F(PromiseTest, ThenOnAfterNoResolvePromiseResult) { |
| ManualPromiseResolver<std::unique_ptr<int>, int> p1(FROM_HERE); |
| |
| RunLoop run_loop; |
| p1.promise() |
| .CatchHere(FROM_HERE, BindLambdaForTesting( |
| [&](int) -> PromiseResult<NoResolve, int> { |
| return Rejected<int>(); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int>) { |
| run_loop.Quit(); |
| return std::make_unique<int>(42); |
| }), |
| BindLambdaForTesting([&](int err) { |
| CHECK(false) << "Shouldn't get here"; |
| return std::make_unique<int>(42); |
| })); |
| |
| p1.GetResolveCallback().Run(std::make_unique<int>(42)); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CatchCreatesNoRejectPromise) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| // Make sure CatchHere returns the expected type. |
| Promise<int> p2 = |
| p.promise() |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int) { |
| return Rejected<std::string>(); |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](std::string) { |
| return Resolved<int>(); |
| })); |
| } |
| |
| TEST_F(PromiseTest, ResolveSkipsCatches) { |
| ManualPromiseResolver<int, void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; })) |
| .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> { |
| CHECK(false) << "Shouldn't get here"; |
| return -1; |
| })) |
| .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> { |
| CHECK(false) << "Shouldn't get here"; |
| return -1; |
| })) |
| .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> { |
| CHECK(false) << "Shouldn't get here"; |
| return -1; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(2, result); |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&]() { |
| FAIL() << "We shouldn't get here, the promise was resolved!"; |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(1); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ThenChainVariousReturnTypes) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() { return 5; })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| EXPECT_EQ(5, result); |
| return std::string("Hello"); |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](std::string result) { |
| EXPECT_EQ("Hello", result); |
| return true; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](bool result) { |
| EXPECT_TRUE(result); |
| run_loop.Quit(); |
| })); |
| |
| p.GetResolveCallback().Run(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedVoidPromise) { |
| Promise<void> p = Promise<void>::CreateResolved(FROM_HERE); |
| ManualPromiseResolver<void> promise_resolver(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, BindOnce( |
| [](ManualPromiseResolver<void>* promise_resolver) { |
| return promise_resolver->promise(); |
| }, |
| &promise_resolver)) |
| .ThenHere(FROM_HERE, run_loop.QuitClosure()); |
| RunLoop().RunUntilIdle(); |
| |
| promise_resolver.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedIntPromise) { |
| Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000); |
| ManualPromiseResolver<int> promise_resolver(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, |
| BindOnce( |
| [](ManualPromiseResolver<int>* promise_resolver, int result) { |
| EXPECT_EQ(1000, result); |
| return promise_resolver->promise(); |
| }, |
| &promise_resolver)) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| RunLoop().RunUntilIdle(); |
| |
| promise_resolver.Resolve(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedIntPromiseChain) { |
| Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000); |
| |
| ManualPromiseResolver<int> promise_resolver_1(FROM_HERE); |
| ManualPromiseResolver<int> promise_resolver_2(FROM_HERE); |
| promise_resolver_2.Resolve(promise_resolver_1.promise()); |
| promise_resolver_1.Resolve(123); |
| |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(1000, result); |
| return promise_resolver_2.promise(); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedIntPromiseChain2) { |
| Promise<int> p1 = Promise<int>::CreateResolved(FROM_HERE, 1000); |
| Promise<int> p2 = Promise<int>::CreateResolved(FROM_HERE, 789); |
| Promise<int> then2; |
| |
| { |
| Promise<int> then1 = |
| Promise<int>::CreateResolved(FROM_HERE, 789) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { return p2; })); |
| then2 = Promise<int>::CreateResolved(FROM_HERE, 789) |
| .ThenHere( |
| FROM_HERE, |
| BindOnce([&](Promise<int> then1) { return then1; }, then1)); |
| } |
| |
| RunLoop run_loop; |
| p1.ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(1000, result); |
| return then2; |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(789, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedIntPromiseChainThenAddedAfterInitialResolve) { |
| ManualPromiseResolver<int> promise_resolver_1(FROM_HERE); |
| ManualPromiseResolver<int> promise_resolver_2(FROM_HERE); |
| ManualPromiseResolver<int> promise_resolver_3(FROM_HERE); |
| promise_resolver_2.Resolve(promise_resolver_1.promise()); |
| promise_resolver_3.Resolve(promise_resolver_2.promise()); |
| |
| RunLoop run_loop; |
| promise_resolver_3.promise().ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| BindLambdaForTesting([&]() { promise_resolver_1.Resolve(123); })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CurriedVoidPromiseModified) { |
| for (size_t i = 0; i < 1000; ++i) { |
| Promise<void> p = Promise<void>::CreateResolved(FROM_HERE); |
| std::unique_ptr<ManualPromiseResolver<int>> promise_resolver = |
| std::make_unique<ManualPromiseResolver<int>>(FROM_HERE); |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, BindOnce([](Promise<int> promise) { return promise; }, |
| promise_resolver->promise())) |
| .ThenHere(FROM_HERE, base::BindOnce([](int v) { EXPECT_EQ(v, 42); })) |
| .ThenHere(FROM_HERE, run_loop.QuitClosure()); |
| PostTask(FROM_HERE, {ThreadPool()}, BindLambdaForTesting([&]() { |
| promise_resolver->Resolve(42); |
| promise_resolver.reset(); |
| })); |
| run_loop.Run(); |
| task_environment_.RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(PromiseTest, PromiseResultReturningAPromise) { |
| Promise<int> p = Promise<int>::CreateResolved(FROM_HERE, 1000); |
| ManualPromiseResolver<int> promise_resolver(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int result) -> PromiseResult<int> { |
| EXPECT_EQ(1000, result); |
| return promise_resolver.promise(); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| RunLoop().RunUntilIdle(); |
| |
| promise_resolver.Resolve(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveToDisambiguateThenReturnValue) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int i) -> PromiseResult<Value, Value> { |
| if ((i % 2) == 1) |
| return Resolved<Value>("Success it was odd."); |
| return Rejected<Value>("Failure it was even."); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](Value result) { |
| EXPECT_EQ("Success it was odd.", result.GetString()); |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](Value err) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was resolved!"; |
| })); |
| |
| p.Resolve(1); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectedToDisambiguateThenReturnValue) { |
| ManualPromiseResolver<int, int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, int> { |
| return Rejected<int>(123); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| run_loop.Quit(); |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| }), |
| BindLambdaForTesting([&](int err) { |
| run_loop.Quit(); |
| EXPECT_EQ(123, err); |
| })); |
| |
| p.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, NestedPromises) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.Resolve(100); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| ManualPromiseResolver<int> p2(FROM_HERE); |
| p2.Resolve(200); |
| return p2.promise().ThenHere( |
| FROM_HERE, BindOnce([](int result) { |
| ManualPromiseResolver<int> p3(FROM_HERE); |
| p3.Resolve(300); |
| return p3.promise().ThenHere( |
| FROM_HERE, |
| BindOnce([](int result) { return result; })); |
| })); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(300, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, Catch) { |
| ManualPromiseResolver<int, std::string> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result; })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result; })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { return result; })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Whoops!", err); |
| run_loop.Quit(); |
| return -1; |
| })); |
| |
| p.Reject("Whoops!"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, BranchedThenChainExecutionOrder) { |
| std::vector<int> run_order; |
| |
| ManualPromiseResolver<void> promise_a(FROM_HERE); |
| Promise<void> promise_b = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1)); |
| |
| Promise<void> promise_c = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3)); |
| |
| Promise<void> promise_d = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5)); |
| |
| promise_a.Resolve(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(0, 2, 4, 1, 3, 5)); |
| } |
| |
| TEST_F(PromiseTest, BranchedThenChainWithCatchExecutionOrder) { |
| std::vector<int> run_order; |
| |
| ManualPromiseResolver<void, void> promise_a(FROM_HERE); |
| Promise<void> promise_b = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1)) |
| .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2)); |
| |
| Promise<void> promise_c = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4)) |
| .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5)); |
| |
| Promise<void> promise_d = |
| promise_a.promise() |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 6)) |
| .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 7)) |
| .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 8)); |
| |
| promise_a.Reject(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(2, 5, 8)); |
| } |
| |
| TEST_F(PromiseTest, CatchRejectInThenChain) { |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindOnce([](int result) -> PromiseResult<int, std::string> { |
| return std::string("Whoops!"); |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| CHECK(false) << "Shouldn't get here"; |
| return result; |
| })) |
| .ThenHere(FROM_HERE, BindOnce([](int result) { |
| CHECK(false) << "Shouldn't get here"; |
| return result; |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Whoops!", err); |
| run_loop.Quit(); |
| return -1; |
| })); |
| |
| p.Resolve(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CatchThenVoid) { |
| ManualPromiseResolver<int, void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .CatchHere(FROM_HERE, BindOnce([]() { return 123; })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| p.Reject(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CatchThenInt) { |
| ManualPromiseResolver<int, int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .CatchHere(FROM_HERE, BindOnce([](int err) { return err + 1; })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) { |
| EXPECT_EQ(124, result); |
| run_loop.Quit(); |
| })); |
| |
| p.Reject(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, SettledTaskFinally) { |
| int result = 0; |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.Resolve(123); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int value) { result = value; })) |
| .FinallyHere(FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, SettledTaskFinallyThen) { |
| int result = 0; |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.Resolve(123); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int value) { result = value; })) |
| .FinallyHere(FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_EQ(123, result); |
| return std::string("hi"); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](const std::string& value) { |
| EXPECT_EQ("hi", value); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, SettledTaskFinallyCatch) { |
| int result = 0; |
| ManualPromiseResolver<int> p(FROM_HERE); |
| p.Resolve(123); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int value) { result = value; })) |
| .FinallyHere(FROM_HERE, BindLambdaForTesting( |
| [&]() -> PromiseResult<void, std::string> { |
| EXPECT_EQ(123, result); |
| return std::string("Oh no"); |
| })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& value) { |
| EXPECT_EQ("Oh no", value); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, ResolveFinally) { |
| int result = 0; |
| ManualPromiseResolver<int> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere( |
| FROM_HERE, BindLambdaForTesting([&](int value) { result = value; })); |
| p.promise().FinallyHere(FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_EQ(123, result); |
| run_loop.Quit(); |
| })); |
| p.Resolve(123); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectFinally) { |
| int result = 0; |
| ManualPromiseResolver<int, void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](int value) { result = value; }), |
| BindLambdaForTesting([&]() { result = -1; })); |
| p.promise().FinallyHere(FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_EQ(-1, result); |
| run_loop.Quit(); |
| })); |
| p.Reject(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, RejectFinallySkipsThens) { |
| ManualPromiseResolver<void> p(FROM_HERE); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { return Rejected<int>(123); })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting( |
| [&]() { FAIL() << "Promise was rejected"; })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting( |
| [&]() { FAIL() << "Promise was rejected"; })) |
| .FinallyHere(FROM_HERE, run_loop.QuitClosure()); |
| p.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, CancelViaWeakPtr) { |
| std::vector<std::string> log; |
| ManualPromiseResolver<void, std::string> mpr(FROM_HERE, |
| RejectPolicy::kCatchNotRequired); |
| Promise<void, std::string> p1 = mpr.promise(); |
| { |
| Cancelable cancelable; |
| Promise<void, std::string> p2 = p1.ThenHere( |
| FROM_HERE, |
| BindOnce(&Cancelable::LogTask, cancelable.weak_ptr_factory.GetWeakPtr(), |
| &log, "Then #1")); |
| p2.ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() -> PromiseResult<void, std::string> { |
| log.push_back("Then #2 (reject)"); |
| return std::string("Whoops!"); |
| })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { log.push_back("Then #3"); })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { log.push_back("Then #4"); })) |
| .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) { |
| log.push_back("Caught " + err); |
| })); |
| |
| p2.FinallyHere(FROM_HERE, |
| BindLambdaForTesting([&]() { log.push_back("Finally"); })); |
| p2.ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { log.push_back("Then #5"); })); |
| p2.ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { log.push_back("Then #6"); })); |
| } |
| |
| mpr.Resolve(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(log.empty()); |
| } |
| |
| TEST_F(PromiseTest, CancelPropagation) { |
| ManualPromiseResolver<void> p1(FROM_HERE); |
| ManualPromiseResolver<void> p2(FROM_HERE); |
| Promise<void> p3; |
| Promise<std::tuple<Void, Void>> pAll; |
| |
| RunLoop run_loop; |
| { |
| Cancelable cancelable; |
| |
| p3 = p2.promise().ThenHere( |
| FROM_HERE, BindOnce(&Cancelable::NopTask, |
| cancelable.weak_ptr_factory.GetWeakPtr())); |
| |
| pAll = Promises::All(FROM_HERE, p1.promise(), p3); |
| |
| p1.Resolve(); |
| p2.Resolve(); |
| EXPECT_FALSE(pAll.IsCancelledForTesting()); |
| } |
| |
| run_loop.RunUntilIdle(); |
| EXPECT_TRUE(pAll.IsCancelledForTesting()); |
| } |
| |
| TEST_F(PromiseTest, CancelPropagationLongerChain) { |
| ManualPromiseResolver<void> p1(FROM_HERE); |
| ManualPromiseResolver<void> p2(FROM_HERE); |
| Promise<void> p3; |
| Promise<std::tuple<Void, Void>> pAll; |
| |
| RunLoop run_loop; |
| { |
| Cancelable cancelable; |
| |
| p3 = p2.promise() |
| .ThenHere(FROM_HERE, |
| BindOnce(&Cancelable::NopTask, |
| cancelable.weak_ptr_factory.GetWeakPtr())) |
| .ThenHere(FROM_HERE, BindOnce([]() {})) |
| .ThenHere(FROM_HERE, BindOnce([]() {})); |
| |
| pAll = Promises::All(FROM_HERE, p1.promise(), p3); |
| |
| p1.Resolve(); |
| p2.Resolve(); |
| EXPECT_FALSE(pAll.IsCancelledForTesting()); |
| } |
| |
| run_loop.RunUntilIdle(); |
| EXPECT_TRUE(pAll.IsCancelledForTesting()); |
| } |
| |
| TEST_F(PromiseTest, CatchNotRequired) { |
| ManualPromiseResolver<bool, int> p(FROM_HERE, |
| RejectPolicy::kCatchNotRequired); |
| |
| RunLoop run_loop; |
| p.promise().ThenHere(FROM_HERE, run_loop.QuitClosure()); |
| |
| // Note this doesn't DCHECK even though we haven't specified a Catch. |
| p.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, MoveOnlyTypeMultipleThensNotAllowed) { |
| #if DCHECK_IS_ON() |
| Promise<std::unique_ptr<int>> p = |
| Promise<std::unique_ptr<int>>::CreateResolved(FROM_HERE, |
| std::make_unique<int>(123)); |
| |
| p.ThenHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); |
| |
| EXPECT_DCHECK_DEATH({ |
| p.ThenHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); |
| }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) { |
| #if DCHECK_IS_ON() |
| auto p = Promise<void, std::unique_ptr<int>>::CreateRejected( |
| FROM_HERE, std::make_unique<int>(123)); |
| |
| p.CatchHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); |
| |
| EXPECT_DCHECK_DEATH({ |
| p.CatchHere(FROM_HERE, |
| BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); })); |
| }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, UnhandledRejection) { |
| #if DCHECK_IS_ON() |
| Promise<void, int> p = Promise<void, int>::CreateRejected(FROM_HERE).ThenHere( |
| FROM_HERE, BindOnce([]() {})); |
| |
| RunLoop().RunUntilIdle(); |
| |
| Promise<void, int> null_promise; |
| EXPECT_DCHECK_DEATH({ p = null_promise; }); |
| |
| // EXPECT_DCHECK_DEATH uses fork under the hood so we still have to tidy up. |
| p.IgnoreUncaughtCatchForTesting(); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverPotentialUnhandledRejection) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void, void> promise_resolver(FROM_HERE); |
| |
| // |promise_resolver| could reject but there's no catch. |
| Promise<void, void> p = |
| promise_resolver.promise().ThenHere(FROM_HERE, BindOnce([]() {})); |
| |
| promise_resolver.Resolve(); |
| RunLoop().RunUntilIdle(); |
| |
| Promise<void, void> null_promise; |
| EXPECT_DCHECK_DEATH({ p = null_promise; }); |
| |
| // EXPECT_DCHECK_DEATH uses fork under the hood so we still have to tidy up. |
| p.IgnoreUncaughtCatchForTesting(); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverResolveCalledTwice) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void> promise_resolver(FROM_HERE); |
| |
| promise_resolver.Resolve(); |
| |
| EXPECT_DCHECK_DEATH({ promise_resolver.Resolve(); }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverRejectCalledTwice) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void, void> promise_resolver( |
| FROM_HERE, RejectPolicy::kCatchNotRequired); |
| |
| promise_resolver.Reject(); |
| |
| EXPECT_DCHECK_DEATH({ promise_resolver.Reject(); }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverResolveCalledAfterReject) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void, void> promise_resolver( |
| FROM_HERE, RejectPolicy::kCatchNotRequired); |
| |
| promise_resolver.Reject(); |
| |
| EXPECT_DCHECK_DEATH({ promise_resolver.Resolve(); }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverRepeatingResolveCallbackCalledTwice) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void, void> promise_resolver( |
| FROM_HERE, RejectPolicy::kCatchNotRequired); |
| RepeatingCallback<void()> resolve = |
| promise_resolver.GetRepeatingResolveCallback(); |
| |
| resolve.Run(); |
| |
| EXPECT_DCHECK_DEATH({ resolve.Run(); }); |
| #endif |
| } |
| |
| TEST_F(PromiseTest, ManualPromiseResolverRepeatingRejectCallbackCalledTwice) { |
| #if DCHECK_IS_ON() |
| ManualPromiseResolver<void, void> promise_resolver( |
| FROM_HERE, RejectPolicy::kCatchNotRequired); |
| RepeatingCallback<void()> resolve = |
| promise_resolver.GetRepeatingRejectCallback(); |
| |
| resolve.Run(); |
| |
| EXPECT_DCHECK_DEATH({ resolve.Run(); }); |
| #endif |
| } |
| |
| class MultiThreadedPromiseTest : public PromiseTest { |
| public: |
| void SetUp() override { |
| thread_a_.reset(new Thread("MultiThreadPromiseTest_Thread_A")); |
| thread_b_.reset(new Thread("MultiThreadPromiseTest_Thread_B")); |
| thread_c_.reset(new Thread("MultiThreadPromiseTest_Thread_C")); |
| thread_a_->Start(); |
| thread_b_->Start(); |
| thread_c_->Start(); |
| } |
| |
| void TearDown() override { |
| thread_a_->Stop(); |
| thread_b_->Stop(); |
| thread_c_->Stop(); |
| } |
| |
| std::unique_ptr<Thread> thread_a_; |
| std::unique_ptr<Thread> thread_b_; |
| std::unique_ptr<Thread> thread_c_; |
| }; |
| |
| TEST_F(MultiThreadedPromiseTest, SimpleThreadHopping) { |
| ManualPromiseResolver<void> promise_resolver(FROM_HERE); |
| |
| RunLoop run_loop; |
| promise_resolver.promise() |
| .ThenOn( |
| thread_a_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_TRUE(thread_a_->task_runner()->RunsTasksInCurrentSequence()); |
| })) |
| .ThenOn( |
| thread_b_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_TRUE(thread_b_->task_runner()->RunsTasksInCurrentSequence()); |
| })) |
| .ThenOn( |
| thread_c_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_TRUE(thread_c_->task_runner()->RunsTasksInCurrentSequence()); |
| })) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { |
| EXPECT_FALSE( |
| thread_a_->task_runner()->RunsTasksInCurrentSequence()); |
| EXPECT_FALSE( |
| thread_b_->task_runner()->RunsTasksInCurrentSequence()); |
| EXPECT_FALSE( |
| thread_c_->task_runner()->RunsTasksInCurrentSequence()); |
| run_loop.Quit(); |
| })); |
| |
| promise_resolver.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(MultiThreadedPromiseTest, CrossThreadThens) { |
| ManualPromiseResolver<void> promise_resolver(FROM_HERE); |
| |
| auto resolve_task = |
| BindLambdaForTesting([&]() { promise_resolver.Resolve(); }); |
| |
| RunLoop run_loop; |
| |
| // Rolling our own thread-unsafe BarrierClosure to ensure atomics aren't |
| // necessary for this test to resolve all Thens on |thread_c_|. |
| int thens_remaining = 1000; |
| auto then_task = BindLambdaForTesting([&]() { |
| --thens_remaining; |
| if (!thens_remaining) |
| run_loop.Quit(); |
| }); |
| |
| thread_a_->task_runner()->PostTask( |
| FROM_HERE, BindLambdaForTesting([&]() { |
| // Post 500 thens. |
| for (int i = 0; i < 500; i++) { |
| promise_resolver.promise().ThenOn(thread_c_->task_runner(), FROM_HERE, |
| then_task); |
| } |
| // Post a task onto the main thread to resolve |promise_resolver|. |
| // This should run at an undefined time yet all the thens should run. |
| thread_b_->task_runner()->PostTask(FROM_HERE, resolve_task); |
| |
| // Post another 500 thens. |
| for (int i = 0; i < 500; i++) { |
| promise_resolver.promise().ThenOn(thread_c_->task_runner(), FROM_HERE, |
| then_task); |
| } |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(MultiThreadedPromiseTest, CrossThreadThensOrdering) { |
| constexpr int kNumThenTasks = 1000; |
| constexpr int kNumRepetitions = 25; |
| for (int repetition = 0; repetition < kNumRepetitions; ++repetition) { |
| RunLoop run_loop; |
| |
| std::vector<int> order; |
| std::vector<OnceCallback<void()>> then_tasks; |
| |
| for (int i = 0; i < kNumThenTasks; ++i) { |
| then_tasks.push_back( |
| BindOnce(BindLambdaForTesting([&order, &run_loop, i]() { |
| order.push_back(i); |
| if (i == (kNumThenTasks - 1)) { |
| run_loop.Quit(); |
| } |
| }))); |
| } |
| |
| ManualPromiseResolver<void> promise_resolver(FROM_HERE); |
| auto resolve_callback = promise_resolver.GetResolveCallback(); |
| |
| thread_a_->task_runner()->PostTask( |
| FROM_HERE, BindLambdaForTesting([&]() { |
| // Post 500 thens. |
| for (int i = 0; i < kNumThenTasks / 2; ++i) { |
| promise_resolver.promise().ThenOn( |
| thread_c_->task_runner(), FROM_HERE, std::move(then_tasks[i])); |
| } |
| |
| // Post a task onto |thread_b| to resolve |promise_resolver|. |
| // This should run at an undefined time yet all the thens should run. |
| thread_b_->task_runner()->PostTask(FROM_HERE, |
| std::move(resolve_callback)); |
| |
| // Post another 500 thens. |
| for (int i = kNumThenTasks / 2; i < kNumThenTasks; ++i) { |
| promise_resolver.promise().ThenOn( |
| thread_c_->task_runner(), FROM_HERE, std::move(then_tasks[i])); |
| } |
| })); |
| |
| run_loop.Run(); |
| for (int i = 0; i < kNumThenTasks; ++i) { |
| EXPECT_EQ(order[i], i); |
| } |
| } |
| } |
| |
| TEST_F(PromiseTest, ThreadPoolThenChain) { |
| ManualPromiseResolver<std::vector<size_t>> p(FROM_HERE); |
| auto main_sequence = SequencedTaskRunnerHandle::Get(); |
| |
| RunLoop run_loop; |
| p.promise() |
| .ThenOn({ThreadPool(), TaskPriority::USER_BLOCKING}, FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence()); |
| result.push_back(1); |
| return result; |
| })) |
| .ThenOn({ThreadPool(), TaskPriority::USER_BLOCKING}, FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence()); |
| result.push_back(2); |
| return result; |
| })) |
| .ThenOn({ThreadPool(), TaskPriority::USER_BLOCKING}, FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| EXPECT_FALSE(main_sequence->RunsTasksInCurrentSequence()); |
| result.push_back(3); |
| return result; |
| })) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](std::vector<size_t> result) { |
| EXPECT_TRUE(main_sequence->RunsTasksInCurrentSequence()); |
| EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u)); |
| run_loop.Quit(); |
| })); |
| |
| p.Resolve(std::vector<size_t>{0}); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, All) { |
| ManualPromiseResolver<float> p1(FROM_HERE); |
| ManualPromiseResolver<int> p2(FROM_HERE); |
| ManualPromiseResolver<bool> p3(FROM_HERE); |
| Promise<std::tuple<float, int, bool>> p = |
| Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise()); |
| |
| RunLoop run_loop; |
| p.ThenHere(FROM_HERE, BindLambdaForTesting( |
| [&](const std::tuple<float, int, bool>& result) { |
| EXPECT_EQ(1.234f, std::get<0>(result)); |
| EXPECT_EQ(1234, std::get<1>(result)); |
| EXPECT_TRUE(std::get<2>(result)); |
| run_loop.Quit(); |
| })); |
| |
| p1.Resolve(1.234f); |
| p2.Resolve(1234); |
| p3.Resolve(true); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllUnpackTuple) { |
| ManualPromiseResolver<float> p1(FROM_HERE); |
| ManualPromiseResolver<int> p2(FROM_HERE); |
| ManualPromiseResolver<bool> p3(FROM_HERE); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise()) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](float a, int b, bool c) { |
| EXPECT_EQ(1.234f, a); |
| EXPECT_EQ(1234, b); |
| EXPECT_TRUE(c); |
| run_loop.Quit(); |
| })); |
| |
| p1.Resolve(1.234f); |
| p2.Resolve(1234); |
| p3.Resolve(true); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllRejectString) { |
| ManualPromiseResolver<float, std::string> p1(FROM_HERE); |
| ManualPromiseResolver<int, std::string> p2(FROM_HERE); |
| ManualPromiseResolver<bool, std::string> p3(FROM_HERE); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise()) |
| .ThenHere( |
| FROM_HERE, |
| BindLambdaForTesting([&](const std::tuple<float, int, bool>& result) { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Whoops!", err); |
| run_loop.Quit(); |
| })); |
| |
| p1.Reject("Whoops!"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllWithSingleValue) { |
| ManualPromiseResolver<int> p1(FROM_HERE); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise()) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value1) { |
| EXPECT_EQ(value1, 1); |
| run_loop.Quit(); |
| })); |
| |
| p1.Resolve(1); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllIntVoid) { |
| ManualPromiseResolver<int> p1(FROM_HERE); |
| ManualPromiseResolver<void> p2(FROM_HERE); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise(), p2.promise()) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&](const std::tuple<int, Void>& result) { |
| EXPECT_EQ(1234, std::get<0>(result)); |
| run_loop.Quit(); |
| })); |
| |
| p1.Resolve(1234); |
| p2.Resolve(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllMoveOnlyType) { |
| ManualPromiseResolver<std::unique_ptr<float>> p1(FROM_HERE); |
| ManualPromiseResolver<std::unique_ptr<int>> p2(FROM_HERE); |
| ManualPromiseResolver<std::unique_ptr<bool>> p3(FROM_HERE); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise()) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting( |
| [&](std::tuple<std::unique_ptr<float>, std::unique_ptr<int>, |
| std::unique_ptr<bool>> result) { |
| EXPECT_EQ(1.234f, *std::get<0>(result)); |
| EXPECT_EQ(1234, *std::get<1>(result)); |
| EXPECT_TRUE(*std::get<2>(result)); |
| run_loop.Quit(); |
| })); |
| |
| p1.Resolve(std::make_unique<float>(1.234f)); |
| p2.Resolve(std::make_unique<int>(1234)); |
| p3.Resolve(std::make_unique<bool>(true)); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllIntWithVoidThen) { |
| ManualPromiseResolver<int> p1(FROM_HERE); |
| ManualPromiseResolver<int> p2(FROM_HERE); |
| ManualPromiseResolver<int> p3(FROM_HERE); |
| |
| // You can choose to ignore the result. |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise()) |
| .ThenHere(FROM_HERE, run_loop.QuitClosure()); |
| |
| p1.Resolve(1); |
| p2.Resolve(2); |
| p3.Resolve(3); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllIntContainer) { |
| ManualPromiseResolver<int> mpr1(FROM_HERE); |
| ManualPromiseResolver<int> mpr2(FROM_HERE); |
| ManualPromiseResolver<int> mpr3(FROM_HERE); |
| ManualPromiseResolver<int> mpr4(FROM_HERE); |
| |
| std::vector<Promise<int>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) { |
| EXPECT_THAT(result, ElementsAre(10, 20, 30, 40)); |
| run_loop.Quit(); |
| })); |
| |
| mpr1.Resolve(10); |
| mpr2.Resolve(20); |
| mpr3.Resolve(30); |
| mpr4.Resolve(40); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllEmptyIntContainer) { |
| std::vector<Promise<int>> promises; |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) { |
| EXPECT_TRUE(result.empty()); |
| run_loop.Quit(); |
| })); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllIntStringContainerReject) { |
| ManualPromiseResolver<int, std::string> mpr1(FROM_HERE); |
| ManualPromiseResolver<int, std::string> mpr2(FROM_HERE); |
| ManualPromiseResolver<int, std::string> mpr3(FROM_HERE); |
| ManualPromiseResolver<int, std::string> mpr4(FROM_HERE); |
| |
| std::vector<Promise<int, std::string>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](const std::string& err) { |
| EXPECT_EQ("Oh dear", err); |
| run_loop.Quit(); |
| })); |
| |
| mpr2.Reject("Oh dear"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllVoidContainer) { |
| ManualPromiseResolver<void> mpr1(FROM_HERE); |
| ManualPromiseResolver<void> mpr2(FROM_HERE); |
| ManualPromiseResolver<void> mpr3(FROM_HERE); |
| ManualPromiseResolver<void> mpr4(FROM_HERE); |
| |
| std::vector<Promise<void>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promise<void> result = |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, |
| BindLambdaForTesting([&]() { run_loop.Quit(); })); |
| |
| mpr1.Resolve(); |
| mpr2.Resolve(); |
| mpr3.Resolve(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(result.IsResolvedForTesting()); |
| |
| mpr4.Resolve(); |
| run_loop.Run(); |
| EXPECT_TRUE(result.IsResolvedForTesting()); |
| } |
| |
| TEST_F(PromiseTest, AllVoidIntContainerReject) { |
| ManualPromiseResolver<void, int> mpr1(FROM_HERE); |
| ManualPromiseResolver<void, int> mpr2(FROM_HERE); |
| ManualPromiseResolver<void, int> mpr3(FROM_HERE); |
| ManualPromiseResolver<void, int> mpr4(FROM_HERE); |
| |
| std::vector<Promise<void, int>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| BindLambdaForTesting([&](int err) { |
| EXPECT_EQ(-1, err); |
| run_loop.Quit(); |
| })); |
| |
| mpr1.Reject(-1); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllVoidContainerReject) { |
| ManualPromiseResolver<void, void> mpr1(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr2(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr3(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr4(FROM_HERE); |
| |
| std::vector<Promise<void, void>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| run_loop.QuitClosure()); |
| |
| mpr4.Reject(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllVoidContainerMultipleRejectsBeforeExecute) { |
| ManualPromiseResolver<void, void> mpr1(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr2(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr3(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr4(FROM_HERE); |
| |
| std::vector<Promise<void, void>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| run_loop.QuitClosure()); |
| |
| mpr1.Reject(); |
| mpr2.Reject(); |
| mpr4.Reject(); |
| run_loop.Run(); |
| } |
| |
| TEST_F(PromiseTest, AllVoidContainerMultipleRejectsAfterExecute) { |
| ManualPromiseResolver<void, void> mpr1(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr2(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr3(FROM_HERE); |
| ManualPromiseResolver<void, void> mpr4(FROM_HERE); |
| |
| std::vector<Promise<void, void>> promises; |
| promises.push_back(mpr1.promise()); |
| promises.push_back(mpr2.promise()); |
| promises.push_back(mpr3.promise()); |
| promises.push_back(mpr4.promise()); |
| |
| RunLoop run_loop; |
| Promises::All(FROM_HERE, promises) |
| .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { |
| FAIL() << "We shouldn't get here, the promise was rejected!"; |
| run_loop.Quit(); |
| }), |
| run_loop.QuitClosure()); |
| |
| mpr1.Reject(); |
| run_loop.Run(); |
| mpr2.Reject(); |
| mpr4.Reject(); |
| } |
| |
| TEST_F(PromiseTest, TakeResolveValueForTesting) { |
| ManualPromiseResolver<void> p1(FROM_HERE); |
| |
| Promise<int> p2 = |
| p1.promise().ThenHere(FROM_HERE, BindOnce([]() { return 123; })); |
| |
| p1.Resolve(); |
| |
| EXPECT_EQ(123, p2.TakeResolveValueForTesting()); |
| } |
| |
| TEST_F(PromiseTest, TakeResolveValueForTestingMoveOnlyType) { |
| ManualPromiseResolver<void> p1(FROM_HERE); |
| |
| Promise<std::unique_ptr<int>> p2 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return std::make_unique<int>(123); })); |
| |
| p1.Resolve(); |
| |
| EXPECT_EQ(123, *p2.TakeResolveValueForTesting()); |
| } |
| |
| TEST_F(PromiseTest, TakeResolveValueForTestingNotResolved) { |
| ManualPromiseResolver<int, int> p1(FROM_HERE, |
| RejectPolicy::kCatchNotRequired); |
| |
| p1.Reject(123); |
| |
| EXPECT_DCHECK_DEATH({ p1.promise().TakeResolveValueForTesting(); }); |
| } |
| |
| TEST_F(PromiseTest, TakeRejectedValueForTesting) { |
| ManualPromiseResolver<void, void> p1(FROM_HERE); |
| |
| Promise<int, int> p2 = p1.promise().ThenHere( |
| FROM_HERE, BindOnce([]() { return Resolved<int>(123); }), |
| BindOnce([]() { return Rejected<int>(456); })); |
| |
| p1.Reject(); |
| |
| EXPECT_EQ(456, p2.TakeRejectValueForTesting()); |
| } |
| |
| TEST_F(PromiseTest, TakeRejectedValueForTestingMoveOnlyType) { |
| ManualPromiseResolver<void, std::unique_ptr<int>> p1(FROM_HERE); |
| |
| p1.Reject(std::make_unique<int>(456)); |
| |
| EXPECT_EQ(456, *p1.promise().TakeRejectValueForTesting()); |
| } |
| |
| TEST_F(PromiseTest, TakeRejectedValueForTestingNotRejected) { |
| ManualPromiseResolver<int, int> p1(FROM_HERE, |
| RejectPolicy::kCatchNotRequired); |
| |
| p1.Resolve(123); |
| |
| EXPECT_DCHECK_DEATH({ p1.promise().TakeRejectValueForTesting(); }); |
| } |
| |
| } // namespace base |