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(¤t_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