Index: base/task_scheduler/task_tracker_unittest.cc |
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc2025c1df6f6d8e4e0991d2f6232218b0af091f |
--- /dev/null |
+++ b/base/task_scheduler/task_tracker_unittest.cc |
@@ -0,0 +1,315 @@ |
+// Copyright 2016 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/task_scheduler/task_tracker.h" |
+ |
+#include <queue> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/task_scheduler/task.h" |
+#include "base/task_scheduler/task_traits.h" |
+#include "base/threading/platform_thread.h" |
+#include "base/threading/simple_thread.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace base { |
+namespace internal { |
+ |
+namespace { |
+ |
+class ThreadCallingShutdown : public SimpleThread { |
+ public: |
+ explicit ThreadCallingShutdown(TaskTracker* tracker) |
+ : SimpleThread("ThreadCallingShutdown"), tracker_(tracker) {} |
+ |
+ // Returns true once the call to Shutdown() has returned. |
+ bool has_returned() const { return has_returned_; } |
+ |
+ private: |
+ void Run() override { |
+ tracker_->Shutdown(); |
+ has_returned_ = true; |
+ } |
+ |
+ TaskTracker* const tracker_; |
+ bool has_returned_ = false; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ThreadCallingShutdown); |
+}; |
+ |
+class TaskSchedulerTaskTrackerTest |
+ : public testing::TestWithParam<TaskShutdownBehavior> { |
+ public: |
+ TaskSchedulerTaskTrackerTest() = default; |
+ |
+ void RunNextPostedTaskViaTracker() { |
+ ASSERT_FALSE(posted_tasks_.empty()); |
+ tracker_.RunTask(posted_tasks_.front().get()); |
+ posted_tasks_.pop(); |
+ } |
+ |
+ protected: |
+ // Creates a task with |shutdown_behavior|. |
+ scoped_ptr<Task> CreateTask(TaskShutdownBehavior shutdown_behavior) { |
+ return make_scoped_ptr(new Task( |
+ FROM_HERE, |
+ Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)), |
+ TaskTraits().WithShutdownBehavior(shutdown_behavior))); |
+ } |
+ |
+ // Tries to post |task| via |tracker_|. If |tracker_| approves the operation, |
+ // |task| is added to |posted_tasks_|. |
+ void PostTaskViaTracker(scoped_ptr<Task> task) { |
+ tracker_.PostTask( |
+ Bind(&TaskSchedulerTaskTrackerTest::PostTaskCallback, Unretained(this)), |
+ std::move(task)); |
+ } |
+ |
+ // Calls tracker_->Shutdown() on a new thread. When this returns, the |
+ // Shutdown() method has been entered on the new thread, but it hasn't |
+ // necessarily returned. |
+ void CallShutdownAsync() { |
+ DCHECK(!thread_calling_shutdown_.get()); |
+ thread_calling_shutdown_.reset(new ThreadCallingShutdown(&tracker_)); |
+ thread_calling_shutdown_->Start(); |
+ while (!tracker_.is_shutting_down_for_testing() && |
+ !tracker_.shutdown_completed()) { |
+ PlatformThread::YieldCurrentThread(); |
+ } |
+ } |
+ |
+ void WaitForAsyncShutdownCompleted() { |
+ DCHECK(thread_calling_shutdown_.get()); |
+ thread_calling_shutdown_->Join(); |
+ EXPECT_TRUE(thread_calling_shutdown_->has_returned()); |
+ EXPECT_TRUE(tracker_.shutdown_completed()); |
+ } |
+ |
+ void VerifyAsyncShutdownInProgress() { |
+ DCHECK(thread_calling_shutdown_.get()); |
+ EXPECT_FALSE(thread_calling_shutdown_->has_returned()); |
+ EXPECT_FALSE(tracker_.shutdown_completed()); |
+ EXPECT_TRUE(tracker_.is_shutting_down_for_testing()); |
+ } |
+ |
+ TaskTracker tracker_; |
+ size_t num_tasks_executed_ = 0; |
+ std::queue<scoped_ptr<Task>> posted_tasks_; |
+ |
+ private: |
+ void PostTaskCallback(scoped_ptr<Task> task) { |
+ posted_tasks_.push(std::move(task)); |
+ } |
+ |
+ void RunTaskCallback() { ++num_tasks_executed_; } |
+ |
+ scoped_ptr<ThreadCallingShutdown> thread_calling_shutdown_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest); |
+}; |
+ |
+// A thread which calls |
+// TaskSchedulerTaskTrackerTest::RunNextPostedTaskViaTracker() asynchronously. |
+class ThreadRunningNextPostedTask : public SimpleThread { |
+ public: |
+ explicit ThreadRunningNextPostedTask(TaskSchedulerTaskTrackerTest* test) |
+ : SimpleThread("ThreadRunningNextPostedTask"), test_(test) {} |
+ |
+ private: |
+ void Run() override { test_->RunNextPostedTaskViaTracker(); } |
+ |
+ TaskSchedulerTaskTrackerTest* const test_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ThreadRunningNextPostedTask); |
+}; |
+ |
+} // namespace |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunBeforeShutdown) { |
+ scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
+ const Task* task_to_post_raw = task_to_post.get(); |
+ |
+ // Post the task. |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ PostTaskViaTracker(std::move(task_to_post)); |
+ ASSERT_EQ(1, posted_tasks_.size()); |
gab
2016/03/17 01:42:12
1U (prefer explicitly unsigned constants to compar
fdoray
2016/03/17 20:12:16
Done.
|
+ EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
+ |
+ // Run the posted task. |
+ EXPECT_EQ(0U, num_tasks_executed_); |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(1U, num_tasks_executed_); |
+ |
+ // Shutdown() shouldn't block. |
+ tracker_.Shutdown(); |
+} |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunLongTaskBeforeShutdown) { |
+ // Post a task that will block until |event| is signaled. |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ WaitableEvent event(false, false); |
+ PostTaskViaTracker(make_scoped_ptr( |
+ new Task(FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)), |
+ TaskTraits().WithShutdownBehavior(GetParam())))); |
+ ASSERT_EQ(1, posted_tasks_.size()); |
+ |
+ // Run the task asynchronouly. |
+ ThreadRunningNextPostedTask thread(this); |
+ thread.Start(); |
+ |
+ // Initiate shutdown while the task is running. |
+ CallShutdownAsync(); |
+ |
+ if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) { |
+ // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress. |
+ WaitForAsyncShutdownCompleted(); |
+ } else { |
+ // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress. |
+ VerifyAsyncShutdownInProgress(); |
+ } |
+ |
+ // Unblock the task. |
+ event.Signal(); |
+ thread.Join(); |
+ |
+ if (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) |
+ WaitForAsyncShutdownCompleted(); |
+} |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunDuringShutdown) { |
+ scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
+ const Task* task_to_post_raw = task_to_post.get(); |
+ |
+ // Post the task. |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ PostTaskViaTracker(std::move(task_to_post)); |
+ ASSERT_EQ(1, posted_tasks_.size()); |
+ EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
+ |
+ // Post a BLOCK_SHUTDOWN task just to block shutdown. |
+ PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
+ |
+ // Call Shutdown() asynchronously. |
+ CallShutdownAsync(); |
+ VerifyAsyncShutdownInProgress(); |
+ |
+ // Try to run task posted at the beginning of this test. It should only |
+ // succeed for a BLOCK_SHUTDOWN task. |
gab
2016/03/17 01:42:13
s/It should only succeed for a BLOCK_SHUTDOWN task
fdoray
2016/03/17 20:12:16
Done.
|
+ EXPECT_EQ(0U, num_tasks_executed_); |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 1U : 0U, |
+ num_tasks_executed_); |
+ VerifyAsyncShutdownInProgress(); |
+ |
+ // Unblock shutdown by running the remaining BLOCK_SHUTDOWN task. |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, |
+ num_tasks_executed_); |
+ WaitForAsyncShutdownCompleted(); |
+} |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunAfterShutdown) { |
+ scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
+ const Task* task_to_post_raw = task_to_post.get(); |
+ |
+ // Post the task. |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ PostTaskViaTracker(std::move(task_to_post)); |
+ ASSERT_EQ(1, posted_tasks_.size()); |
+ ASSERT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
+ |
+ // Call Shutdown() asynchronously. |
+ CallShutdownAsync(); |
+ EXPECT_EQ(0U, num_tasks_executed_); |
+ |
+ if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
+ VerifyAsyncShutdownInProgress(); |
+ |
+ // Run the task to unblock shutdown. |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(1U, num_tasks_executed_); |
+ WaitForAsyncShutdownCompleted(); |
+ |
+ // Running a BLOCK_SHUTDOWN task after shutdown should fail. |
+ if (DCHECK_IS_ON()) { |
+ EXPECT_DEATH( |
gab
2016/03/17 01:42:13
Use EXPECT_DCHECK_DEATH macro from Rob's Scheduler
fdoray
2016/03/17 20:12:16
Done.
|
+ { |
+ tracker_.RunTask( |
+ CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN).get()); |
gab
2016/03/17 01:42:12
Since that task was never posted to the tracker, i
fdoray
2016/03/17 20:12:16
There is no way to test running after shutdown a B
|
+ }, |
+ ""); |
+ } |
+ } else { |
+ WaitForAsyncShutdownCompleted(); |
+ |
+ // The task shouldn't be allowed to run after shutdown. |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(0U, num_tasks_executed_); |
+ } |
+} |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunDuringShutdown) { |
+ // Post a BLOCK_SHUTDOWN task just to block shutdown. |
+ PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
+ scoped_ptr<Task> block_shutdown_task = std::move(posted_tasks_.front()); |
+ posted_tasks_.pop(); |
+ |
+ // Call Shutdown() asynchronously. |
+ CallShutdownAsync(); |
+ VerifyAsyncShutdownInProgress(); |
+ |
+ if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
+ // Post a task. This should succeed. |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ PostTaskViaTracker(CreateTask(GetParam())); |
+ EXPECT_EQ(1U, posted_tasks_.size()); |
+ |
+ // Run the task. This should succeed. |
+ EXPECT_EQ(0U, num_tasks_executed_); |
+ RunNextPostedTaskViaTracker(); |
+ EXPECT_EQ(1U, num_tasks_executed_); |
+ } else { |
+ // It shouldn't be possible to post a task which isn't BLOCK_SHUTDOWN. |
+ PostTaskViaTracker(CreateTask(GetParam())); |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ |
+ // Don't try to run the task, because it hasn't been posted successfully. |
+ } |
+ |
+ // Unblock shutdown by running the BLOCK_SHUTDOWN task posted at the beginning |
+ // of the test. |
+ VerifyAsyncShutdownInProgress(); |
+ tracker_.RunTask(block_shutdown_task.get()); |
+ EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, |
+ num_tasks_executed_); |
+ WaitForAsyncShutdownCompleted(); |
+} |
+ |
+TEST_P(TaskSchedulerTaskTrackerTest, PostAfterShutdown) { |
+ // It is not possible to post a task after shutdown. |
+ tracker_.Shutdown(); |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+ PostTaskViaTracker(CreateTask(GetParam())); |
+ EXPECT_TRUE(posted_tasks_.empty()); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ ContinueOnShutdown, |
+ TaskSchedulerTaskTrackerTest, |
+ ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); |
+INSTANTIATE_TEST_CASE_P( |
+ SkipOnShutdown, |
+ TaskSchedulerTaskTrackerTest, |
+ ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); |
+INSTANTIATE_TEST_CASE_P( |
+ BlockShutdown, |
+ TaskSchedulerTaskTrackerTest, |
+ ::testing::Values(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
+ |
+} // namespace internal |
+} // namespace base |