serial_runner_unittest.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 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/bind.h"
6#include "base/debug/stack_trace.h"
7#include "base/message_loop/message_loop.h"
8#include "media/base/pipeline_status.h"
9#include "media/base/serial_runner.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace media {
13
14class SerialRunnerTest : public ::testing::Test {
15 public:
16  SerialRunnerTest()
17      : inside_start_(false), done_called_(false), done_status_(PIPELINE_OK) {}
18  virtual ~SerialRunnerTest() {}
19
20  void RunSerialRunner() {
21    message_loop_.PostTask(FROM_HERE, base::Bind(
22        &SerialRunnerTest::StartRunnerInternal, base::Unretained(this),
23        bound_fns_));
24    message_loop_.RunUntilIdle();
25  }
26
27  // Pushes a bound function to the queue that will run its callback with
28  // |status|. called(i) returns whether the i'th bound function pushed to the
29  // queue was called while running the SerialRunner.
30  void PushBoundFunction(PipelineStatus status) {
31    bound_fns_.Push(base::Bind(&SerialRunnerTest::RunBoundFunction,
32                               base::Unretained(this),
33                               status,
34                               called_.size()));
35    called_.push_back(false);
36  }
37
38  void PushBoundClosure() {
39    bound_fns_.Push(base::Bind(&SerialRunnerTest::RunBoundClosure,
40                               base::Unretained(this),
41                               called_.size()));
42    called_.push_back(false);
43  }
44
45  void PushClosure() {
46    bound_fns_.Push(base::Bind(&SerialRunnerTest::RunClosure,
47                               base::Unretained(this),
48                               called_.size()));
49    called_.push_back(false);
50  }
51
52  // Push a bound function to the queue that will delete the SerialRunner,
53  // which should cancel all remaining queued work.
54  void PushCancellation() {
55    bound_fns_.Push(base::Bind(&SerialRunnerTest::CancelSerialRunner,
56                               base::Unretained(this)));
57  }
58
59  // Queries final status of pushed functions and done callback. Valid only
60  // after calling RunSerialRunner().
61  bool called(size_t index) { return called_[index]; }
62  bool done_called() { return done_called_; }
63  PipelineStatus done_status() { return done_status_; }
64
65 private:
66  void RunBoundFunction(PipelineStatus status,
67                        size_t index,
68                        const PipelineStatusCB& status_cb) {
69    EXPECT_EQ(index == 0u, inside_start_)
70        << "First bound function should run on same stack as "
71        << "SerialRunner::Run() while all others should not\n"
72        << base::debug::StackTrace().ToString();
73
74    called_[index] = true;
75    status_cb.Run(status);
76  }
77
78  void RunBoundClosure(size_t index,
79                       const base::Closure& done_cb) {
80    EXPECT_EQ(index == 0u, inside_start_)
81        << "First bound function should run on same stack as "
82        << "SerialRunner::Run() while all others should not\n"
83        << base::debug::StackTrace().ToString();
84
85    called_[index] = true;
86    done_cb.Run();
87  }
88
89  void RunClosure(size_t index) {
90    EXPECT_EQ(index == 0u, inside_start_)
91        << "First bound function should run on same stack as "
92        << "SerialRunner::Run() while all others should not\n"
93        << base::debug::StackTrace().ToString();
94
95    called_[index] = true;
96  }
97
98  void StartRunnerInternal(const SerialRunner::Queue& bound_fns) {
99    inside_start_ = true;
100    runner_ = SerialRunner::Run(bound_fns_, base::Bind(
101        &SerialRunnerTest::DoneCallback, base::Unretained(this)));
102    inside_start_ = false;
103  }
104
105  void DoneCallback(PipelineStatus status) {
106    EXPECT_FALSE(inside_start_)
107        << "Done callback should not run on same stack as SerialRunner::Run()\n"
108        << base::debug::StackTrace().ToString();
109
110    done_called_ = true;
111    done_status_ = status;
112    message_loop_.QuitWhenIdle();
113  }
114
115  void CancelSerialRunner(const PipelineStatusCB& status_cb) {
116    // Tasks run by |runner_| shouldn't reset it, hence we post a task to do so.
117    message_loop_.PostTask(FROM_HERE, base::Bind(
118        &SerialRunnerTest::ResetSerialRunner, base::Unretained(this)));
119    status_cb.Run(PIPELINE_OK);
120  }
121
122  void ResetSerialRunner() {
123    runner_.reset();
124  }
125
126  base::MessageLoop message_loop_;
127  SerialRunner::Queue bound_fns_;
128  scoped_ptr<SerialRunner> runner_;
129
130  // Used to enforce calling stack guarantees of the API.
131  bool inside_start_;
132
133  // Tracks whether the i'th bound function was called.
134  std::vector<bool> called_;
135
136  // Tracks whether the final done callback was called + resulting status.
137  bool done_called_;
138  PipelineStatus done_status_;
139
140  DISALLOW_COPY_AND_ASSIGN(SerialRunnerTest);
141};
142
143TEST_F(SerialRunnerTest, Empty) {
144  RunSerialRunner();
145
146  EXPECT_TRUE(done_called());
147  EXPECT_EQ(PIPELINE_OK, done_status());
148}
149
150TEST_F(SerialRunnerTest, Single) {
151  PushBoundFunction(PIPELINE_OK);
152  RunSerialRunner();
153
154  EXPECT_TRUE(called(0));
155  EXPECT_TRUE(done_called());
156  EXPECT_EQ(PIPELINE_OK, done_status());
157}
158
159TEST_F(SerialRunnerTest, Single_Error) {
160  PushBoundFunction(PIPELINE_ERROR_ABORT);
161  RunSerialRunner();
162
163  EXPECT_TRUE(called(0));
164  EXPECT_TRUE(done_called());
165  EXPECT_EQ(PIPELINE_ERROR_ABORT, done_status());
166}
167
168TEST_F(SerialRunnerTest, Single_Cancel) {
169  PushBoundFunction(PIPELINE_OK);
170  PushCancellation();
171  RunSerialRunner();
172
173  EXPECT_TRUE(called(0));
174  EXPECT_FALSE(done_called());
175}
176
177TEST_F(SerialRunnerTest, Multiple) {
178  PushBoundFunction(PIPELINE_OK);
179  PushBoundFunction(PIPELINE_OK);
180  RunSerialRunner();
181
182  EXPECT_TRUE(called(0));
183  EXPECT_TRUE(called(1));
184  EXPECT_TRUE(done_called());
185  EXPECT_EQ(PIPELINE_OK, done_status());
186}
187
188TEST_F(SerialRunnerTest, Multiple_Error) {
189  PushBoundFunction(PIPELINE_ERROR_ABORT);
190  PushBoundFunction(PIPELINE_OK);
191  RunSerialRunner();
192
193  EXPECT_TRUE(called(0));
194  EXPECT_FALSE(called(1));  // A bad status cancels remaining work.
195  EXPECT_TRUE(done_called());
196  EXPECT_EQ(PIPELINE_ERROR_ABORT, done_status());
197}
198
199TEST_F(SerialRunnerTest, Multiple_Cancel) {
200  PushBoundFunction(PIPELINE_OK);
201  PushCancellation();
202  PushBoundFunction(PIPELINE_OK);
203  RunSerialRunner();
204
205  EXPECT_TRUE(called(0));
206  EXPECT_FALSE(called(1));
207  EXPECT_FALSE(done_called());
208}
209
210TEST_F(SerialRunnerTest, BoundClosure) {
211  PushBoundClosure();
212  RunSerialRunner();
213
214  EXPECT_TRUE(called(0));
215  EXPECT_TRUE(done_called());
216  EXPECT_EQ(PIPELINE_OK, done_status());
217}
218
219TEST_F(SerialRunnerTest, Closure) {
220  PushClosure();
221  RunSerialRunner();
222
223  EXPECT_TRUE(called(0));
224  EXPECT_TRUE(done_called());
225  EXPECT_EQ(PIPELINE_OK, done_status());
226}
227
228}  // namespace media
229