balanced_media_task_runner_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 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 <list>
6#include <vector>
7
8#include "base/basictypes.h"
9#include "base/bind.h"
10#include "base/memory/ref_counted.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/threading/thread.h"
13#include "base/time/time.h"
14#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
15#include "chromecast/media/cma/base/media_task_runner.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace chromecast {
19namespace media {
20
21namespace {
22
23struct MediaTaskRunnerTestContext {
24  MediaTaskRunnerTestContext();
25  ~MediaTaskRunnerTestContext();
26
27  scoped_refptr<MediaTaskRunner> media_task_runner;
28
29  bool is_pending_task;
30
31  std::vector<base::TimeDelta> task_timestamp_list;
32
33  size_t task_index;
34  base::TimeDelta max_timestamp;
35};
36
37MediaTaskRunnerTestContext::MediaTaskRunnerTestContext() {
38}
39
40MediaTaskRunnerTestContext::~MediaTaskRunnerTestContext() {
41}
42
43}  // namespace
44
45class BalancedMediaTaskRunnerTest : public testing::Test {
46 public:
47  BalancedMediaTaskRunnerTest();
48  virtual ~BalancedMediaTaskRunnerTest();
49
50  void SetupTest(base::TimeDelta max_delta,
51                 const std::vector<std::vector<int> >& timestamps_in_ms,
52                 const std::vector<size_t>& pattern,
53                 const std::vector<int>& expected_task_timestamps_ms);
54  void ProcessAllTasks();
55
56 protected:
57  // Expected task order based on their timestamps.
58  std::list<base::TimeDelta> expected_task_timestamps_;
59
60 private:
61  void ScheduleTask();
62  void Task(size_t task_runner_id, base::TimeDelta timestamp);
63
64  void OnTestTimeout();
65
66  scoped_refptr<BalancedMediaTaskRunnerFactory> media_task_runner_factory_;
67
68  // Schedule first a task on media task runner #scheduling_pattern[0]
69  // then a task on media task runner #scheduling_pattern[1] and so on.
70  // Wrap around when reaching the end of the pattern.
71  std::vector<size_t> scheduling_pattern_;
72  size_t pattern_index_;
73
74  // For each media task runner, keep a track of which task has already been
75  // scheduled.
76  std::vector<MediaTaskRunnerTestContext> contexts_;
77
78  DISALLOW_COPY_AND_ASSIGN(BalancedMediaTaskRunnerTest);
79};
80
81BalancedMediaTaskRunnerTest::BalancedMediaTaskRunnerTest() {
82}
83
84BalancedMediaTaskRunnerTest::~BalancedMediaTaskRunnerTest() {
85}
86
87void BalancedMediaTaskRunnerTest::SetupTest(
88    base::TimeDelta max_delta,
89    const std::vector<std::vector<int> >& timestamps_in_ms,
90    const std::vector<size_t>& pattern,
91    const std::vector<int>& expected_task_timestamps_ms) {
92  media_task_runner_factory_ = new BalancedMediaTaskRunnerFactory(max_delta);
93
94  scheduling_pattern_ = pattern;
95  pattern_index_ = 0;
96
97  // Setup each task runner.
98  size_t n = timestamps_in_ms.size();
99  contexts_.resize(n);
100  for (size_t k = 0; k < n; k++) {
101    contexts_[k].media_task_runner =
102        media_task_runner_factory_->CreateMediaTaskRunner(
103            base::MessageLoopProxy::current());
104    contexts_[k].is_pending_task = false;
105    contexts_[k].task_index = 0;
106    contexts_[k].task_timestamp_list.resize(
107        timestamps_in_ms[k].size());
108    for (size_t i = 0; i < timestamps_in_ms[k].size(); i++) {
109      contexts_[k].task_timestamp_list[i] =
110          base::TimeDelta::FromMilliseconds(timestamps_in_ms[k][i]);
111    }
112  }
113
114  // Expected task order (for tasks that are actually run).
115  for (size_t k = 0; k < expected_task_timestamps_ms.size(); k++) {
116    expected_task_timestamps_.push_back(
117        base::TimeDelta::FromMilliseconds(expected_task_timestamps_ms[k]));
118  }
119}
120
121void BalancedMediaTaskRunnerTest::ProcessAllTasks() {
122  base::MessageLoop::current()->PostDelayedTask(
123      FROM_HERE,
124      base::Bind(&BalancedMediaTaskRunnerTest::OnTestTimeout,
125                 base::Unretained(this)),
126      base::TimeDelta::FromSeconds(5));
127  ScheduleTask();
128}
129
130void BalancedMediaTaskRunnerTest::ScheduleTask() {
131  bool has_task = false;
132  for (size_t k = 0; k < contexts_.size(); k++) {
133    if (contexts_[k].task_index < contexts_[k].task_timestamp_list.size())
134      has_task = true;
135  }
136  if (!has_task) {
137    base::MessageLoop::current()->QuitWhenIdle();
138    return;
139  }
140
141  size_t next_pattern_index =
142      (pattern_index_ + 1) % scheduling_pattern_.size();
143
144  size_t task_runner_id = scheduling_pattern_[pattern_index_];
145  MediaTaskRunnerTestContext& context = contexts_[task_runner_id];
146
147  // Check whether all tasks have been scheduled for that task runner
148  // or if there is already one pending task.
149  if (context.task_index >= context.task_timestamp_list.size() ||
150      context.is_pending_task) {
151    pattern_index_ = next_pattern_index;
152    base::MessageLoopProxy::current()->PostTask(
153        FROM_HERE,
154        base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask,
155                   base::Unretained(this)));
156    return;
157  }
158
159  bool expected_may_run = false;
160  if (context.task_timestamp_list[context.task_index] >=
161      context.max_timestamp) {
162    expected_may_run = true;
163    context.max_timestamp = context.task_timestamp_list[context.task_index];
164  }
165
166  bool may_run = context.media_task_runner->PostMediaTask(
167      FROM_HERE,
168      base::Bind(&BalancedMediaTaskRunnerTest::Task,
169                 base::Unretained(this),
170                 task_runner_id,
171                 context.task_timestamp_list[context.task_index]),
172      context.task_timestamp_list[context.task_index]);
173  EXPECT_EQ(may_run, expected_may_run);
174
175  if (may_run)
176    context.is_pending_task = true;
177
178  context.task_index++;
179  pattern_index_ = next_pattern_index;
180  base::MessageLoopProxy::current()->PostTask(
181      FROM_HERE,
182      base::Bind(&BalancedMediaTaskRunnerTest::ScheduleTask,
183                 base::Unretained(this)));
184}
185
186void BalancedMediaTaskRunnerTest::Task(
187    size_t task_runner_id, base::TimeDelta timestamp) {
188  ASSERT_FALSE(expected_task_timestamps_.empty());
189  EXPECT_EQ(timestamp, expected_task_timestamps_.front());
190  expected_task_timestamps_.pop_front();
191
192  contexts_[task_runner_id].is_pending_task = false;
193}
194
195void BalancedMediaTaskRunnerTest::OnTestTimeout() {
196  ADD_FAILURE() << "Test timed out";
197  if (base::MessageLoop::current())
198    base::MessageLoop::current()->QuitWhenIdle();
199}
200
201TEST_F(BalancedMediaTaskRunnerTest, OneTaskRunner) {
202  scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
203
204  // Timestamps of tasks for the single task runner.
205  int timestamps0_ms[] = {0, 10, 20, 30, 40, 30, 50, 60, 20, 30, 70};
206  std::vector<std::vector<int> > timestamps_ms(1);
207  timestamps_ms[0] = std::vector<int>(
208      timestamps0_ms, timestamps0_ms + arraysize(timestamps0_ms));
209
210  // Scheduling pattern.
211  std::vector<size_t> scheduling_pattern(1);
212  scheduling_pattern[0] = 0;
213
214  // Expected results.
215  int expected_timestamps[] = {0, 10, 20, 30, 40, 50, 60, 70};
216  std::vector<int> expected_timestamps_ms(std::vector<int>(
217      expected_timestamps,
218      expected_timestamps + arraysize(expected_timestamps)));
219
220  SetupTest(base::TimeDelta::FromMilliseconds(30),
221            timestamps_ms,
222            scheduling_pattern,
223            expected_timestamps_ms);
224  ProcessAllTasks();
225  message_loop->Run();
226  EXPECT_TRUE(expected_task_timestamps_.empty());
227}
228
229TEST_F(BalancedMediaTaskRunnerTest, TwoTaskRunnerUnbalanced) {
230  scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop());
231
232  // Timestamps of tasks for the 2 task runners.
233  int timestamps0_ms[] = {0, 10, 20, 30, 40, 30, 50, 60, 20, 30, 70};
234  int timestamps1_ms[] = {5, 15, 25, 35, 45, 35, 55, 65, 25, 35, 75};
235  std::vector<std::vector<int> > timestamps_ms(2);
236  timestamps_ms[0] = std::vector<int>(
237      timestamps0_ms, timestamps0_ms + arraysize(timestamps0_ms));
238  timestamps_ms[1] = std::vector<int>(
239      timestamps1_ms, timestamps1_ms + arraysize(timestamps1_ms));
240
241  // Scheduling pattern.
242  size_t pattern[] = {1, 0, 0, 0, 0};
243  std::vector<size_t> scheduling_pattern = std::vector<size_t>(
244      pattern, pattern + arraysize(pattern));
245
246  // Expected results.
247  int expected_timestamps[] = {
248    5, 0, 10, 20, 30, 15, 40, 25, 50, 35, 60, 45, 70, 55, 65, 75 };
249  std::vector<int> expected_timestamps_ms(std::vector<int>(
250      expected_timestamps,
251      expected_timestamps + arraysize(expected_timestamps)));
252
253  SetupTest(base::TimeDelta::FromMilliseconds(30),
254            timestamps_ms,
255            scheduling_pattern,
256            expected_timestamps_ms);
257  ProcessAllTasks();
258  message_loop->Run();
259  EXPECT_TRUE(expected_task_timestamps_.empty());
260}
261
262}  // namespace media
263}  // namespace chromecast
264