test_utils.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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}  // namespace
71
72void RunMessageLoop() {
73  base::RunLoop run_loop;
74  RunThisRunLoop(&run_loop);
75}
76
77void RunThisRunLoop(base::RunLoop* run_loop) {
78  base::MessageLoop::ScopedNestableTaskAllower allow(
79      base::MessageLoop::current());
80
81  // If we're running inside a browser test, we might need to allow the test
82  // launcher to do extra work before/after running a nested message loop.
83  TestLauncherDelegate* delegate = NULL;
84#if !defined(OS_IOS)
85  delegate = GetCurrentTestLauncherDelegate();
86#endif
87  if (delegate)
88    delegate->PreRunMessageLoop(run_loop);
89  run_loop->Run();
90  if (delegate)
91    delegate->PostRunMessageLoop();
92}
93
94void RunAllPendingInMessageLoop() {
95  base::MessageLoop::current()->PostTask(
96      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
97  RunMessageLoop();
98}
99
100void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
101  if (BrowserThread::CurrentlyOn(thread_id)) {
102    RunAllPendingInMessageLoop();
103    return;
104  }
105  BrowserThread::ID current_thread_id;
106  if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
107    NOTREACHED();
108    return;
109  }
110
111  base::RunLoop run_loop;
112  BrowserThread::PostTask(thread_id, FROM_HERE,
113      base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
114                 run_loop.QuitClosure()));
115  RunThisRunLoop(&run_loop);
116}
117
118base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
119  return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
120                    kNumQuitDeferrals);
121}
122
123scoped_ptr<base::Value> ExecuteScriptAndGetValue(
124    RenderViewHost* render_view_host,
125    const std::string& script) {
126  ScriptCallback observer;
127
128  render_view_host->ExecuteJavascriptInWebFrameCallbackResult(
129      string16(),  // frame_xpath,
130      UTF8ToUTF16(script),
131      base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
132  base::MessageLoop* loop = base::MessageLoop::current();
133  loop->Run();
134  return observer.result().Pass();
135}
136
137MessageLoopRunner::MessageLoopRunner()
138    : loop_running_(false),
139      quit_closure_called_(false) {
140}
141
142MessageLoopRunner::~MessageLoopRunner() {
143}
144
145void MessageLoopRunner::Run() {
146  // Do not run the message loop if our quit closure has already been called.
147  // This helps in scenarios where the closure has a chance to run before
148  // we Run explicitly.
149  if (quit_closure_called_)
150    return;
151
152  loop_running_ = true;
153  RunThisRunLoop(&run_loop_);
154}
155
156base::Closure MessageLoopRunner::QuitClosure() {
157  return base::Bind(&MessageLoopRunner::Quit, this);
158}
159
160void MessageLoopRunner::Quit() {
161  quit_closure_called_ = true;
162
163  // Only run the quit task if we are running the message loop.
164  if (loop_running_) {
165    GetQuitTaskForRunLoop(&run_loop_).Run();
166    loop_running_ = false;
167  }
168}
169
170WindowedNotificationObserver::WindowedNotificationObserver(
171    int notification_type,
172    const NotificationSource& source)
173    : seen_(false),
174      running_(false),
175      source_(NotificationService::AllSources()) {
176  registrar_.Add(this, notification_type, source);
177}
178
179WindowedNotificationObserver::WindowedNotificationObserver(
180    int notification_type,
181    const ConditionTestCallback& callback)
182    : seen_(false),
183      running_(false),
184      callback_(callback),
185      source_(NotificationService::AllSources()) {
186  registrar_.Add(this, notification_type, source_);
187}
188
189WindowedNotificationObserver::~WindowedNotificationObserver() {}
190
191void WindowedNotificationObserver::Wait() {
192  if (seen_)
193    return;
194
195  running_ = true;
196  message_loop_runner_ = new MessageLoopRunner;
197  message_loop_runner_->Run();
198  EXPECT_TRUE(seen_);
199}
200
201void WindowedNotificationObserver::Observe(
202    int type,
203    const NotificationSource& source,
204    const NotificationDetails& details) {
205  source_ = source;
206  details_ = details;
207  if (!callback_.is_null() && !callback_.Run())
208    return;
209
210  seen_ = true;
211  if (!running_)
212    return;
213
214  message_loop_runner_->Quit();
215  running_ = false;
216}
217
218}  // namespace content
219