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
12 namespace media {
13
14 class SerialRunnerTest : public ::testing::Test {
15 public:
SerialRunnerTest()16 SerialRunnerTest()
17 : inside_start_(false), done_called_(false), done_status_(PIPELINE_OK) {}
~SerialRunnerTest()18 virtual ~SerialRunnerTest() {}
19
RunSerialRunner()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.
PushBoundFunction(PipelineStatus status)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
PushBoundClosure()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
PushClosure()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.
PushCancellation()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().
called(size_t index)61 bool called(size_t index) { return called_[index]; }
done_called()62 bool done_called() { return done_called_; }
done_status()63 PipelineStatus done_status() { return done_status_; }
64
65 private:
RunBoundFunction(PipelineStatus status,size_t index,const PipelineStatusCB & status_cb)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
RunBoundClosure(size_t index,const base::Closure & done_cb)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
RunClosure(size_t index)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
StartRunnerInternal(const SerialRunner::Queue & bound_fns)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
DoneCallback(PipelineStatus status)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
CancelSerialRunner(const PipelineStatusCB & status_cb)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
ResetSerialRunner()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
TEST_F(SerialRunnerTest,Empty)143 TEST_F(SerialRunnerTest, Empty) {
144 RunSerialRunner();
145
146 EXPECT_TRUE(done_called());
147 EXPECT_EQ(PIPELINE_OK, done_status());
148 }
149
TEST_F(SerialRunnerTest,Single)150 TEST_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
TEST_F(SerialRunnerTest,Single_Error)159 TEST_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
TEST_F(SerialRunnerTest,Single_Cancel)168 TEST_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
TEST_F(SerialRunnerTest,Multiple)177 TEST_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
TEST_F(SerialRunnerTest,Multiple_Error)188 TEST_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
TEST_F(SerialRunnerTest,Multiple_Cancel)199 TEST_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
TEST_F(SerialRunnerTest,BoundClosure)210 TEST_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
TEST_F(SerialRunnerTest,Closure)219 TEST_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