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