| // 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/abstract_promise.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_simple_task_runner.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // Even trivial DCHECK_DEATH_TESTs like |
| // AbstractPromiseTest.CantRejectIfpromiseDeclaredAsNonRejecting can flakily |
| // timeout on the chromeos bots. |
| #if defined(OS_CHROMEOS) |
| #define ABSTRACT_PROMISE_DEATH_TEST(test_name) DISABLED_##test_name |
| #else |
| #define ABSTRACT_PROMISE_DEATH_TEST(test_name) test_name |
| #endif |
| |
| using testing::ElementsAre; |
| |
| using ArgumentPassingType = |
| base::internal::PromiseExecutor::ArgumentPassingType; |
| |
| using PrerequisitePolicy = base::internal::PromiseExecutor::PrerequisitePolicy; |
| |
| namespace base { |
| namespace internal { |
| namespace { |
| |
| size_t CountTasksRunUntilIdle( |
| const scoped_refptr<TestSimpleTaskRunner>& task_runner) { |
| size_t count = 0; |
| while (task_runner->HasPendingTask()) { |
| count += task_runner->NumPendingTasks(); |
| task_runner->RunPendingTasks(); |
| } |
| return count; |
| } |
| |
| } // namespace |
| |
| class TestExecutor { |
| public: |
| TestExecutor(PrerequisitePolicy policy, |
| #if DCHECK_IS_ON() |
| ArgumentPassingType resolve_executor_type, |
| ArgumentPassingType reject_executor_type, |
| bool can_resolve, |
| bool can_reject, |
| #endif |
| base::OnceCallback<void(AbstractPromise*)> callback) |
| : callback_(std::move(callback)), |
| #if DCHECK_IS_ON() |
| resolve_argument_passing_type_(resolve_executor_type), |
| reject_argument_passing_type_(reject_executor_type), |
| resolve_flags_(can_resolve + (can_reject << 1)), |
| #endif |
| policy_(policy) { |
| } |
| |
| #if DCHECK_IS_ON() |
| ArgumentPassingType ResolveArgumentPassingType() const { |
| return resolve_argument_passing_type_; |
| } |
| |
| ArgumentPassingType RejectArgumentPassingType() const { |
| return reject_argument_passing_type_; |
| } |
| |
| bool CanResolve() const { return resolve_flags_ & 1; } |
| |
| bool CanReject() const { return resolve_flags_ & 2; } |
| #endif |
| |
| PrerequisitePolicy GetPrerequisitePolicy() const { return policy_; } |
| |
| bool IsCancelled() const { return false; } |
| |
| void Execute(AbstractPromise* p) { std::move(callback_).Run(p); } |
| |
| private: |
| base::OnceCallback<void(AbstractPromise*)> callback_; |
| #if DCHECK_IS_ON() |
| const ArgumentPassingType resolve_argument_passing_type_; |
| const ArgumentPassingType reject_argument_passing_type_; |
| // On 32 bit platform we need to pack to fit in the space requirement of 3x |
| // void*. |
| uint8_t resolve_flags_; |
| #endif |
| const PrerequisitePolicy policy_; |
| }; |
| |
| class AbstractPromiseTest : public testing::Test { |
| public: |
| enum class CallbackResultType : uint8_t { |
| kNoCallback, |
| kCanResolve, |
| kCanReject, |
| kCanResolveOrReject, |
| }; |
| |
| struct PromiseSettings { |
| PromiseSettings( |
| Location from_here, |
| std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites) |
| : from_here(from_here), prerequisites(std::move(prerequisites)) {} |
| |
| Location from_here; |
| |
| std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites; |
| |
| PrerequisitePolicy prerequisite_policy = |
| PromiseExecutor::PrerequisitePolicy::kAll; |
| |
| bool executor_can_resolve = true; |
| |
| bool executor_can_reject = false; |
| |
| ArgumentPassingType resolve_executor_type = ArgumentPassingType::kNormal; |
| |
| ArgumentPassingType reject_executor_type = ArgumentPassingType::kNoCallback; |
| |
| RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection; |
| |
| base::OnceCallback<void(AbstractPromise*)> callback; |
| |
| scoped_refptr<TaskRunner> task_runner = ThreadTaskRunnerHandle::Get(); |
| }; |
| |
| class PromiseSettingsBuilder { |
| public: |
| PromiseSettingsBuilder( |
| Location from_here, |
| std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites) |
| : settings(from_here, std::move(prerequisites)) {} |
| |
| PromiseSettingsBuilder& With(PrerequisitePolicy prerequisite_policy) { |
| settings.prerequisite_policy = prerequisite_policy; |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& With(const scoped_refptr<TaskRunner>& task_runner) { |
| settings.task_runner = task_runner; |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& With(RejectPolicy reject_policy) { |
| settings.reject_policy = reject_policy; |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& With( |
| base::OnceCallback<void(AbstractPromise*)> callback) { |
| settings.callback = std::move(callback); |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& With(CallbackResultType callback_result_type) { |
| switch (callback_result_type) { |
| case CallbackResultType::kNoCallback: |
| settings.executor_can_resolve = false; |
| settings.executor_can_reject = false; |
| break; |
| case CallbackResultType::kCanResolve: |
| settings.executor_can_resolve = true; |
| settings.executor_can_reject = false; |
| break; |
| case CallbackResultType::kCanReject: |
| settings.executor_can_resolve = false; |
| settings.executor_can_reject = true; |
| break; |
| case CallbackResultType::kCanResolveOrReject: |
| settings.executor_can_resolve = true; |
| settings.executor_can_reject = true; |
| break; |
| }; |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& WithResolve( |
| ArgumentPassingType resolve_executor_type) { |
| settings.resolve_executor_type = resolve_executor_type; |
| return *this; |
| } |
| |
| PromiseSettingsBuilder& WithReject( |
| ArgumentPassingType reject_executor_type) { |
| settings.reject_executor_type = reject_executor_type; |
| return *this; |
| } |
| |
| operator scoped_refptr<AbstractPromise>() { |
| PromiseExecutor::Data executor_data( |
| in_place_type_t<TestExecutor>(), settings.prerequisite_policy, |
| #if DCHECK_IS_ON() |
| settings.resolve_executor_type, settings.reject_executor_type, |
| settings.executor_can_resolve, settings.executor_can_reject, |
| #endif |
| std::move(settings.callback)); |
| |
| return AbstractPromise::Create( |
| settings.task_runner, settings.from_here, |
| std::move(settings.prerequisites), settings.reject_policy, |
| DependentList::ConstructUnresolved(), std::move(executor_data)); |
| } |
| |
| private: |
| PromiseSettings settings; |
| }; |
| |
| PromiseSettingsBuilder ThenPromise(Location from_here, |
| scoped_refptr<AbstractPromise> parent) { |
| PromiseSettingsBuilder builder( |
| from_here, |
| parent ? std::make_unique<AbstractPromise::AdjacencyList>(parent.get()) |
| : std::make_unique<AbstractPromise::AdjacencyList>()); |
| builder.With(BindOnce([](AbstractPromise* p) { |
| AbstractPromise* prerequisite = p->GetOnlyPrerequisite(); |
| if (prerequisite->IsResolved()) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| } else if (prerequisite->IsRejected()) { |
| // Consistent with BaseThenAndCatchExecutor::ProcessNullExecutor. |
| p->emplace(scoped_refptr<AbstractPromise>(prerequisite)); |
| p->OnResolved(); |
| } else { |
| NOTREACHED(); |
| } |
| })); |
| return builder; |
| } |
| |
| PromiseSettingsBuilder CatchPromise(Location from_here, |
| scoped_refptr<AbstractPromise> parent) { |
| PromiseSettingsBuilder builder( |
| from_here, |
| parent ? std::make_unique<AbstractPromise::AdjacencyList>(parent.get()) |
| : std::make_unique<AbstractPromise::AdjacencyList>()); |
| builder.With(CallbackResultType::kNoCallback) |
| .With(CallbackResultType::kCanResolve) |
| .WithResolve(ArgumentPassingType::kNoCallback) |
| .WithReject(ArgumentPassingType::kNormal) |
| .With(BindOnce([](AbstractPromise* p) { |
| AbstractPromise* prerequisite = p->GetOnlyPrerequisite(); |
| if (prerequisite->IsResolved()) { |
| // Consistent with BaseThenAndCatchExecutor::ProcessNullExecutor. |
| p->emplace(scoped_refptr<AbstractPromise>(prerequisite)); |
| p->OnResolved(); |
| } else if (prerequisite->IsRejected()) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| } else { |
| NOTREACHED(); |
| } |
| })); |
| return builder; |
| } |
| |
| PromiseSettingsBuilder AllPromise( |
| Location from_here, |
| std::vector<internal::DependentList::Node> prerequisite_list) { |
| PromiseSettingsBuilder builder( |
| from_here, std::make_unique<AbstractPromise::AdjacencyList>( |
| std::move(prerequisite_list))); |
| builder.With(PrerequisitePolicy::kAll) |
| .With(BindOnce([](AbstractPromise* p) { |
| AbstractPromise* first_settled = p->GetFirstSettledPrerequisite(); |
| if (first_settled && first_settled->IsRejected()) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| return; |
| } |
| |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| return builder; |
| } |
| |
| PromiseSettingsBuilder AnyPromise( |
| Location from_here, |
| std::vector<internal::DependentList::Node> prerequisite_list) { |
| PromiseSettingsBuilder builder( |
| from_here, std::make_unique<AbstractPromise::AdjacencyList>( |
| std::move(prerequisite_list))); |
| builder.With(PrerequisitePolicy::kAny) |
| .With(BindOnce([](AbstractPromise* p) { |
| AbstractPromise* first_settled = p->GetFirstSettledPrerequisite(); |
| if (first_settled && first_settled->IsRejected()) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| return; |
| } |
| |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| return builder; |
| } |
| |
| test::TaskEnvironment task_environment_; |
| }; |
| |
| TEST_F(AbstractPromiseTest, UnfulfilledPromise) { |
| scoped_refptr<AbstractPromise> promise = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| EXPECT_FALSE(promise->IsResolvedForTesting()); |
| EXPECT_FALSE(promise->IsRejectedForTesting()); |
| EXPECT_FALSE(promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, OnResolve) { |
| scoped_refptr<AbstractPromise> promise = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| EXPECT_FALSE(promise->IsResolvedForTesting()); |
| promise->OnResolved(); |
| EXPECT_TRUE(promise->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, OnReject) { |
| scoped_refptr<AbstractPromise> promise = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy( |
| RejectPolicy::kCatchNotRequired); |
| EXPECT_FALSE(promise->IsRejectedForTesting()); |
| promise->OnRejected(); |
| EXPECT_TRUE(promise->IsRejectedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ExecuteOnResolve) { |
| scoped_refptr<AbstractPromise> promise = |
| ThenPromise(FROM_HERE, nullptr).With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| EXPECT_FALSE(promise->IsResolvedForTesting()); |
| promise->Execute(); |
| EXPECT_TRUE(promise->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ExecuteOnReject) { |
| scoped_refptr<AbstractPromise> promise = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(RejectPolicy::kCatchNotRequired) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| EXPECT_FALSE(promise->IsRejectedForTesting()); |
| promise->Execute(); |
| EXPECT_TRUE(promise->IsRejectedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| EXPECT_FALSE(p4->IsResolvedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p1->IsResolvedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MoveExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3).WithResolve(ArgumentPassingType::kMove); |
| |
| scoped_refptr<AbstractPromise> p5 = |
| ThenPromise(FROM_HERE, p4).WithResolve(ArgumentPassingType::kMove); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| EXPECT_FALSE(p4->IsResolvedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p1->IsResolvedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1) |
| .With(CallbackResultType::kCanReject) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p2) |
| .With(CallbackResultType::kCanResolve) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(CallbackResultType::kCanReject) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p5 = |
| CatchPromise(FROM_HERE, p4) |
| .With(CallbackResultType::kCanResolve) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsRejectedForTesting()); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| EXPECT_FALSE(p4->IsRejectedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsRejectedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsRejectedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChainType2) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1) |
| .With(CallbackResultType::kCanReject) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p2) |
| .With(CallbackResultType::kCanReject) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| CatchPromise(FROM_HERE, p3) |
| .With(CallbackResultType::kCanResolve) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p5 = |
| ThenPromise(FROM_HERE, p4) |
| .With(CallbackResultType::kCanResolve) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p6 = |
| ThenPromise(FROM_HERE, p5) |
| .With(CallbackResultType::kCanReject) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p7 = |
| CatchPromise(FROM_HERE, p6) |
| .With(CallbackResultType::kCanReject) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p8 = |
| CatchPromise(FROM_HERE, p7) |
| .With(CallbackResultType::kCanResolve) |
| .WithReject(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p9 = |
| ThenPromise(FROM_HERE, p8) |
| .With(CallbackResultType::kCanResolve) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsRejectedForTesting()); |
| EXPECT_FALSE(p3->IsRejectedForTesting()); |
| EXPECT_FALSE(p4->IsResolvedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| EXPECT_FALSE(p6->IsRejectedForTesting()); |
| EXPECT_FALSE(p7->IsRejectedForTesting()); |
| EXPECT_FALSE(p8->IsResolvedForTesting()); |
| EXPECT_FALSE(p9->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsRejectedForTesting()); |
| EXPECT_TRUE(p3->IsRejectedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| EXPECT_TRUE(p6->IsRejectedForTesting()); |
| EXPECT_TRUE(p7->IsRejectedForTesting()); |
| EXPECT_TRUE(p8->IsResolvedForTesting()); |
| EXPECT_TRUE(p9->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MixedMoveAndNormalExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove); |
| |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3).WithResolve(ArgumentPassingType::kMove); |
| |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| EXPECT_FALSE(p4->IsResolvedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p1->IsResolvedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MoveAtEndOfChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove); |
| } |
| |
| TEST_F(AbstractPromiseTest, BranchedExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| EXPECT_FALSE(p4->IsResolvedForTesting()); |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsResolvedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, PrerequisiteAlreadyResolved) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| p1->OnResolved(); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, PrerequisiteAlreadyRejected) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| p1->OnRejected(); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| CatchPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| EXPECT_TRUE( |
| p->GetFirstSettledPrerequisite()->IsRejectedForTesting()); |
| EXPECT_EQ(p->GetFirstSettledPrerequisite(), p1); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MultipleResolvedPrerequisitePolicyALL) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| p1->OnResolved(); |
| p2->OnResolved(); |
| p3->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(all_promise->IsResolvedForTesting()); |
| p4->OnResolved(); |
| |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(all_promise->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| MultithreadedMultipleResolvedPrerequisitePolicyALL) { |
| constexpr int num_threads = 4; |
| constexpr int num_promises = 1000; |
| |
| std::unique_ptr<Thread> thread[num_threads]; |
| for (int i = 0; i < num_threads; i++) { |
| thread[i] = std::make_unique<Thread>("Test thread"); |
| thread[i]->Start(); |
| } |
| |
| scoped_refptr<AbstractPromise> promise[num_promises]; |
| std::vector<internal::DependentList::Node> prerequisite_list(num_promises); |
| for (int i = 0; i < num_promises; i++) { |
| promise[i] = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| prerequisite_list[i].SetPrerequisite(promise[i].get()); |
| } |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| RunLoop run_loop; |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, all_promise) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_loop.Quit(); |
| p->OnResolved(); |
| })); |
| |
| for (int i = 0; i < num_promises; i++) { |
| thread[i % num_threads]->task_runner()->PostTask( |
| FROM_HERE, BindOnce( |
| [](scoped_refptr<AbstractPromise> all_promise, |
| scoped_refptr<AbstractPromise> promise) { |
| EXPECT_FALSE(all_promise->IsResolvedForTesting()); |
| promise->OnResolved(); |
| }, |
| all_promise, promise[i])); |
| } |
| |
| run_loop.Run(); |
| |
| for (int i = 0; i < num_promises; i++) { |
| EXPECT_TRUE(promise[i]->IsResolvedForTesting()); |
| } |
| |
| EXPECT_TRUE(all_promise->IsResolvedForTesting()); |
| |
| for (int i = 0; i < num_threads; i++) { |
| thread[i]->Stop(); |
| } |
| } |
| |
| TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyALL) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)) |
| .With(CallbackResultType::kCanResolveOrReject) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| EXPECT_TRUE( |
| p->GetFirstSettledPrerequisite()->IsRejectedForTesting()); |
| EXPECT_EQ(p->GetFirstSettledPrerequisite(), p3); |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, all_promise); |
| |
| p3->OnRejected(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(all_promise->IsRejectedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MultipleRejectPrerequisitePolicyALL) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)) |
| .With(CallbackResultType::kCanResolveOrReject) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| AbstractPromise* settled = p->GetFirstSettledPrerequisite(); |
| if (settled && settled->IsRejected()) { |
| EXPECT_EQ(settled, p2); |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| } else { |
| FAIL() << "A prerequisite was rejected"; |
| } |
| })); |
| |
| scoped_refptr<AbstractPromise> p5 = |
| CatchPromise(FROM_HERE, all_promise) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| EXPECT_FALSE(p->IsSettled()); // Should only happen once. |
| EXPECT_TRUE( |
| p->GetFirstSettledPrerequisite()->IsRejectedForTesting()); |
| EXPECT_EQ(p->GetFirstSettledPrerequisite(), all_promise); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p2->OnRejected(); |
| p1->OnRejected(); |
| p3->OnRejected(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(all_promise->IsRejectedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, SingleResolvedPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| p2->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(any_promise->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, MultipleResolvedPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| p1->OnResolved(); |
| p2->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(any_promise->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)) |
| .With(CallbackResultType::kCanResolveOrReject); |
| |
| scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, any_promise); |
| |
| p3->OnRejected(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(any_promise->IsRejectedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, SingleResolvePrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, any_promise); |
| |
| p3->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(any_promise->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, IsCanceled) { |
| scoped_refptr<AbstractPromise> promise = ThenPromise(FROM_HERE, nullptr); |
| EXPECT_FALSE(promise->IsCanceled()); |
| promise->OnCanceled(); |
| EXPECT_TRUE(promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, OnCanceledPreventsExecution) { |
| scoped_refptr<AbstractPromise> promise = |
| ThenPromise(FROM_HERE, nullptr).With(BindOnce([](AbstractPromise* p) { |
| FAIL() << "Should not be called"; |
| })); |
| promise->OnCanceled(); |
| promise->Execute(); |
| } |
| |
| TEST_F(AbstractPromiseTest, CancelationStopsExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1).With(BindOnce([](AbstractPromise* p) { |
| p->OnCanceled(); |
| p->OnCanceled(); // NOP shouldn't crash. |
| })); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| |
| p1->OnResolved(); |
| |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p3->IsCanceled()); |
| EXPECT_TRUE(p4->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CancelationStopsBranchedExecutionChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1).With(BindOnce([](AbstractPromise* p) { |
| p->OnCanceled(); |
| })); |
| |
| // Branch one |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| |
| // Branch two |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> promise6 = ThenPromise(FROM_HERE, p5); |
| |
| p1->OnResolved(); |
| |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p3->IsCanceled()); |
| EXPECT_TRUE(p4->IsCanceled()); |
| EXPECT_TRUE(p5->IsCanceled()); |
| EXPECT_TRUE(promise6->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CancelChainCanReject) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2); |
| |
| p0->OnCanceled(); |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, CancelationPrerequisitePolicyALL) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(3); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| p2->OnCanceled(); |
| EXPECT_TRUE(all_promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CancelationPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(3); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| p3->OnCanceled(); |
| p2->OnCanceled(); |
| EXPECT_FALSE(any_promise->IsCanceled()); |
| |
| p1->OnCanceled(); |
| EXPECT_TRUE(any_promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, AlreadyCanceledPrerequisitePolicyALL) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(3); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| p2->OnCanceled(); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| EXPECT_TRUE(all_promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, SomeAlreadyCanceledPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(3); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| p2->OnCanceled(); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| EXPECT_FALSE(any_promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, AllAlreadyCanceledPrerequisitePolicyANY) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(3); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| p1->OnCanceled(); |
| p2->OnCanceled(); |
| p3->OnCanceled(); |
| |
| scoped_refptr<AbstractPromise> any_promise = |
| AnyPromise(FROM_HERE, std::move(prerequisite_list)); |
| |
| EXPECT_TRUE(any_promise->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CurriedResolvedPromiseAny) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [](scoped_refptr<AbstractPromise> p2, AbstractPromise* p) { |
| p->emplace(std::move(p2)); |
| p->OnResolved(); |
| }, |
| p2)) |
| .With(PrerequisitePolicy::kAny); |
| |
| scoped_refptr<TestSimpleTaskRunner> task_runner = |
| MakeRefCounted<TestSimpleTaskRunner>(); |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1).With(task_runner); |
| |
| p0->OnResolved(); |
| p2->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // |p3| should run. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| } |
| |
| TEST_F(AbstractPromiseTest, CurriedRejectedPromiseAny) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true); |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [](scoped_refptr<AbstractPromise> p2, AbstractPromise* p) { |
| p->emplace(std::move(p2)); |
| p->OnResolved(); |
| }, |
| p2)) |
| .With(PrerequisitePolicy::kAny); |
| |
| scoped_refptr<TestSimpleTaskRunner> task_runner = |
| MakeRefCounted<TestSimpleTaskRunner>(); |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p1).With(task_runner); |
| |
| p0->OnResolved(); |
| p2->OnRejected(); |
| RunLoop().RunUntilIdle(); |
| |
| // |p3| should run. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DetectResolveDoubleMoveHazard)) { |
| scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove); |
| }); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST( |
| DetectMixedResolveCallbackMoveAndNonMoveHazard)) { |
| scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH( |
| { scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); }); |
| } |
| |
| TEST_F(AbstractPromiseTest, MultipleNonMoveCatchCallbacksAreOK) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * E3 E4 |
| * C C |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DetectCatchCallbackDoubleMoveHazard)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * C C |
| * |
| * We need to make sure P3 & P4's reject callback don't both use move |
| * semantics since they share a common ancestor with no intermediate catches. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| CatchPromise(FROM_HERE, p0).WithReject(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p2 = |
| CatchPromise(FROM_HERE, p0).WithReject(ArgumentPassingType::kMove); |
| }); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DetectCatchCallbackDoubleMoveHazardInChain)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * P3 P4 |
| * C C |
| * |
| * We need to make sure P3 & P4's reject callback don't both use move |
| * semantics since they share a common ancestor with no intermediate catches. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p4 = |
| CatchPromise(FROM_HERE, p2).WithReject(ArgumentPassingType::kMove); |
| }); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST( |
| DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * P3 P4 |
| * C C |
| * |
| * We need to make sure P3 & P4's reject callback don't both use move |
| * semantics since they share a common ancestor with no intermediate catches. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p4 = |
| CatchPromise(FROM_HERE, p2).WithReject(ArgumentPassingType::kMove); |
| }); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DetectMixedCatchCallbackMoveAndNonMoveHazard)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * P3 P4 |
| * C C |
| * |
| * We can't guarantee the order in which P3 and P4's reject callbacks run so |
| * we need to need to catch the case where move and non-move semantics are |
| * mixed. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject); |
| |
| scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH( |
| { scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2); }); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DetectThenCallbackDoubleMoveHazardInChain)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * C C |
| * | | |
| * \| |/ |
| * P1 P2 |
| * C C |
| * | | |
| * \| |/ |
| * P3 P4 |
| * T T |
| * |
| * We need to make sure P3 & P4's resolve callback don't both use move |
| * semantics since they share a common ancestor with no intermediate then's. |
| */ |
| scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr); |
| scoped_refptr<AbstractPromise> p1 = CatchPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = CatchPromise(FROM_HERE, p0); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove); |
| }); |
| } |
| |
| TEST_F(AbstractPromiseTest, SimpleMissingCatch) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p1| is deleted. |
| EXPECT_DCHECK_DEATH({ p1 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p1| isn't actually |
| // cleared so we need to tidy up. |
| p1->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, ABSTRACT_PROMISE_DEATH_TEST(MissingCatch)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| // The missing catch here will get noticed. |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p2| is deleted. |
| EXPECT_DCHECK_DEATH({ p2 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p2| isn't actually |
| // cleared so we need to tidy up. |
| p2->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, MissingCatchNotRequired) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(RejectPolicy::kCatchNotRequired) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| // The missing catch here will gets ignored. |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| |
| p0->OnResolved(); |
| |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(MissingCatchFromCurriedPromise)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) { |
| // Resolve with a promise that can and does reject. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p1)); |
| p->emplace(std::move(p1)); |
| p->OnResolved(); |
| }, |
| std::move(p1))); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p2| is deleted. |
| EXPECT_DCHECK_DEATH({ p2 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p2| isn't actually |
| // cleared so we need to tidy up. |
| p2->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(MissingCatchFromCurriedPromiseWithDependent)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [&](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) { |
| // Resolve with a promise that can and does reject. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p1)); |
| p->emplace(std::move(p1)); |
| p->OnResolved(); |
| }, |
| std::move(p1))); |
| |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p3| is deleted. |
| EXPECT_DCHECK_DEATH({ p3 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p3| isn't actually |
| // cleared so we need to tidy up. |
| p3->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST( |
| MissingCatchFromCurriedPromiseWithDependentAddedAfterExecution)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [&](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) { |
| // Resolve with a promise that can and does reject. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p1)); |
| p->emplace(std::move(p1)); |
| p->OnResolved(); |
| }, |
| std::move(p1))); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p3| is deleted. |
| EXPECT_DCHECK_DEATH({ p3 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p3| isn't actually |
| // cleared so we need to tidy up. |
| p3->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(MissingCatchLongChain)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p4| is deleted. |
| EXPECT_DCHECK_DEATH({ p4 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually |
| // cleared so we need to tidy up. |
| p4->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST( |
| ThenAddedToSettledPromiseWithMissingCatchAndSeveralDependents)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p2); |
| |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p5| is deleted. |
| EXPECT_DCHECK_DEATH({ p5 = nullptr; }); |
| |
| // Tidy up. |
| p3->IgnoreUncaughtCatchForTesting(); |
| p4->IgnoreUncaughtCatchForTesting(); |
| p5->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(ThenAddedAfterChainExecutionWithMissingCatch)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // The missing catch here will get noticed. |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p4| is deleted. |
| EXPECT_DCHECK_DEATH({ p4 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually |
| // cleared so we need to tidy up. |
| p4->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, CatchAddedAfterChainExecution) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3); |
| |
| // We shouldn't get a DCHECK failure because |p4| catches the rejection |
| // from |p1|. |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(MultipleThensAddedAfterChainExecution)) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| // |p5| - |p7| should still inherit catch responsibility despite this. |
| scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| // The missing catches will get noticed. |
| scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p3); |
| scoped_refptr<AbstractPromise> p6 = ThenPromise(FROM_HERE, p3); |
| scoped_refptr<AbstractPromise> p7 = ThenPromise(FROM_HERE, p3); |
| RunLoop().RunUntilIdle(); |
| |
| // This should DCHECK when |p5|, |p6| or |p7| are deleted. |
| EXPECT_DCHECK_DEATH({ p5 = nullptr; }); |
| EXPECT_DCHECK_DEATH({ p6 = nullptr; }); |
| EXPECT_DCHECK_DEATH({ p7 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p5|, |p6| & |p7| aren't |
| // actually cleared so we need to tidy up. |
| p5->IgnoreUncaughtCatchForTesting(); |
| p6->IgnoreUncaughtCatchForTesting(); |
| p7->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F(AbstractPromiseTest, MultipleDependentsAddedAfterChainExecution) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3); |
| scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, p4); |
| scoped_refptr<AbstractPromise> p6 = ThenPromise(FROM_HERE, p3); |
| scoped_refptr<AbstractPromise> p7 = CatchPromise(FROM_HERE, p6); |
| |
| // We shouldn't get a DCHECK failure because |p6| and |p7| catch the rejection |
| // from |p1|. |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, CatchAfterLongChain) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, p0) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2); |
| scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3); |
| |
| p0->OnResolved(); |
| |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(MissingCatchOneSideOfBranchedExecutionChain)) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * P3 P4 |
| * C T |
| * |
| * The missing catch for P4 should get noticed. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p2); |
| |
| p0->OnRejected(); |
| |
| RunLoop().RunUntilIdle(); |
| // This should DCHECK when |p4| is deleted. |
| EXPECT_DCHECK_DEATH({ p4 = nullptr; }); |
| |
| // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually |
| // cleared so we need to tidy up. |
| p4->IgnoreUncaughtCatchForTesting(); |
| } |
| |
| TEST_F( |
| AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(CantResolveIfpromiseDeclaredAsNonResolving)) { |
| scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE); |
| |
| EXPECT_DCHECK_DEATH({ p->OnResolved(); }); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(CantRejectIfpromiseDeclaredAsNonRejecting)) { |
| scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE); |
| |
| EXPECT_DCHECK_DEATH({ p->OnRejected(); }); |
| } |
| |
| TEST_F(AbstractPromiseTest, |
| ABSTRACT_PROMISE_DEATH_TEST(DoubleMoveDoNothingPromise)) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<int>(42)); |
| p->OnResolved(); |
| })); |
| |
| EXPECT_DCHECK_DEATH({ |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1) |
| .WithResolve(ArgumentPassingType::kMove) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<int>(42)); |
| p->OnResolved(); |
| })); |
| }); |
| } |
| |
| TEST_F(AbstractPromiseTest, CatchBothSidesOfBranchedExecutionChain) { |
| /* |
| * Key: T = Then, C = Catch |
| * |
| * P0 |
| * T T |
| * | | |
| * \| |/ |
| * P1 P2 |
| * T T |
| * | | |
| * \| |/ |
| * P3 P4 |
| * C C |
| * |
| * This should execute without DCHECKS. |
| */ |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); |
| scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1); |
| scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2); |
| |
| p0->OnRejected(); |
| |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, ResolvedCurriedPromise) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| std::vector<int> run_order; |
| |
| // Promise |p3| will be resolved with. |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(2); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(3); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p2)); |
| p->emplace(std::move(p2)); |
| p->OnResolved(); |
| |
| EXPECT_TRUE(p3->IsResolvedWithPromise()); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(4); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(3, 2, 4)); |
| } |
| |
| TEST_F(AbstractPromiseTest, UnresolvedCurriedPromise) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| std::vector<int> run_order; |
| |
| // Promise |p3| will be resolved with. |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(3); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p2)); |
| p->emplace(p2); |
| p->OnResolved(); |
| |
| EXPECT_TRUE(p3->IsResolvedWithPromise()); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(4); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(3)); |
| |
| p2->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(3, 4)); |
| } |
| |
| TEST_F(AbstractPromiseTest, NeverResolvedCurriedPromise) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| std::vector<int> run_order; |
| |
| // Promise |p3| will be resolved with. |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(3); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p2)); |
| p->emplace(p2); |
| p->OnResolved(); |
| |
| EXPECT_TRUE(p3->IsResolvedWithPromise()); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(4); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(3)); |
| |
| // This shouldn't leak. |
| } |
| |
| TEST_F(AbstractPromiseTest, CanceledCurriedPromise) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| // Promise |p3| will be resolved with. |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| p2->OnCanceled(); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p2)); |
| EXPECT_TRUE(p2->IsCanceled()); |
| p->emplace(p2); |
| p->OnResolved(); |
| EXPECT_TRUE(p->IsCanceled()); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting( |
| [&](AbstractPromise* p) { FAIL() << "Should not get here"; })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(p4->IsCanceled()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CurriedPromiseChain) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| std::vector<int> run_order; |
| |
| // Promise |p3| will be resolved with. |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(2); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| // Promise |p4| will be resolved with. |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(3); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p2)); |
| p->emplace(std::move(p2)); |
| p->OnResolved(); |
| })); |
| |
| // Promise |p5| will be resolved with. |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(4); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p3)); |
| p->emplace(std::move(p3)); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p5 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(5); |
| // Resolve with a promise. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p4)); |
| p->emplace(std::move(p4)); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p6 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| run_order.push_back(6); |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(5, 4, 3, 2, 6)); |
| } |
| |
| TEST_F(AbstractPromiseTest, CurriedPromiseChainType2) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(p1); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p2) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(p2); |
| p->OnResolved(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(p3); |
| p->OnResolved(); |
| })); |
| |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_EQ(p1.get(), p4->FindNonCurriedAncestor()); |
| } |
| |
| TEST_F(AbstractPromiseTest, CurriedPromiseMoveArg) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| })) |
| .WithResolve(ArgumentPassingType::kMove); |
| ; |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) { |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p1)); |
| p->emplace(std::move(p1)); |
| p->OnResolved(); |
| }, |
| std::move(p1))) |
| .WithResolve(ArgumentPassingType::kMove); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(AbstractPromiseTest, CatchCurriedPromise) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| ThenPromise(FROM_HERE, nullptr) |
| .With(CallbackResultType::kCanReject) |
| .With(BindOnce([](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindOnce( |
| [](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) { |
| // Resolve with a promise that can and does reject. |
| ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::Execute, p1)); |
| p->emplace(std::move(p1)); |
| p->OnResolved(); |
| }, |
| std::move(p1))); |
| |
| scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2); |
| |
| p0->OnResolved(); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ManuallyResolveWithNonSettledCurriedPromise) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1); |
| |
| p1->emplace(p0); |
| p1->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p1->IsResolvedForTesting()); |
| EXPECT_FALSE(p2->IsResolvedForTesting()); |
| |
| p0->OnResolved(); |
| RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(p2->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ExecuteCalledOnceForLateResolvedCurriedPromise) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<TestSimpleTaskRunner> task_runner = |
| MakeRefCounted<TestSimpleTaskRunner>(); |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(p1); |
| p->OnResolved(); |
| })) |
| .With(task_runner); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p1).With(task_runner); |
| |
| p0->OnResolved(); |
| // |p2| should run but not |p3|. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| |
| p1->OnResolved(); |
| // |p3| should run. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ExecuteCalledOnceForLateRejectedCurriedPromise) { |
| scoped_refptr<AbstractPromise> p0 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy( |
| RejectPolicy::kCatchNotRequired); |
| |
| scoped_refptr<TestSimpleTaskRunner> task_runner = |
| MakeRefCounted<TestSimpleTaskRunner>(); |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p0) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(p1); |
| p->OnResolved(); |
| })) |
| .With(task_runner); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| CatchPromise(FROM_HERE, p1).With(task_runner); |
| |
| p0->OnResolved(); |
| // |p2| should run but not |p3|. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| EXPECT_FALSE(p3->IsResolvedForTesting()); |
| |
| p1->OnRejected(); |
| // |p3| should run. |
| EXPECT_EQ(1u, CountTasksRunUntilIdle(task_runner)); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| } |
| |
| TEST_F(AbstractPromiseTest, ThreadHopping) { |
| std::unique_ptr<Thread> thread_a(new Thread("AbstractPromiseTest_Thread_A")); |
| std::unique_ptr<Thread> thread_b(new Thread("AbstractPromiseTest_Thread_B")); |
| std::unique_ptr<Thread> thread_c(new Thread("AbstractPromiseTest_Thread_C")); |
| thread_a->Start(); |
| thread_b->Start(); |
| thread_c->Start(); |
| RunLoop run_loop; |
| |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| scoped_refptr<AbstractPromise> p2 = |
| ThenPromise(FROM_HERE, p1) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| CHECK(thread_a->task_runner()->BelongsToCurrentThread()); |
| })) |
| .With(thread_a->task_runner()); |
| |
| scoped_refptr<AbstractPromise> p3 = |
| ThenPromise(FROM_HERE, p2) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| CHECK(thread_b->task_runner()->BelongsToCurrentThread()); |
| })) |
| .With(thread_b->task_runner()); |
| |
| scoped_refptr<AbstractPromise> p4 = |
| ThenPromise(FROM_HERE, p3) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| CHECK(thread_c->task_runner()->BelongsToCurrentThread()); |
| })) |
| .With(thread_c->task_runner()); |
| |
| scoped_refptr<SingleThreadTaskRunner> main_thread = |
| ThreadTaskRunnerHandle::Get(); |
| scoped_refptr<AbstractPromise> p5 = |
| ThenPromise(FROM_HERE, p4) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| run_loop.Quit(); |
| CHECK(main_thread->BelongsToCurrentThread()); |
| })) |
| .With(main_thread); |
| |
| p1->OnResolved(); |
| |
| EXPECT_FALSE(p5->IsResolvedForTesting()); |
| run_loop.Run(); |
| EXPECT_TRUE(p2->IsResolvedForTesting()); |
| EXPECT_TRUE(p3->IsResolvedForTesting()); |
| EXPECT_TRUE(p4->IsResolvedForTesting()); |
| EXPECT_TRUE(p5->IsResolvedForTesting()); |
| |
| thread_a->Stop(); |
| thread_b->Stop(); |
| thread_c->Stop(); |
| } |
| |
| TEST_F(AbstractPromiseTest, MutipleThreadsAddingDependants) { |
| constexpr int num_threads = 4; |
| constexpr int num_promises = 100000; |
| |
| std::unique_ptr<Thread> thread[num_threads]; |
| for (int i = 0; i < num_threads; i++) { |
| thread[i] = std::make_unique<Thread>("Test thread"); |
| thread[i]->Start(); |
| } |
| |
| scoped_refptr<AbstractPromise> root = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true); |
| |
| RunLoop run_loop; |
| std::atomic<int> pending_count(num_promises); |
| |
| // After being called |num_promises| times |decrement_cb| will quit |run_loop| |
| auto decrement_cb = BindLambdaForTesting([&](AbstractPromise* p) { |
| int count = pending_count.fetch_sub(1, std::memory_order_acq_rel); |
| if (count == 1) |
| run_loop.Quit(); |
| p->OnResolved(); |
| }); |
| |
| // Post a bunch of tasks on multiple threads that create Then promises |
| // dependent on |root| which call |decrement_cb| when resolved. |
| for (int i = 0; i < num_promises; i++) { |
| thread[i % num_threads]->task_runner()->PostTask( |
| FROM_HERE, BindLambdaForTesting([&]() { |
| scoped_refptr<AbstractPromise> p = |
| ThenPromise(FROM_HERE, root).With(decrement_cb); |
| })); |
| |
| // Mid way through post a task to resolve |root|. |
| if (i == num_promises / 2) { |
| thread[i % num_threads]->task_runner()->PostTask( |
| FROM_HERE, BindOnce(&AbstractPromise::OnResolved, root)); |
| } |
| } |
| |
| // This should exit cleanly without any TSan errors. |
| run_loop.Run(); |
| |
| for (int i = 0; i < num_threads; i++) { |
| thread[i]->Stop(); |
| } |
| } |
| |
| TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyALLModified) { |
| // Regression test to ensure cross thread rejection works as intended. Loop |
| // increaces chances of catching any bugs. |
| for (size_t i = 0; i < 1000; ++i) { |
| scoped_refptr<AbstractPromise> p1 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p2 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p3 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| scoped_refptr<AbstractPromise> p4 = |
| DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve( |
| false); |
| |
| std::vector<internal::DependentList::Node> prerequisite_list(4); |
| prerequisite_list[0].SetPrerequisite(p1.get()); |
| prerequisite_list[1].SetPrerequisite(p2.get()); |
| prerequisite_list[2].SetPrerequisite(p3.get()); |
| prerequisite_list[3].SetPrerequisite(p4.get()); |
| |
| scoped_refptr<AbstractPromise> all_promise = |
| AllPromise(FROM_HERE, std::move(prerequisite_list)) |
| .With(CallbackResultType::kCanResolveOrReject) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Rejected<void>()); |
| p->OnRejected(); |
| })); |
| |
| base::PostTask( |
| FROM_HERE, {base::ThreadPool()}, |
| base::Bind([](scoped_refptr<AbstractPromise> p2) { p2->OnRejected(); }, |
| p2)); |
| |
| RunLoop run_loop; |
| scoped_refptr<AbstractPromise> p5 = |
| CatchPromise(FROM_HERE, all_promise) |
| .With(BindLambdaForTesting([&](AbstractPromise* p) { |
| p->emplace(Resolved<void>()); |
| p->OnResolved(); |
| run_loop.Quit(); |
| })); |
| |
| p3->OnRejected(); |
| run_loop.Run(); |
| EXPECT_TRUE(all_promise->IsRejected()); |
| EXPECT_TRUE(p5->IsResolved()); |
| task_environment_.RunUntilIdle(); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace base |