1// Copyright (c) 2013 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/deferred_sequenced_task_runner.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/bind_helpers.h"
10#include "base/memory/ref_counted.h"
11#include "base/message_loop/message_loop.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/threading/non_thread_safe.h"
14#include "base/threading/thread.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace {
19
20class DeferredSequencedTaskRunnerTest : public testing::Test,
21                                        public base::NonThreadSafe {
22 public:
23  class ExecuteTaskOnDestructor :
24      public base::RefCounted<ExecuteTaskOnDestructor> {
25   public:
26    ExecuteTaskOnDestructor(
27        DeferredSequencedTaskRunnerTest* executor,
28        int task_id)
29        : executor_(executor),
30          task_id_(task_id) {
31    }
32  private:
33    friend class base::RefCounted<ExecuteTaskOnDestructor>;
34    virtual ~ExecuteTaskOnDestructor() {
35      executor_->ExecuteTask(task_id_);
36    }
37    DeferredSequencedTaskRunnerTest* executor_;
38    int task_id_;
39  };
40
41  void ExecuteTask(int task_id) {
42    base::AutoLock lock(lock_);
43    executed_task_ids_.push_back(task_id);
44  }
45
46  void PostExecuteTask(int task_id) {
47    runner_->PostTask(FROM_HERE,
48                      base::Bind(&DeferredSequencedTaskRunnerTest::ExecuteTask,
49                                 base::Unretained(this),
50                                 task_id));
51  }
52
53  void StartRunner() {
54    runner_->Start();
55  }
56
57  void DoNothing(ExecuteTaskOnDestructor* object) {
58  }
59
60 protected:
61  DeferredSequencedTaskRunnerTest() :
62      loop_(),
63      runner_(
64          new base::DeferredSequencedTaskRunner(loop_.message_loop_proxy())) {
65  }
66
67  base::MessageLoop loop_;
68  scoped_refptr<base::DeferredSequencedTaskRunner> runner_;
69  mutable base::Lock lock_;
70  std::vector<int> executed_task_ids_;
71};
72
73TEST_F(DeferredSequencedTaskRunnerTest, Stopped) {
74  PostExecuteTask(1);
75  loop_.RunUntilIdle();
76  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
77}
78
79TEST_F(DeferredSequencedTaskRunnerTest, Start) {
80  StartRunner();
81  PostExecuteTask(1);
82  loop_.RunUntilIdle();
83  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
84}
85
86TEST_F(DeferredSequencedTaskRunnerTest, StartWithMultipleElements) {
87  StartRunner();
88  for (int i = 1; i < 5; ++i)
89    PostExecuteTask(i);
90
91  loop_.RunUntilIdle();
92  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4));
93}
94
95TEST_F(DeferredSequencedTaskRunnerTest, DeferredStart) {
96  PostExecuteTask(1);
97  loop_.RunUntilIdle();
98  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
99
100  StartRunner();
101  loop_.RunUntilIdle();
102  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
103
104  PostExecuteTask(2);
105  loop_.RunUntilIdle();
106  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2));
107}
108
109TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleElements) {
110  for (int i = 1; i < 5; ++i)
111    PostExecuteTask(i);
112  loop_.RunUntilIdle();
113  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
114
115  StartRunner();
116  for (int i = 5; i < 9; ++i)
117    PostExecuteTask(i);
118  loop_.RunUntilIdle();
119  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
120}
121
122TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleThreads) {
123  {
124    base::Thread thread1("DeferredSequencedTaskRunnerTestThread1");
125    base::Thread thread2("DeferredSequencedTaskRunnerTestThread2");
126    thread1.Start();
127    thread2.Start();
128    for (int i = 0; i < 5; ++i) {
129      thread1.message_loop()->PostTask(
130          FROM_HERE,
131          base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
132                     base::Unretained(this),
133                     2 * i));
134      thread2.message_loop()->PostTask(
135          FROM_HERE,
136          base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
137                     base::Unretained(this),
138                     2 * i + 1));
139      if (i == 2) {
140        thread1.message_loop()->PostTask(
141            FROM_HERE,
142            base::Bind(&DeferredSequencedTaskRunnerTest::StartRunner,
143                       base::Unretained(this)));
144      }
145    }
146  }
147
148  loop_.RunUntilIdle();
149  EXPECT_THAT(executed_task_ids_,
150      testing::WhenSorted(testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)));
151}
152
153TEST_F(DeferredSequencedTaskRunnerTest, ObjectDestructionOrder) {
154  {
155    base::Thread thread("DeferredSequencedTaskRunnerTestThread");
156    thread.Start();
157    runner_ =
158        new base::DeferredSequencedTaskRunner(thread.message_loop_proxy());
159    for (int i = 0; i < 5; ++i) {
160      {
161        // Use a block to ensure that no reference to |short_lived_object|
162        // is kept on the main thread after it is posted to |runner_|.
163        scoped_refptr<ExecuteTaskOnDestructor> short_lived_object =
164            new ExecuteTaskOnDestructor(this, 2 * i);
165        runner_->PostTask(
166            FROM_HERE,
167            base::Bind(&DeferredSequencedTaskRunnerTest::DoNothing,
168                       base::Unretained(this),
169                       short_lived_object));
170      }
171      // |short_lived_object| with id |2 * i| should be destroyed before the
172      // task |2 * i + 1| is executed.
173      PostExecuteTask(2 * i + 1);
174    }
175    StartRunner();
176  }
177
178  // All |short_lived_object| with id |2 * i| are destroyed before the task
179  // |2 * i + 1| is executed.
180  EXPECT_THAT(executed_task_ids_,
181              testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
182}
183
184}  // namespace
185