blob: b878eafd91189b9336de29e36e9cfe190dc21426 [file] [log] [blame]
// 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