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