test_utils.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_process_host.h"
15#include "content/public/browser/render_view_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 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// Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
74// a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
75// by ignoring the notification source and details.
76bool IgnoreSourceAndDetails(
77    const WindowedNotificationObserver::
78        ConditionTestCallbackWithoutSourceAndDetails& callback,
79    const NotificationSource& source,
80    const NotificationDetails& details) {
81  return callback.Run();
82}
83
84}  // namespace
85
86void RunMessageLoop() {
87  base::RunLoop run_loop;
88  RunThisRunLoop(&run_loop);
89}
90
91void RunThisRunLoop(base::RunLoop* run_loop) {
92  base::MessageLoop::ScopedNestableTaskAllower allow(
93      base::MessageLoop::current());
94
95  // If we're running inside a browser test, we might need to allow the test
96  // launcher to do extra work before/after running a nested message loop.
97  TestLauncherDelegate* delegate = NULL;
98#if !defined(OS_IOS)
99  delegate = GetCurrentTestLauncherDelegate();
100#endif
101  if (delegate)
102    delegate->PreRunMessageLoop(run_loop);
103  run_loop->Run();
104  if (delegate)
105    delegate->PostRunMessageLoop();
106}
107
108void RunAllPendingInMessageLoop() {
109  base::MessageLoop::current()->PostTask(
110      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
111  RunMessageLoop();
112}
113
114void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
115  if (BrowserThread::CurrentlyOn(thread_id)) {
116    RunAllPendingInMessageLoop();
117    return;
118  }
119  BrowserThread::ID current_thread_id;
120  if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
121    NOTREACHED();
122    return;
123  }
124
125  base::RunLoop run_loop;
126  BrowserThread::PostTask(thread_id, FROM_HERE,
127      base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
128                 run_loop.QuitClosure()));
129  RunThisRunLoop(&run_loop);
130}
131
132base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
133  return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
134                    kNumQuitDeferrals);
135}
136
137scoped_ptr<base::Value> ExecuteScriptAndGetValue(
138    RenderViewHost* render_view_host,
139    const std::string& script) {
140  ScriptCallback observer;
141
142  render_view_host->ExecuteJavascriptInWebFrameCallbackResult(
143      base::string16(),  // frame_xpath,
144      base::UTF8ToUTF16(script),
145      base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
146  base::MessageLoop* loop = base::MessageLoop::current();
147  loop->Run();
148  return observer.result().Pass();
149}
150
151MessageLoopRunner::MessageLoopRunner()
152    : loop_running_(false),
153      quit_closure_called_(false) {
154}
155
156MessageLoopRunner::~MessageLoopRunner() {
157}
158
159void MessageLoopRunner::Run() {
160  // Do not run the message loop if our quit closure has already been called.
161  // This helps in scenarios where the closure has a chance to run before
162  // we Run explicitly.
163  if (quit_closure_called_)
164    return;
165
166  loop_running_ = true;
167  RunThisRunLoop(&run_loop_);
168}
169
170base::Closure MessageLoopRunner::QuitClosure() {
171  return base::Bind(&MessageLoopRunner::Quit, this);
172}
173
174void MessageLoopRunner::Quit() {
175  quit_closure_called_ = true;
176
177  // Only run the quit task if we are running the message loop.
178  if (loop_running_) {
179    GetQuitTaskForRunLoop(&run_loop_).Run();
180    loop_running_ = false;
181  }
182}
183
184WindowedNotificationObserver::WindowedNotificationObserver(
185    int notification_type,
186    const NotificationSource& source)
187    : seen_(false),
188      running_(false),
189      source_(NotificationService::AllSources()) {
190  AddNotificationType(notification_type, source);
191}
192
193WindowedNotificationObserver::WindowedNotificationObserver(
194    int notification_type,
195    const ConditionTestCallback& callback)
196    : seen_(false),
197      running_(false),
198      callback_(callback),
199      source_(NotificationService::AllSources()) {
200  AddNotificationType(notification_type, source_);
201}
202
203WindowedNotificationObserver::WindowedNotificationObserver(
204    int notification_type,
205    const ConditionTestCallbackWithoutSourceAndDetails& callback)
206    : seen_(false),
207      running_(false),
208      callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
209      source_(NotificationService::AllSources()) {
210  registrar_.Add(this, notification_type, source_);
211}
212
213WindowedNotificationObserver::~WindowedNotificationObserver() {}
214
215void WindowedNotificationObserver::AddNotificationType(
216    int notification_type,
217    const NotificationSource& source) {
218  registrar_.Add(this, notification_type, source);
219}
220
221void WindowedNotificationObserver::Wait() {
222  if (seen_)
223    return;
224
225  running_ = true;
226  message_loop_runner_ = new MessageLoopRunner;
227  message_loop_runner_->Run();
228  EXPECT_TRUE(seen_);
229}
230
231void WindowedNotificationObserver::Observe(
232    int type,
233    const NotificationSource& source,
234    const NotificationDetails& details) {
235  source_ = source;
236  details_ = details;
237  if (!callback_.is_null() && !callback_.Run(source, details))
238    return;
239
240  seen_ = true;
241  if (!running_)
242    return;
243
244  message_loop_runner_->Quit();
245  running_ = false;
246}
247
248InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
249    : child_thread_count_(0) {
250  RenderProcessHost::SetRunRendererInProcess(true);
251  BrowserChildProcessObserver::Add(this);
252}
253
254InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
255  if (child_thread_count_) {
256    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
257    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
258    runner_ = new MessageLoopRunner;
259    runner_->Run();
260  }
261  BrowserChildProcessObserver::Remove(this);
262  RenderProcessHost::SetRunRendererInProcess(false);
263}
264
265void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
266    const ChildProcessData& data) {
267  child_thread_count_++;
268}
269
270void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
271    const ChildProcessData& data) {
272  if (--child_thread_count_)
273    return;
274
275  if (runner_.get())
276    runner_->Quit();
277}
278
279}  // namespace content
280