// Copyright (c) 2012 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 <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_token.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_checker_impl.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
// A thread that runs a callback.
class RunCallbackThread : public SimpleThread {
public:
explicit RunCallbackThread(const Closure& callback)
: SimpleThread("RunCallbackThread"), callback_(callback) {}
private:
// SimpleThread:
void Run() override { callback_.Run(); }
const Closure callback_;
DISALLOW_COPY_AND_ASSIGN(RunCallbackThread);
};
// Runs a callback on a new thread synchronously.
void RunCallbackOnNewThreadSynchronously(const Closure& callback) {
RunCallbackThread run_callback_thread(callback);
run_callback_thread.Start();
run_callback_thread.Join();
}
void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
ASSERT_TRUE(thread_checker);
// This should bind |thread_checker| to the current thread if it wasn't
// already bound to a thread.
EXPECT_TRUE(thread_checker->CalledOnValidThread());
// Since |thread_checker| is now bound to the current thread, another call to
// CalledOnValidThread() should return true.
EXPECT_TRUE(thread_checker->CalledOnValidThread());
}
void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
ASSERT_TRUE(thread_checker);
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
void ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle(
ThreadCheckerImpl* thread_checker,
SequenceToken sequence_token) {
ThreadTaskRunnerHandle thread_task_runner_handle(
make_scoped_refptr(new TestSimpleTaskRunner));
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
ExpectNotCalledOnValidThread(thread_checker);
}
} // namespace
TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) {
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest,
AllowedSameThreadAndSequenceDifferentTasksWithThreadTaskRunnerHandle) {
ThreadTaskRunnerHandle thread_task_runner_handle(
make_scoped_refptr(new TestSimpleTaskRunner));
std::unique_ptr<ThreadCheckerImpl> thread_checker;
const SequenceToken sequence_token = SequenceToken::Create();
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
thread_checker.reset(new ThreadCheckerImpl);
}
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
EXPECT_TRUE(thread_checker->CalledOnValidThread());
}
}
TEST(ThreadCheckerTest,
AllowedSameThreadSequenceAndTaskNoThreadTaskRunnerHandle) {
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest,
DisallowedSameThreadAndSequenceDifferentTasksNoThreadTaskRunnerHandle) {
std::unique_ptr<ThreadCheckerImpl> thread_checker;
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
thread_checker.reset(new ThreadCheckerImpl);
}
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
}
TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) {
ThreadCheckerImpl thread_checker;
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectNotCalledOnValidThread, Unretained(&thread_checker)));
}
TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) {
ThreadTaskRunnerHandle thread_task_runner_handle(
make_scoped_refptr(new TestSimpleTaskRunner));
const SequenceToken sequence_token(SequenceToken::Create());
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(sequence_token);
ThreadCheckerImpl thread_checker;
EXPECT_TRUE(thread_checker.CalledOnValidThread());
RunCallbackOnNewThreadSynchronously(Bind(
&ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle,
Unretained(&thread_checker), sequence_token));
}
TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) {
std::unique_ptr<ThreadCheckerImpl> thread_checker;
ThreadTaskRunnerHandle thread_task_runner_handle(
make_scoped_refptr(new TestSimpleTaskRunner));
{
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
thread_checker.reset(new ThreadCheckerImpl);
}
{
// Different SequenceToken.
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
// No SequenceToken.
EXPECT_FALSE(thread_checker->CalledOnValidThread());
}
TEST(ThreadCheckerTest, DetachFromThread) {
ThreadCheckerImpl thread_checker;
thread_checker.DetachFromThread();
// Verify that CalledOnValidThread() returns true when called on a different
// thread after a call to DetachFromThread().
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
EXPECT_FALSE(thread_checker.CalledOnValidThread());
}
TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) {
ThreadTaskRunnerHandle thread_task_runner_handle(
make_scoped_refptr(new TestSimpleTaskRunner));
ScopedSetSequenceTokenForCurrentThread
scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
ThreadCheckerImpl thread_checker;
thread_checker.DetachFromThread();
// Verify that CalledOnValidThread() returns true when called on a different
// thread after a call to DetachFromThread().
RunCallbackOnNewThreadSynchronously(
Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
EXPECT_FALSE(thread_checker.CalledOnValidThread());
}
} // namespace base