test_utils.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/notification_service.h"
13#include "content/public/browser/render_view_host.h"
14#include "content/public/test/test_launcher.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace content {
18
19namespace {
20
21// Number of times to repost a Quit task so that the MessageLoop finishes up
22// pending tasks and tasks posted by those pending tasks without risking the
23// potential hang behavior of MessageLoop::QuitWhenIdle.
24// The criteria for choosing this number: it should be high enough to make the
25// quit act like QuitWhenIdle, while taking into account that any page which is
26// animating may be rendering another frame for each quit deferral. For an
27// animating page, the potential delay to quitting the RunLoop would be
28// kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
29// 200ms/frame.
30static const int kNumQuitDeferrals = 10;
31
32static void DeferredQuitRunLoop(const base::Closure& quit_task,
33                                int num_quit_deferrals) {
34  if (num_quit_deferrals <= 0) {
35    quit_task.Run();
36  } else {
37    base::MessageLoop::current()->PostTask(
38        FROM_HERE,
39        base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1));
40  }
41}
42
43void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,
44                                     const base::Closure& quit_task) {
45  RunAllPendingInMessageLoop();
46  BrowserThread::PostTask(thread_id, FROM_HERE, quit_task);
47}
48
49// Class used handle result callbacks for ExecuteScriptAndGetValue.
50class ScriptCallback {
51 public:
52  ScriptCallback() { }
53  virtual ~ScriptCallback() { }
54  void ResultCallback(const base::Value* result);
55
56  scoped_ptr<base::Value> result() { return result_.Pass(); }
57
58 private:
59  scoped_ptr<base::Value> result_;
60
61  DISALLOW_COPY_AND_ASSIGN(ScriptCallback);
62};
63
64void ScriptCallback::ResultCallback(const base::Value* result) {
65  if (result)
66    result_.reset(result->DeepCopy());
67  base::MessageLoop::current()->Quit();
68}
69
70// Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
71// a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
72// by ignoring the notification source and details.
73bool IgnoreSourceAndDetails(
74    const WindowedNotificationObserver::
75        ConditionTestCallbackWithoutSourceAndDetails& callback,
76    const NotificationSource& source,
77    const NotificationDetails& details) {
78  return callback.Run();
79}
80
81}  // namespace
82
83void RunMessageLoop() {
84  base::RunLoop run_loop;
85  RunThisRunLoop(&run_loop);
86}
87
88void RunThisRunLoop(base::RunLoop* run_loop) {
89  base::MessageLoop::ScopedNestableTaskAllower allow(
90      base::MessageLoop::current());
91
92  // If we're running inside a browser test, we might need to allow the test
93  // launcher to do extra work before/after running a nested message loop.
94  TestLauncherDelegate* delegate = NULL;
95#if !defined(OS_IOS)
96  delegate = GetCurrentTestLauncherDelegate();
97#endif
98  if (delegate)
99    delegate->PreRunMessageLoop(run_loop);
100  run_loop->Run();
101  if (delegate)
102    delegate->PostRunMessageLoop();
103}
104
105void RunAllPendingInMessageLoop() {
106  base::MessageLoop::current()->PostTask(
107      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
108  RunMessageLoop();
109}
110
111void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
112  if (BrowserThread::CurrentlyOn(thread_id)) {
113    RunAllPendingInMessageLoop();
114    return;
115  }
116  BrowserThread::ID current_thread_id;
117  if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
118    NOTREACHED();
119    return;
120  }
121
122  base::RunLoop run_loop;
123  BrowserThread::PostTask(thread_id, FROM_HERE,
124      base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
125                 run_loop.QuitClosure()));
126  RunThisRunLoop(&run_loop);
127}
128
129base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
130  return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
131                    kNumQuitDeferrals);
132}
133
134scoped_ptr<base::Value> ExecuteScriptAndGetValue(
135    RenderViewHost* render_view_host,
136    const std::string& script) {
137  ScriptCallback observer;
138
139  render_view_host->ExecuteJavascriptInWebFrameCallbackResult(
140      base::string16(),  // frame_xpath,
141      UTF8ToUTF16(script),
142      base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
143  base::MessageLoop* loop = base::MessageLoop::current();
144  loop->Run();
145  return observer.result().Pass();
146}
147
148MessageLoopRunner::MessageLoopRunner()
149    : loop_running_(false),
150      quit_closure_called_(false) {
151}
152
153MessageLoopRunner::~MessageLoopRunner() {
154}
155
156void MessageLoopRunner::Run() {
157  // Do not run the message loop if our quit closure has already been called.
158  // This helps in scenarios where the closure has a chance to run before
159  // we Run explicitly.
160  if (quit_closure_called_)
161    return;
162
163  loop_running_ = true;
164  RunThisRunLoop(&run_loop_);
165}
166
167base::Closure MessageLoopRunner::QuitClosure() {
168  return base::Bind(&MessageLoopRunner::Quit, this);
169}
170
171void MessageLoopRunner::Quit() {
172  quit_closure_called_ = true;
173
174  // Only run the quit task if we are running the message loop.
175  if (loop_running_) {
176    GetQuitTaskForRunLoop(&run_loop_).Run();
177    loop_running_ = false;
178  }
179}
180
181WindowedNotificationObserver::WindowedNotificationObserver(
182    int notification_type,
183    const NotificationSource& source)
184    : seen_(false),
185      running_(false),
186      source_(NotificationService::AllSources()) {
187  AddNotificationType(notification_type, source);
188}
189
190WindowedNotificationObserver::WindowedNotificationObserver(
191    int notification_type,
192    const ConditionTestCallback& callback)
193    : seen_(false),
194      running_(false),
195      callback_(callback),
196      source_(NotificationService::AllSources()) {
197  AddNotificationType(notification_type, source_);
198}
199
200WindowedNotificationObserver::WindowedNotificationObserver(
201    int notification_type,
202    const ConditionTestCallbackWithoutSourceAndDetails& callback)
203    : seen_(false),
204      running_(false),
205      callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
206      source_(NotificationService::AllSources()) {
207  registrar_.Add(this, notification_type, source_);
208}
209
210WindowedNotificationObserver::~WindowedNotificationObserver() {}
211
212void WindowedNotificationObserver::AddNotificationType(
213    int notification_type,
214    const NotificationSource& source) {
215  registrar_.Add(this, notification_type, source);
216}
217
218void WindowedNotificationObserver::Wait() {
219  if (seen_)
220    return;
221
222  running_ = true;
223  message_loop_runner_ = new MessageLoopRunner;
224  message_loop_runner_->Run();
225  EXPECT_TRUE(seen_);
226}
227
228void WindowedNotificationObserver::Observe(
229    int type,
230    const NotificationSource& source,
231    const NotificationDetails& details) {
232  source_ = source;
233  details_ = details;
234  if (!callback_.is_null() && !callback_.Run(source, details))
235    return;
236
237  seen_ = true;
238  if (!running_)
239    return;
240
241  message_loop_runner_->Quit();
242  running_ = false;
243}
244
245}  // namespace content
246