// 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/message_loop.h" #include "base/strings/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, base::MessageLoop::QuitWhenIdleClosure()); }); 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("", task_order_[i]) << " (i=" << i << ")"; } } } // The message loop for the test main thread. base::MessageLoop message_loop_; // The task runner under test. scoped_refptr task_runner_; // Vector that records data from TaskOrderMarker. std::vector 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", base::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, base::MessageLoop::QuitWhenIdleClosure()); })); })); 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, base::MessageLoop::QuitWhenIdleClosure()); }), 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)); }