| // 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 "base/mac/libdispatch_task_runner.h" |
| |
| #include "base/bind.h" |
| #include "base/mac/bind_objc_block.h" |
| #include "base/message_loop.h" |
| #include "base/stringprintf.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| class LibDispatchTaskRunnerTest : public testing::Test { |
| public: |
| virtual void SetUp() OVERRIDE { |
| task_runner_ = new base::mac::LibDispatchTaskRunner( |
| "org.chromium.LibDispatchTaskRunnerTest"); |
| } |
| |
| // DispatchLastTask is used to run the main test thread's MessageLoop until |
| // all non-delayed tasks are run on the LibDispatchTaskRunner. |
| void DispatchLastTask() { |
| dispatch_async(task_runner_->GetDispatchQueue(), ^{ |
| (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| }); |
| message_loop_.Run(); |
| task_runner_->Shutdown(); |
| } |
| |
| // VerifyTaskOrder takes the expectations from TaskOrderMarkers and compares |
| // them against the recorded values. |
| void VerifyTaskOrder(const char* const expectations[], |
| size_t num_expectations) { |
| size_t actual_size = task_order_.size(); |
| |
| for (size_t i = 0; i < num_expectations; ++i) { |
| if (i >= actual_size) { |
| EXPECT_LE(i, actual_size) << "Expected " << expectations[i]; |
| continue; |
| } |
| |
| EXPECT_EQ(expectations[i], task_order_[i]); |
| } |
| |
| if (actual_size > num_expectations) { |
| EXPECT_LE(actual_size, num_expectations) << "Extra tasks were run:"; |
| for (size_t i = num_expectations; i < actual_size; ++i) { |
| EXPECT_EQ("<none>", task_order_[i]) << " (i=" << i << ")"; |
| } |
| } |
| } |
| |
| // The message loop for the test main thread. |
| MessageLoop message_loop_; |
| |
| // The task runner under test. |
| scoped_refptr<base::mac::LibDispatchTaskRunner> task_runner_; |
| |
| // Vector that records data from TaskOrderMarker. |
| std::vector<std::string> task_order_; |
| }; |
| |
| // Scoper that records the beginning and end of a running task. |
| class TaskOrderMarker { |
| public: |
| TaskOrderMarker(LibDispatchTaskRunnerTest* test, const std::string& name) |
| : test_(test), |
| name_(name) { |
| test->task_order_.push_back(std::string("BEGIN ") + name); |
| } |
| ~TaskOrderMarker() { |
| test_->task_order_.push_back(std::string("END ") + name_); |
| } |
| |
| private: |
| LibDispatchTaskRunnerTest* test_; |
| std::string name_; |
| }; |
| |
| void RecordTaskOrder(LibDispatchTaskRunnerTest* test, const std::string& name) { |
| TaskOrderMarker marker(test, name); |
| } |
| |
| // Returns a closure that records the task order. |
| base::Closure BoundRecordTaskOrder(LibDispatchTaskRunnerTest* test, |
| const std::string& name) { |
| return base::Bind(&RecordTaskOrder, base::Unretained(test), name); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, PostTask) { |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Basic Task")); |
| DispatchLastTask(); |
| const char* const expectations[] = { |
| "BEGIN Basic Task", |
| "END Basic Task" |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, PostTaskWithinTask) { |
| task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, "Outer"); |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Inner")); |
| })); |
| DispatchLastTask(); |
| |
| const char* const expectations[] = { |
| "BEGIN Outer", |
| "END Outer", |
| "BEGIN Inner", |
| "END Inner" |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, NoMessageLoop) { |
| task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, |
| base::StringPrintf("MessageLoop = %p", MessageLoop::current())); |
| })); |
| DispatchLastTask(); |
| |
| const char* const expectations[] = { |
| "BEGIN MessageLoop = 0x0", |
| "END MessageLoop = 0x0" |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, DispatchAndPostTasks) { |
| dispatch_async(task_runner_->GetDispatchQueue(), ^{ |
| TaskOrderMarker marker(this, "First Block"); |
| }); |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First Task")); |
| dispatch_async(task_runner_->GetDispatchQueue(), ^{ |
| TaskOrderMarker marker(this, "Second Block"); |
| }); |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second Task")); |
| DispatchLastTask(); |
| |
| const char* const expectations[] = { |
| "BEGIN First Block", |
| "END First Block", |
| "BEGIN First Task", |
| "END First Task", |
| "BEGIN Second Block", |
| "END Second Block", |
| "BEGIN Second Task", |
| "END Second Task", |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, NonNestable) { |
| task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, "First"); |
| task_runner_->PostNonNestableTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, "Second NonNestable"); |
| (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| })); |
| })); |
| message_loop_.Run(); |
| task_runner_->Shutdown(); |
| |
| const char* const expectations[] = { |
| "BEGIN First", |
| "END First", |
| "BEGIN Second NonNestable", |
| "END Second NonNestable" |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, PostDelayed) { |
| base::TimeTicks post_time; |
| __block base::TimeTicks run_time; |
| const base::TimeDelta delta = base::TimeDelta::FromMilliseconds(50); |
| |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First")); |
| post_time = base::TimeTicks::Now(); |
| task_runner_->PostDelayedTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, "Timed"); |
| run_time = base::TimeTicks::Now(); |
| (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| }), delta); |
| task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second")); |
| message_loop_.Run(); |
| task_runner_->Shutdown(); |
| |
| const char* const expectations[] = { |
| "BEGIN First", |
| "END First", |
| "BEGIN Second", |
| "END Second", |
| "BEGIN Timed", |
| "END Timed", |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| |
| EXPECT_GE(run_time, post_time + delta); |
| } |
| |
| TEST_F(LibDispatchTaskRunnerTest, PostAfterShutdown) { |
| EXPECT_TRUE(task_runner_->PostTask(FROM_HERE, |
| BoundRecordTaskOrder(this, "First"))); |
| EXPECT_TRUE(task_runner_->PostTask(FROM_HERE, |
| BoundRecordTaskOrder(this, "Second"))); |
| task_runner_->Shutdown(); |
| EXPECT_FALSE(task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ |
| TaskOrderMarker marker(this, "Not Run"); |
| ADD_FAILURE() << "Should not run a task after Shutdown"; |
| }))); |
| |
| const char* const expectations[] = { |
| "BEGIN First", |
| "END First", |
| "BEGIN Second", |
| "END Second" |
| }; |
| VerifyTaskOrder(expectations, arraysize(expectations)); |
| } |