| Index: webrtc/base/sequenced_task_checker_unittest.cc
|
| diff --git a/webrtc/base/sequenced_task_checker_unittest.cc b/webrtc/base/sequenced_task_checker_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2450bd5175061c39eacbf3e635be37170bedaa2a
|
| --- /dev/null
|
| +++ b/webrtc/base/sequenced_task_checker_unittest.cc
|
| @@ -0,0 +1,273 @@
|
| +/*
|
| + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "webrtc/base/checks.h"
|
| +#include "webrtc/base/constructormagic.h"
|
| +#include "webrtc/base/platform_thread.h"
|
| +#include "webrtc/base/sequenced_task_checker.h"
|
| +#include "webrtc/base/task_queue.h"
|
| +
|
| +namespace rtc {
|
| +
|
| +namespace {
|
| +// Calls SequencedTaskChecker::CalledSequentially on another thread.
|
| +class CallCalledSequentiallyOnThread {
|
| + public:
|
| + CallCalledSequentiallyOnThread(bool expect_true,
|
| + SequencedTaskChecker* sequenced_task_checker)
|
| + : expect_true_(expect_true),
|
| + thread_has_run_event_(false, false),
|
| + thread_(&Run, this, "call_do_stuff_on_thread"),
|
| + sequenced_task_checker_(sequenced_task_checker) {
|
| + thread_.Start();
|
| + }
|
| + ~CallCalledSequentiallyOnThread() {
|
| + EXPECT_TRUE(thread_has_run_event_.Wait(1000));
|
| + thread_.Stop();
|
| + }
|
| +
|
| + private:
|
| + static bool Run(void* obj) {
|
| + CallCalledSequentiallyOnThread* call_stuff_on_thread =
|
| + static_cast<CallCalledSequentiallyOnThread*>(obj);
|
| + EXPECT_EQ(
|
| + call_stuff_on_thread->expect_true_,
|
| + call_stuff_on_thread->sequenced_task_checker_->CalledSequentially());
|
| + call_stuff_on_thread->thread_has_run_event_.Set();
|
| + return false;
|
| + }
|
| +
|
| + const bool expect_true_;
|
| + Event thread_has_run_event_;
|
| + PlatformThread thread_;
|
| + SequencedTaskChecker* const sequenced_task_checker_;
|
| +
|
| + RTC_DISALLOW_COPY_AND_ASSIGN(CallCalledSequentiallyOnThread);
|
| +};
|
| +
|
| +// Deletes SequencedTaskChecker on a different thread.
|
| +class DeleteSequencedCheckerOnThread {
|
| + public:
|
| + explicit DeleteSequencedCheckerOnThread(
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker)
|
| + : thread_(&Run, this, "delete_sequenced_task_checker_on_thread"),
|
| + thread_has_run_event_(false, false),
|
| + sequenced_task_checker_(std::move(sequenced_task_checker)) {
|
| + thread_.Start();
|
| + }
|
| +
|
| + ~DeleteSequencedCheckerOnThread() {
|
| + EXPECT_TRUE(thread_has_run_event_.Wait(1000));
|
| + thread_.Stop();
|
| + }
|
| +
|
| + private:
|
| + static bool Run(void* obj) {
|
| + DeleteSequencedCheckerOnThread* instance =
|
| + static_cast<DeleteSequencedCheckerOnThread*>(obj);
|
| + instance->sequenced_task_checker_.reset();
|
| + instance->thread_has_run_event_.Set();
|
| + return false;
|
| + }
|
| +
|
| + private:
|
| + PlatformThread thread_;
|
| + Event thread_has_run_event_;
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker_;
|
| +
|
| + RTC_DISALLOW_COPY_AND_ASSIGN(DeleteSequencedCheckerOnThread);
|
| +};
|
| +
|
| +void RunMethodOnDifferentThread(bool expect_true) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + CallCalledSequentiallyOnThread call_on_thread(expect_true,
|
| + sequenced_task_checker.get());
|
| +}
|
| +
|
| +void RunMethodOnDifferentTaskQueue(bool expect_true) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + static const char kQueueName[] = "MethodNotAllowedOnDifferentTq";
|
| + TaskQueue queue(kQueueName);
|
| + Event done_event(false, false);
|
| + queue.PostTask([&sequenced_task_checker, &done_event, expect_true] {
|
| + if (expect_true)
|
| + EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
|
| + else
|
| + EXPECT_FALSE(sequenced_task_checker->CalledSequentially());
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +}
|
| +
|
| +void DetachThenCallFromDifferentTaskQueue(bool expect_true) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + sequenced_task_checker->Detach();
|
| +
|
| + Event done_event(false, false);
|
| + TaskQueue queue1("DetachThenCallFromDifferentTaskQueueImpl1");
|
| + queue1.PostTask([&sequenced_task_checker, &done_event] {
|
| + EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +
|
| + // CalledSequentially should return false in debug builds after moving to
|
| + // another task queue.
|
| + TaskQueue queue2("DetachThenCallFromDifferentTaskQueueImpl2");
|
| + queue2.PostTask([&sequenced_task_checker, &done_event, expect_true] {
|
| + if (expect_true)
|
| + EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
|
| + else
|
| + EXPECT_FALSE(sequenced_task_checker->CalledSequentially());
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +}
|
| +} // namespace
|
| +
|
| +TEST(SequencedTaskCheckerTest, CallsAllowedOnSameThread) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
|
| +
|
| + // Verify that the destructor doesn't assert.
|
| + sequenced_task_checker.reset();
|
| +}
|
| +
|
| +TEST(SequencedTaskCheckerTest, DestructorAllowedOnDifferentThread) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + // Verify that the destructor doesn't assert when called on a different
|
| + // thread.
|
| + DeleteSequencedCheckerOnThread delete_on_thread(
|
| + std::move(sequenced_task_checker));
|
| +}
|
| +
|
| +TEST(SequencedTaskCheckerTest, DetachFromThread) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + sequenced_task_checker->Detach();
|
| + CallCalledSequentiallyOnThread call_on_thread(true,
|
| + sequenced_task_checker.get());
|
| +}
|
| +
|
| +TEST(SequencedTaskCheckerTest, DetachFromThreadAndUseOnTaskQueue) {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + sequenced_task_checker->Detach();
|
| + static const char kQueueName[] = "DetachFromThreadAndUseOnTaskQueue";
|
| + TaskQueue queue(kQueueName);
|
| + Event done_event(false, false);
|
| + queue.PostTask([&sequenced_task_checker, &done_event] {
|
| + EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +}
|
| +
|
| +TEST(SequencedTaskCheckerTest, DetachFromTaskQueueAndUseOnThread) {
|
| + TaskQueue queue("DetachFromTaskQueueAndUseOnThread");
|
| + Event done_event(false, false);
|
| + queue.PostTask([&done_event] {
|
| + std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
|
| + new SequencedTaskChecker());
|
| +
|
| + sequenced_task_checker->Detach();
|
| + CallCalledSequentiallyOnThread call_on_thread(true,
|
| + sequenced_task_checker.get());
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +}
|
| +
|
| +#if !NDEBUG || DCHECK_ALWAYS_ON
|
| +TEST(SequencedTaskCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) {
|
| + RunMethodOnDifferentThread(false);
|
| +}
|
| +#else
|
| +TEST(SequencedTaskCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
|
| + RunMethodOnDifferentThread(true);
|
| +}
|
| +#endif
|
| +
|
| +#if !NDEBUG || DCHECK_ALWAYS_ON
|
| +TEST(SequencedTaskCheckerTest, MethodNotAllowedOnDifferentTaskQueueInDebug) {
|
| + RunMethodOnDifferentTaskQueue(false);
|
| +}
|
| +#else
|
| +TEST(SequencedTaskCheckerTest, MethodAllowedOnDifferentTaskQueueInRelease) {
|
| + RunMethodOnDifferentTaskQueue(true);
|
| +}
|
| +#endif
|
| +
|
| +#if !NDEBUG || DCHECK_ALWAYS_ON
|
| +TEST(SequencedTaskCheckerTest, DetachFromTaskQueueInDebug) {
|
| + DetachThenCallFromDifferentTaskQueue(false);
|
| +}
|
| +#else
|
| +TEST(SequencedTaskCheckerTest, DetachFromTaskQueueInRelease) {
|
| + DetachThenCallFromDifferentTaskQueue(true);
|
| +}
|
| +#endif
|
| +
|
| +class TestAnnotations {
|
| + public:
|
| + TestAnnotations() : test_var_(false) {}
|
| +
|
| + void ModifyTestVar() {
|
| + RTC_DCHECK_CALLED_SEQUENTIALLY(&checker_);
|
| + test_var_ = true;
|
| + }
|
| +
|
| + private:
|
| + bool test_var_ GUARDED_BY(&checker_);
|
| + SequencedTaskChecker checker_;
|
| +};
|
| +
|
| +TEST(SequencedTaskCheckerTest, TestAnnotations) {
|
| + TestAnnotations annotations;
|
| + annotations.ModifyTestVar();
|
| +}
|
| +
|
| +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
| +
|
| +void TestAnnotationsOnWrongQueue() {
|
| + TestAnnotations annotations;
|
| + static const char kQueueName[] = "TestAnnotationsOnWrongQueueDebug";
|
| + TaskQueue queue(kQueueName);
|
| + Event done_event(false, false);
|
| + queue.PostTask([&annotations, &done_event] {
|
| + annotations.ModifyTestVar();
|
| + done_event.Set();
|
| + });
|
| + EXPECT_TRUE(done_event.Wait(1000));
|
| +}
|
| +
|
| +#if RTC_DCHECK_IS_ON
|
| +TEST(SequencedTaskCheckerTest, TestAnnotationsOnWrongQueueDebug) {
|
| + ASSERT_DEATH({ TestAnnotationsOnWrongQueue(); }, "");
|
| +}
|
| +#else
|
| +TEST(SequencedTaskCheckerTest, TestAnnotationsOnWrongQueueRelease) {
|
| + TestAnnotationsOnWrongQueue();
|
| +}
|
| +#endif
|
| +#endif // GTEST_HAS_DEATH_TEST
|
| +} // namespace rtc
|
|
|