blob: 81eb8a44eac53f18a7b9720127a6797a5a422a60 [file] [log] [blame]
// Copyright 2020 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/bind_post_task.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/sequence_checker_impl.h"
#include "base/sequenced_task_runner.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
void SetBool(bool* variable, bool value) {
*variable = value;
}
void SetInt(int* variable, int value) {
*variable = value;
}
int Multiply(int value) {
return value * 5;
}
void ClearReference(OnceClosure callback) {}
class SequenceRestrictionChecker {
public:
explicit SequenceRestrictionChecker(bool& set_on_destroy)
: set_on_destroy_(set_on_destroy) {}
~SequenceRestrictionChecker() {
EXPECT_TRUE(checker_.CalledOnValidSequence());
set_on_destroy_ = true;
}
void Run() { EXPECT_TRUE(checker_.CalledOnValidSequence()); }
private:
SequenceCheckerImpl checker_;
bool& set_on_destroy_;
};
} // namespace
class BindPostTaskTest : public testing::Test {
protected:
test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<SequencedTaskRunner> task_runner_ =
SequencedTaskRunnerHandle::Get();
};
TEST_F(BindPostTaskTest, OnceClosure) {
bool val = false;
OnceClosure cb = BindOnce(&SetBool, &val, true);
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
std::move(post_cb).Run();
EXPECT_FALSE(val);
RunLoop().RunUntilIdle();
EXPECT_TRUE(val);
}
TEST_F(BindPostTaskTest, OnceCallback) {
OnceCallback<void(bool*, bool)> cb = BindOnce(&SetBool);
OnceCallback<void(bool*, bool)> post_cb =
BindPostTask(task_runner_, std::move(cb));
bool val = false;
std::move(post_cb).Run(&val, true);
EXPECT_FALSE(val);
RunLoop().RunUntilIdle();
EXPECT_TRUE(val);
}
TEST_F(BindPostTaskTest, OnceWithIgnoreResult) {
OnceCallback<void(int)> post_cb =
BindPostTask(task_runner_, BindOnce(IgnoreResult(&Multiply)));
std::move(post_cb).Run(1);
RunLoop().RunUntilIdle();
}
TEST_F(BindPostTaskTest, OnceThen) {
int value = 0;
// Multiply() returns an int and SetInt() takes an int as a parameter.
OnceClosure then_cb =
BindOnce(&Multiply, 5)
.Then(BindPostTask(task_runner_, BindOnce(&SetInt, &value)));
std::move(then_cb).Run();
EXPECT_EQ(value, 0);
RunLoop().RunUntilIdle();
EXPECT_EQ(value, 25);
}
// Ensure that the input callback is run/destroyed on the correct thread even if
// the callback returned from BindPostTask() is run on a different thread.
TEST_F(BindPostTaskTest, OnceRunDestroyedOnBound) {
Thread target_thread("testing");
ASSERT_TRUE(target_thread.Start());
// SequenceRestrictionChecker checks it's creation, Run() and deletion all
// happen on the main thread.
bool destroyed = false;
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is run
// on a different thread which triggers a PostTask() back to the test main
// thread to invoke `cb` which runs SequenceRestrictionChecker::Run(). After
// `cb` has been invoked `checker` is destroyed along with the BindState.
OnceClosure cb =
BindOnce(&SequenceRestrictionChecker::Run, std::move(checker));
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
target_thread.task_runner()->PostTask(FROM_HERE, std::move(post_cb));
target_thread.FlushForTesting();
EXPECT_FALSE(destroyed);
RunLoop().RunUntilIdle();
EXPECT_TRUE(destroyed);
}
// Ensure that the input callback is destroyed on the correct thread even if the
// callback returned from BindPostTask() is destroyed without being run on a
// different thread.
TEST_F(BindPostTaskTest, OnceNotRunDestroyedOnBound) {
Thread target_thread("testing");
ASSERT_TRUE(target_thread.Start());
// SequenceRestrictionChecker checks it's creation and deletion all happen on
// the test main thread.
bool destroyed = false;
auto checker = std::make_unique<SequenceRestrictionChecker>(destroyed);
// `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is
// deleted on a different thread which triggers a PostTask() back to the test
// main thread to destroy `cb` and `checker`.
OnceClosure cb =
BindOnce(&SequenceRestrictionChecker::Run, std::move(checker));
OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb));
target_thread.task_runner()->PostTask(
FROM_HERE, BindOnce(&ClearReference, std::move(post_cb)));
target_thread.FlushForTesting();
EXPECT_FALSE(destroyed);
RunLoop().RunUntilIdle();
EXPECT_TRUE(destroyed);
}
} // namespace base