| // Copyright (c) 2018 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 "chromeos/components/nearby/scheduled_executor_impl.h" |
| |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_forward.h" |
| #include "base/synchronization/lock.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/unguessable_token.h" |
| #include "chromeos/components/nearby/library/cancelable.h" |
| #include "chromeos/components/nearby/library/runnable.h" |
| #include "chromeos/components/nearby/library/scheduled_executor.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromeos { |
| |
| namespace nearby { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kDefaultDelayTimeDelta = |
| base::TimeDelta::FromMinutes(10); |
| |
| class SimpleRunnable : public location::nearby::Runnable { |
| public: |
| explicit SimpleRunnable(base::OnceClosure closure) |
| : closure_(std::move(closure)) {} |
| ~SimpleRunnable() override = default; |
| |
| void run() override { |
| EXPECT_FALSE(closure_.is_null()); |
| std::move(closure_).Run(); |
| } |
| |
| private: |
| base::OnceClosure closure_; |
| }; |
| |
| } // namespace |
| |
| class ScheduledExecutorImplTest : public testing::Test { |
| protected: |
| ScheduledExecutorImplTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME), |
| scheduled_executor_(std::make_unique<ScheduledExecutorImpl>( |
| scoped_task_environment_.GetMainThreadTaskRunner())) {} |
| |
| ~ScheduledExecutorImplTest() override = default; |
| |
| std::shared_ptr<location::nearby::Cancelable> PostRunnableWithIdAndDelay( |
| const base::UnguessableToken& id, |
| base::TimeDelta delay) { |
| std::shared_ptr<location::nearby::Cancelable> cancelable = |
| scheduled_executor_->schedule(CreateRunnable(id), |
| delay.InMilliseconds()); |
| |
| // In order to make thread-safe calls to the API of base::OneShotTimer, |
| // schedule() will post a task to an internal base::SequencedTaskRunner that |
| // calls Start() on a base::OneShotTimer. Executing RunUntilIdle() simply |
| // ensures that the base::OneShotTimer associated with the Runnable has been |
| // Start()ed, but offers no guarantee on whether the Runnable has been run() |
| // or not. |
| scoped_task_environment_.RunUntilIdle(); |
| |
| return cancelable; |
| } |
| |
| void CancelTaskAndVerifyState( |
| std::shared_ptr<location::nearby::Cancelable> cancelable, |
| bool should_expect_success) { |
| EXPECT_EQ(should_expect_success, cancelable->cancel()); |
| |
| // Ensures that the base::OneShotTimer associated with the given Cancelable |
| // has been Stop()ped before this method returns. |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| void VerifySetContainsId(const base::UnguessableToken& id) { |
| base::AutoLock al(id_set_lock_); |
| EXPECT_NE(id_set_.end(), id_set_.find(id)); |
| } |
| |
| size_t GetSetSize() { |
| base::AutoLock al(id_set_lock_); |
| return id_set_.size(); |
| } |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| std::unique_ptr<location::nearby::ScheduledExecutor> scheduled_executor_; |
| |
| private: |
| std::shared_ptr<location::nearby::Runnable> CreateRunnable( |
| const base::UnguessableToken& id) { |
| return std::make_shared<SimpleRunnable>(base::BindOnce( |
| &ScheduledExecutorImplTest::AddIdToSet, base::Unretained(this), id)); |
| } |
| |
| void AddIdToSet(const base::UnguessableToken& id) { |
| base::AutoLock al(id_set_lock_); |
| id_set_.insert(id); |
| } |
| |
| base::Lock id_set_lock_; |
| std::set<base::UnguessableToken> id_set_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScheduledExecutorImplTest); |
| }; |
| |
| TEST_F(ScheduledExecutorImplTest, SingleTaskExecutes) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta); |
| EXPECT_EQ(1u, GetSetSize()); |
| VerifySetContainsId(id); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, StaggeredTasksExecute) { |
| base::UnguessableToken id0 = base::UnguessableToken::Create(); |
| base::UnguessableToken id1 = base::UnguessableToken::Create(); |
| PostRunnableWithIdAndDelay(id0, kDefaultDelayTimeDelta); |
| PostRunnableWithIdAndDelay(id1, kDefaultDelayTimeDelta * 2); |
| |
| // Only the first scheduled task should run at first. |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta); |
| EXPECT_EQ(1u, GetSetSize()); |
| VerifySetContainsId(id0); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta); |
| EXPECT_EQ(2u, GetSetSize()); |
| VerifySetContainsId(id1); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, SingleTaskCancels) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| CancelTaskAndVerifyState(cancelable, true /* should_expect_success */); |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(0u, GetSetSize()); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FirstTaskCancelsAndSecondTaskExecutes) { |
| base::UnguessableToken id0 = base::UnguessableToken::Create(); |
| base::UnguessableToken id1 = base::UnguessableToken::Create(); |
| auto cancelable0 = PostRunnableWithIdAndDelay(id0, kDefaultDelayTimeDelta); |
| PostRunnableWithIdAndDelay(id1, kDefaultDelayTimeDelta * 3); |
| |
| CancelTaskAndVerifyState(cancelable0, true /* should_expect_success */); |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(0u, GetSetSize()); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(1u, GetSetSize()); |
| VerifySetContainsId(id1); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToCancelAfterRun) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| CancelTaskAndVerifyState(cancelable, false /* should_expect_success */); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToRunAfterCancel) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| CancelTaskAndVerifyState(cancelable, true /* should_expect_success */); |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(0u, GetSetSize()); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToCancelAfterCancel) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| // The first call should successfully cancel the task. Subsequent invocations |
| // will return false by default, as CancelableTask uses a base::OnceClosure |
| // that will be consumed after the first call to cancel(). |
| CancelTaskAndVerifyState(cancelable, true /* should_expect_success */); |
| CancelTaskAndVerifyState(cancelable, false /* should_expect_success */); |
| CancelTaskAndVerifyState(cancelable, false /* should_expect_success */); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToCancelAfterExecutorIsDestroyed) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| scheduled_executor_.reset(); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| CancelTaskAndVerifyState(cancelable, false /* should_expect_success */); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToScheduleAfterShutdown) { |
| scheduled_executor_->shutdown(); |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(0u, GetSetSize()); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, FailToCancelAfterShutdown) { |
| scheduled_executor_->shutdown(); |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| CancelTaskAndVerifyState(cancelable, false /* should_expect_success */); |
| } |
| |
| TEST_F(ScheduledExecutorImplTest, ShutdownAllowsExistingTaskToComplete) { |
| base::UnguessableToken id = base::UnguessableToken::Create(); |
| auto cancelable = PostRunnableWithIdAndDelay(id, kDefaultDelayTimeDelta); |
| scheduled_executor_->shutdown(); |
| |
| scoped_task_environment_.FastForwardBy(kDefaultDelayTimeDelta * 2); |
| EXPECT_EQ(1u, GetSetSize()); |
| VerifySetContainsId(id); |
| } |
| |
| } // namespace nearby |
| |
| } // namespace chromeos |