test_utils.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 "content/public/test/test_utils.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/values.h"
12#include "content/public/browser/browser_child_process_host_iterator.h"
13#include "content/public/browser/notification_service.h"
14#include "content/public/browser/render_frame_host.h"
15#include "content/public/browser/render_process_host.h"
16#include "content/public/common/process_type.h"
17#include "content/public/test/test_launcher.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace content {
21
22namespace {
23
24// Number of times to repost a Quit task so that the MessageLoop finishes up
25// pending tasks and tasks posted by those pending tasks without risking the
26// potential hang behavior of MessageLoop::QuitWhenIdle.
27// The criteria for choosing this number: it should be high enough to make the
28// quit act like QuitWhenIdle, while taking into account that any page which is
29// animating may be rendering another frame for each quit deferral. For an
30// animating page, the potential delay to quitting the RunLoop would be
31// kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
32// 200ms/frame.
33static const int kNumQuitDeferrals = 10;
34
35static void DeferredQuitRunLoop(const base::Closure& quit_task,
36                                int num_quit_deferrals) {
37  if (num_quit_deferrals <= 0) {
38    quit_task.Run();
39  } else {
40    base::MessageLoop::current()->PostTask(
41        FROM_HERE,
42        base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1));
43  }
44}
45
46void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,
47                                     const base::Closure& quit_task) {
48  RunAllPendingInMessageLoop();
49  BrowserThread::PostTask(thread_id, FROM_HERE, quit_task);
50}
51
52// Class used to handle result callbacks for ExecuteScriptAndGetValue.
53class ScriptCallback {
54 public:
55  ScriptCallback() { }
56  virtual ~ScriptCallback() { }
57  void ResultCallback(const base::Value* result);
58
59  scoped_ptr<base::Value> result() { return result_.Pass(); }
60
61 private:
62  scoped_ptr<base::Value> result_;
63
64  DISALLOW_COPY_AND_ASSIGN(ScriptCallback);
65};
66
67void ScriptCallback::ResultCallback(const base::Value* result) {
68  if (result)
69    result_.reset(result->DeepCopy());
70  base::MessageLoop::current()->Quit();
71}
72
73// Monitors if any task is processed by the message loop.
74class TaskObserver : public base::MessageLoop::TaskObserver {
75 public:
76  TaskObserver() : processed_(false) {}
77  virtual ~TaskObserver() {}
78
79  // MessageLoop::TaskObserver overrides.
80  virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
81  }
82  virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
83    processed_ = true;
84  }
85
86  // Returns true if any task was processed.
87  bool processed() const { return processed_; }
88
89 private:
90  bool processed_;
91  DISALLOW_COPY_AND_ASSIGN(TaskObserver);
92};
93
94// Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
95// a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
96// by ignoring the notification source and details.
97bool IgnoreSourceAndDetails(
98    const WindowedNotificationObserver::
99        ConditionTestCallbackWithoutSourceAndDetails& callback,
100    const NotificationSource& source,
101    const NotificationDetails& details) {
102  return callback.Run();
103}
104
105}  // namespace
106
107void RunMessageLoop() {
108  base::RunLoop run_loop;
109  RunThisRunLoop(&run_loop);
110}
111
112void RunThisRunLoop(base::RunLoop* run_loop) {
113  base::MessageLoop::ScopedNestableTaskAllower allow(
114      base::MessageLoop::current());
115
116  // If we're running inside a browser test, we might need to allow the test
117  // launcher to do extra work before/after running a nested message loop.
118  TestLauncherDelegate* delegate = NULL;
119#if !defined(OS_IOS)
120  delegate = GetCurrentTestLauncherDelegate();
121#endif
122  if (delegate)
123    delegate->PreRunMessageLoop(run_loop);
124  run_loop->Run();
125  if (delegate)
126    delegate->PostRunMessageLoop();
127}
128
129void RunAllPendingInMessageLoop() {
130  base::MessageLoop::current()->PostTask(
131      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
132  RunMessageLoop();
133}
134
135void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
136  if (BrowserThread::CurrentlyOn(thread_id)) {
137    RunAllPendingInMessageLoop();
138    return;
139  }
140  BrowserThread::ID current_thread_id;
141  if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
142    NOTREACHED();
143    return;
144  }
145
146  base::RunLoop run_loop;
147  BrowserThread::PostTask(thread_id, FROM_HERE,
148      base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
149                 run_loop.QuitClosure()));
150  RunThisRunLoop(&run_loop);
151}
152
153void RunAllBlockingPoolTasksUntilIdle() {
154  while (true) {
155    content::BrowserThread::GetBlockingPool()->FlushForTesting();
156
157    TaskObserver task_observer;
158    base::MessageLoop::current()->AddTaskObserver(&task_observer);
159    base::RunLoop().RunUntilIdle();
160    base::MessageLoop::current()->RemoveTaskObserver(&task_observer);
161
162    if (!task_observer.processed())
163      break;
164  }
165}
166
167base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
168  return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
169                    kNumQuitDeferrals);
170}
171
172scoped_ptr<base::Value> ExecuteScriptAndGetValue(
173    RenderFrameHost* render_frame_host, const std::string& script) {
174  ScriptCallback observer;
175
176  render_frame_host->ExecuteJavaScript(
177      base::UTF8ToUTF16(script),
178      base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
179  base::MessageLoop* loop = base::MessageLoop::current();
180  loop->Run();
181  return observer.result().Pass();
182}
183
184MessageLoopRunner::MessageLoopRunner()
185    : loop_running_(false),
186      quit_closure_called_(false) {
187}
188
189MessageLoopRunner::~MessageLoopRunner() {
190}
191
192void MessageLoopRunner::Run() {
193  // Do not run the message loop if our quit closure has already been called.
194  // This helps in scenarios where the closure has a chance to run before
195  // we Run explicitly.
196  if (quit_closure_called_)
197    return;
198
199  loop_running_ = true;
200  RunThisRunLoop(&run_loop_);
201}
202
203base::Closure MessageLoopRunner::QuitClosure() {
204  return base::Bind(&MessageLoopRunner::Quit, this);
205}
206
207void MessageLoopRunner::Quit() {
208  quit_closure_called_ = true;
209
210  // Only run the quit task if we are running the message loop.
211  if (loop_running_) {
212    GetQuitTaskForRunLoop(&run_loop_).Run();
213    loop_running_ = false;
214  }
215}
216
217WindowedNotificationObserver::WindowedNotificationObserver(
218    int notification_type,
219    const NotificationSource& source)
220    : seen_(false),
221      running_(false),
222      source_(NotificationService::AllSources()) {
223  AddNotificationType(notification_type, source);
224}
225
226WindowedNotificationObserver::WindowedNotificationObserver(
227    int notification_type,
228    const ConditionTestCallback& callback)
229    : seen_(false),
230      running_(false),
231      callback_(callback),
232      source_(NotificationService::AllSources()) {
233  AddNotificationType(notification_type, source_);
234}
235
236WindowedNotificationObserver::WindowedNotificationObserver(
237    int notification_type,
238    const ConditionTestCallbackWithoutSourceAndDetails& callback)
239    : seen_(false),
240      running_(false),
241      callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
242      source_(NotificationService::AllSources()) {
243  registrar_.Add(this, notification_type, source_);
244}
245
246WindowedNotificationObserver::~WindowedNotificationObserver() {}
247
248void WindowedNotificationObserver::AddNotificationType(
249    int notification_type,
250    const NotificationSource& source) {
251  registrar_.Add(this, notification_type, source);
252}
253
254void WindowedNotificationObserver::Wait() {
255  if (seen_)
256    return;
257
258  running_ = true;
259  message_loop_runner_ = new MessageLoopRunner;
260  message_loop_runner_->Run();
261  EXPECT_TRUE(seen_);
262}
263
264void WindowedNotificationObserver::Observe(
265    int type,
266    const NotificationSource& source,
267    const NotificationDetails& details) {
268  source_ = source;
269  details_ = details;
270  if (!callback_.is_null() && !callback_.Run(source, details))
271    return;
272
273  seen_ = true;
274  if (!running_)
275    return;
276
277  message_loop_runner_->Quit();
278  running_ = false;
279}
280
281InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
282    : child_thread_count_(0) {
283  RenderProcessHost::SetRunRendererInProcess(true);
284  BrowserChildProcessObserver::Add(this);
285}
286
287InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
288  if (child_thread_count_) {
289    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
290    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
291    runner_ = new MessageLoopRunner;
292    runner_->Run();
293  }
294  BrowserChildProcessObserver::Remove(this);
295  RenderProcessHost::SetRunRendererInProcess(false);
296}
297
298void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
299    const ChildProcessData& data) {
300  child_thread_count_++;
301}
302
303void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
304    const ChildProcessData& data) {
305  if (--child_thread_count_)
306    return;
307
308  if (runner_.get())
309    runner_->Quit();
310}
311
312}  // namespace content
313