test_utils.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
12dd234de53b4b4648fa47a275bfe10ecfcbc9d50Argiris Kirtzidis// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22dd234de53b4b4648fa47a275bfe10ecfcbc9d50Argiris Kirtzidis// Use of this source code is governed by a BSD-style license that can be
32dd234de53b4b4648fa47a275bfe10ecfcbc9d50Argiris Kirtzidis// found in the LICENSE file.
42dd234de53b4b4648fa47a275bfe10ecfcbc9d50Argiris Kirtzidis
52dd234de53b4b4648fa47a275bfe10ecfcbc9d50Argiris Kirtzidis#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 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    RenderFrameHost* render_frame_host, const std::string& script) {
139  ScriptCallback observer;
140
141  render_frame_host->ExecuteJavaScript(
142      base::UTF8ToUTF16(script),
143      base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
144  base::MessageLoop* loop = base::MessageLoop::current();
145  loop->Run();
146  return observer.result().Pass();
147}
148
149MessageLoopRunner::MessageLoopRunner()
150    : loop_running_(false),
151      quit_closure_called_(false) {
152}
153
154MessageLoopRunner::~MessageLoopRunner() {
155}
156
157void MessageLoopRunner::Run() {
158  // Do not run the message loop if our quit closure has already been called.
159  // This helps in scenarios where the closure has a chance to run before
160  // we Run explicitly.
161  if (quit_closure_called_)
162    return;
163
164  loop_running_ = true;
165  RunThisRunLoop(&run_loop_);
166}
167
168base::Closure MessageLoopRunner::QuitClosure() {
169  return base::Bind(&MessageLoopRunner::Quit, this);
170}
171
172void MessageLoopRunner::Quit() {
173  quit_closure_called_ = true;
174
175  // Only run the quit task if we are running the message loop.
176  if (loop_running_) {
177    GetQuitTaskForRunLoop(&run_loop_).Run();
178    loop_running_ = false;
179  }
180}
181
182WindowedNotificationObserver::WindowedNotificationObserver(
183    int notification_type,
184    const NotificationSource& source)
185    : seen_(false),
186      running_(false),
187      source_(NotificationService::AllSources()) {
188  AddNotificationType(notification_type, source);
189}
190
191WindowedNotificationObserver::WindowedNotificationObserver(
192    int notification_type,
193    const ConditionTestCallback& callback)
194    : seen_(false),
195      running_(false),
196      callback_(callback),
197      source_(NotificationService::AllSources()) {
198  AddNotificationType(notification_type, source_);
199}
200
201WindowedNotificationObserver::WindowedNotificationObserver(
202    int notification_type,
203    const ConditionTestCallbackWithoutSourceAndDetails& callback)
204    : seen_(false),
205      running_(false),
206      callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
207      source_(NotificationService::AllSources()) {
208  registrar_.Add(this, notification_type, source_);
209}
210
211WindowedNotificationObserver::~WindowedNotificationObserver() {}
212
213void WindowedNotificationObserver::AddNotificationType(
214    int notification_type,
215    const NotificationSource& source) {
216  registrar_.Add(this, notification_type, source);
217}
218
219void WindowedNotificationObserver::Wait() {
220  if (seen_)
221    return;
222
223  running_ = true;
224  message_loop_runner_ = new MessageLoopRunner;
225  message_loop_runner_->Run();
226  EXPECT_TRUE(seen_);
227}
228
229void WindowedNotificationObserver::Observe(
230    int type,
231    const NotificationSource& source,
232    const NotificationDetails& details) {
233  source_ = source;
234  details_ = details;
235  if (!callback_.is_null() && !callback_.Run(source, details))
236    return;
237
238  seen_ = true;
239  if (!running_)
240    return;
241
242  message_loop_runner_->Quit();
243  running_ = false;
244}
245
246InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
247    : child_thread_count_(0) {
248  RenderProcessHost::SetRunRendererInProcess(true);
249  BrowserChildProcessObserver::Add(this);
250}
251
252InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
253  if (child_thread_count_) {
254    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
255    DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
256    runner_ = new MessageLoopRunner;
257    runner_->Run();
258  }
259  BrowserChildProcessObserver::Remove(this);
260  RenderProcessHost::SetRunRendererInProcess(false);
261}
262
263void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
264    const ChildProcessData& data) {
265  child_thread_count_++;
266}
267
268void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
269    const ChildProcessData& data) {
270  if (--child_thread_count_)
271    return;
272
273  if (runner_.get())
274    runner_->Quit();
275}
276
277}  // namespace content
278