test_utils.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/lazy_instance.h"
9#include "base/message_loop.h"
10#include "base/run_loop.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/values.h"
13#include "content/public/browser/notification_service.h"
14#include "content/public/browser/render_view_host.h"
15#include "content/public/test/test_launcher.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace content {
19
20namespace {
21
22base::LazyInstance<std::vector<RunMessageLoopHook> >::Leaky
23    g_pre_run_message_loop_hooks = LAZY_INSTANCE_INITIALIZER;
24
25base::LazyInstance<std::vector<RunMessageLoopHook> >::Leaky
26    g_post_run_message_loop_hooks = LAZY_INSTANCE_INITIALIZER;
27
28// Number of times to repost a Quit task so that the MessageLoop finishes up
29// pending tasks and tasks posted by those pending tasks without risking the
30// potential hang behavior of MessageLoop::QuitWhenIdle.
31// The criteria for choosing this number: it should be high enough to make the
32// quit act like QuitWhenIdle, while taking into account that any page which is
33// animating may be rendering another frame for each quit deferral. For an
34// animating page, the potential delay to quitting the RunLoop would be
35// kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
36// 200ms/frame.
37static const int kNumQuitDeferrals = 10;
38
39static void DeferredQuitRunLoop(const base::Closure& quit_task,
40                                int num_quit_deferrals) {
41  if (num_quit_deferrals <= 0) {
42    quit_task.Run();
43  } else {
44    base::MessageLoop::current()->PostTask(
45        FROM_HERE,
46        base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1));
47  }
48}
49
50void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,
51                                     const base::Closure& quit_task) {
52  RunAllPendingInMessageLoop();
53  BrowserThread::PostTask(thread_id, FROM_HERE, quit_task);
54}
55
56// Class used handle result callbacks for ExecuteScriptAndGetValue.
57class ScriptCallback {
58 public:
59  ScriptCallback() { }
60  virtual ~ScriptCallback() { }
61  void ResultCallback(const base::Value* result);
62
63  scoped_ptr<base::Value> result() { return result_.Pass(); }
64
65 private:
66  scoped_ptr<base::Value> result_;
67
68  DISALLOW_COPY_AND_ASSIGN(ScriptCallback);
69};
70
71void ScriptCallback::ResultCallback(const base::Value* result) {
72  if (result)
73    result_.reset(result->DeepCopy());
74  base::MessageLoop::current()->Quit();
75}
76
77}  // namespace
78
79void RunMessageLoop() {
80  base::RunLoop run_loop;
81  RunThisRunLoop(&run_loop);
82}
83
84void RunThisRunLoop(base::RunLoop* run_loop) {
85  base::MessageLoop::ScopedNestableTaskAllower allow(
86      base::MessageLoop::current());
87
88  for (size_t i = 0; i < g_pre_run_message_loop_hooks.Get().size(); i++)
89    g_pre_run_message_loop_hooks.Get()[i].Run(run_loop);
90
91  run_loop->Run();
92
93  for (size_t i = 0; i < g_pre_run_message_loop_hooks.Get().size(); i++)
94    g_post_run_message_loop_hooks.Get()[i].Run(run_loop);
95}
96
97void AddPreRunMessageLoopHook(const RunMessageLoopHook& hook) {
98  g_pre_run_message_loop_hooks.Get().push_back(hook);
99}
100
101void AddPostRunMessageLoopHook(const RunMessageLoopHook& hook) {
102  g_post_run_message_loop_hooks.Get().push_back(hook);
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      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  registrar_.Add(this, notification_type, source);
188}
189
190WindowedNotificationObserver::~WindowedNotificationObserver() {}
191
192void WindowedNotificationObserver::Wait() {
193  if (seen_)
194    return;
195
196  running_ = true;
197  message_loop_runner_ = new MessageLoopRunner;
198  message_loop_runner_->Run();
199  EXPECT_TRUE(seen_);
200}
201
202void WindowedNotificationObserver::Observe(
203    int type,
204    const NotificationSource& source,
205    const NotificationDetails& details) {
206  source_ = source;
207  details_ = details;
208  seen_ = true;
209  if (!running_)
210    return;
211
212  message_loop_runner_->Quit();
213  running_ = false;
214}
215
216}  // namespace content
217