1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/mac/libdispatch_task_runner.h" 6 7#include "base/bind.h" 8#include "base/mac/bind_objc_block.h" 9#include "base/message_loop/message_loop.h" 10#include "base/strings/stringprintf.h" 11#include "testing/gtest/include/gtest/gtest.h" 12 13class LibDispatchTaskRunnerTest : public testing::Test { 14 public: 15 virtual void SetUp() OVERRIDE { 16 task_runner_ = new base::mac::LibDispatchTaskRunner( 17 "org.chromium.LibDispatchTaskRunnerTest"); 18 } 19 20 // DispatchLastTask is used to run the main test thread's MessageLoop until 21 // all non-delayed tasks are run on the LibDispatchTaskRunner. 22 void DispatchLastTask() { 23 dispatch_async(task_runner_->GetDispatchQueue(), ^{ 24 message_loop_.PostTask(FROM_HERE, 25 base::MessageLoop::QuitWhenIdleClosure()); 26 }); 27 message_loop_.Run(); 28 task_runner_->Shutdown(); 29 } 30 31 // VerifyTaskOrder takes the expectations from TaskOrderMarkers and compares 32 // them against the recorded values. 33 void VerifyTaskOrder(const char* const expectations[], 34 size_t num_expectations) { 35 size_t actual_size = task_order_.size(); 36 37 for (size_t i = 0; i < num_expectations; ++i) { 38 if (i >= actual_size) { 39 EXPECT_LE(i, actual_size) << "Expected " << expectations[i]; 40 continue; 41 } 42 43 EXPECT_EQ(expectations[i], task_order_[i]); 44 } 45 46 if (actual_size > num_expectations) { 47 EXPECT_LE(actual_size, num_expectations) << "Extra tasks were run:"; 48 for (size_t i = num_expectations; i < actual_size; ++i) { 49 EXPECT_EQ("<none>", task_order_[i]) << " (i=" << i << ")"; 50 } 51 } 52 } 53 54 // The message loop for the test main thread. 55 base::MessageLoop message_loop_; 56 57 // The task runner under test. 58 scoped_refptr<base::mac::LibDispatchTaskRunner> task_runner_; 59 60 // Vector that records data from TaskOrderMarker. 61 std::vector<std::string> task_order_; 62}; 63 64// Scoper that records the beginning and end of a running task. 65class TaskOrderMarker { 66 public: 67 TaskOrderMarker(LibDispatchTaskRunnerTest* test, const std::string& name) 68 : test_(test), 69 name_(name) { 70 test->task_order_.push_back(std::string("BEGIN ") + name); 71 } 72 ~TaskOrderMarker() { 73 test_->task_order_.push_back(std::string("END ") + name_); 74 } 75 76 private: 77 LibDispatchTaskRunnerTest* test_; 78 std::string name_; 79}; 80 81void RecordTaskOrder(LibDispatchTaskRunnerTest* test, const std::string& name) { 82 TaskOrderMarker marker(test, name); 83} 84 85// Returns a closure that records the task order. 86base::Closure BoundRecordTaskOrder(LibDispatchTaskRunnerTest* test, 87 const std::string& name) { 88 return base::Bind(&RecordTaskOrder, base::Unretained(test), name); 89} 90 91TEST_F(LibDispatchTaskRunnerTest, PostTask) { 92 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Basic Task")); 93 DispatchLastTask(); 94 const char* const expectations[] = { 95 "BEGIN Basic Task", 96 "END Basic Task" 97 }; 98 VerifyTaskOrder(expectations, arraysize(expectations)); 99} 100 101TEST_F(LibDispatchTaskRunnerTest, PostTaskWithinTask) { 102 task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ 103 TaskOrderMarker marker(this, "Outer"); 104 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Inner")); 105 })); 106 DispatchLastTask(); 107 108 const char* const expectations[] = { 109 "BEGIN Outer", 110 "END Outer", 111 "BEGIN Inner", 112 "END Inner" 113 }; 114 VerifyTaskOrder(expectations, arraysize(expectations)); 115} 116 117TEST_F(LibDispatchTaskRunnerTest, NoMessageLoop) { 118 task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ 119 TaskOrderMarker marker(this, 120 base::StringPrintf("MessageLoop = %p", base::MessageLoop::current())); 121 })); 122 DispatchLastTask(); 123 124 const char* const expectations[] = { 125 "BEGIN MessageLoop = 0x0", 126 "END MessageLoop = 0x0" 127 }; 128 VerifyTaskOrder(expectations, arraysize(expectations)); 129} 130 131TEST_F(LibDispatchTaskRunnerTest, DispatchAndPostTasks) { 132 dispatch_async(task_runner_->GetDispatchQueue(), ^{ 133 TaskOrderMarker marker(this, "First Block"); 134 }); 135 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First Task")); 136 dispatch_async(task_runner_->GetDispatchQueue(), ^{ 137 TaskOrderMarker marker(this, "Second Block"); 138 }); 139 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second Task")); 140 DispatchLastTask(); 141 142 const char* const expectations[] = { 143 "BEGIN First Block", 144 "END First Block", 145 "BEGIN First Task", 146 "END First Task", 147 "BEGIN Second Block", 148 "END Second Block", 149 "BEGIN Second Task", 150 "END Second Task", 151 }; 152 VerifyTaskOrder(expectations, arraysize(expectations)); 153} 154 155TEST_F(LibDispatchTaskRunnerTest, NonNestable) { 156 task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ 157 TaskOrderMarker marker(this, "First"); 158 task_runner_->PostNonNestableTask(FROM_HERE, base::BindBlock(^{ 159 TaskOrderMarker marker(this, "Second NonNestable"); 160 message_loop_.PostTask(FROM_HERE, 161 base::MessageLoop::QuitWhenIdleClosure()); 162 })); 163 })); 164 message_loop_.Run(); 165 task_runner_->Shutdown(); 166 167 const char* const expectations[] = { 168 "BEGIN First", 169 "END First", 170 "BEGIN Second NonNestable", 171 "END Second NonNestable" 172 }; 173 VerifyTaskOrder(expectations, arraysize(expectations)); 174} 175 176TEST_F(LibDispatchTaskRunnerTest, PostDelayed) { 177 base::TimeTicks post_time; 178 __block base::TimeTicks run_time; 179 const base::TimeDelta delta = base::TimeDelta::FromMilliseconds(50); 180 181 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First")); 182 post_time = base::TimeTicks::Now(); 183 task_runner_->PostDelayedTask(FROM_HERE, base::BindBlock(^{ 184 TaskOrderMarker marker(this, "Timed"); 185 run_time = base::TimeTicks::Now(); 186 message_loop_.PostTask(FROM_HERE, 187 base::MessageLoop::QuitWhenIdleClosure()); 188 }), delta); 189 task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second")); 190 message_loop_.Run(); 191 task_runner_->Shutdown(); 192 193 const char* const expectations[] = { 194 "BEGIN First", 195 "END First", 196 "BEGIN Second", 197 "END Second", 198 "BEGIN Timed", 199 "END Timed", 200 }; 201 VerifyTaskOrder(expectations, arraysize(expectations)); 202 203 EXPECT_GE(run_time, post_time + delta); 204} 205 206TEST_F(LibDispatchTaskRunnerTest, PostAfterShutdown) { 207 EXPECT_TRUE(task_runner_->PostTask(FROM_HERE, 208 BoundRecordTaskOrder(this, "First"))); 209 EXPECT_TRUE(task_runner_->PostTask(FROM_HERE, 210 BoundRecordTaskOrder(this, "Second"))); 211 task_runner_->Shutdown(); 212 EXPECT_FALSE(task_runner_->PostTask(FROM_HERE, base::BindBlock(^{ 213 TaskOrderMarker marker(this, "Not Run"); 214 ADD_FAILURE() << "Should not run a task after Shutdown"; 215 }))); 216 217 const char* const expectations[] = { 218 "BEGIN First", 219 "END First", 220 "BEGIN Second", 221 "END Second" 222 }; 223 VerifyTaskOrder(expectations, arraysize(expectations)); 224} 225