test_utils.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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// Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from 71// a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails 72// by ignoring the notification source and details. 73bool IgnoreSourceAndDetails( 74 const WindowedNotificationObserver:: 75 ConditionTestCallbackWithoutSourceAndDetails& callback, 76 const NotificationSource& source, 77 const NotificationDetails& details) { 78 return callback.Run(); 79} 80 81} // namespace 82 83void RunMessageLoop() { 84 base::RunLoop run_loop; 85 RunThisRunLoop(&run_loop); 86} 87 88void RunThisRunLoop(base::RunLoop* run_loop) { 89 base::MessageLoop::ScopedNestableTaskAllower allow( 90 base::MessageLoop::current()); 91 92 // If we're running inside a browser test, we might need to allow the test 93 // launcher to do extra work before/after running a nested message loop. 94 TestLauncherDelegate* delegate = NULL; 95#if !defined(OS_IOS) 96 delegate = GetCurrentTestLauncherDelegate(); 97#endif 98 if (delegate) 99 delegate->PreRunMessageLoop(run_loop); 100 run_loop->Run(); 101 if (delegate) 102 delegate->PostRunMessageLoop(); 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(¤t_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 AddNotificationType(notification_type, source); 188} 189 190WindowedNotificationObserver::WindowedNotificationObserver( 191 int notification_type, 192 const ConditionTestCallback& callback) 193 : seen_(false), 194 running_(false), 195 callback_(callback), 196 source_(NotificationService::AllSources()) { 197 AddNotificationType(notification_type, source_); 198} 199 200WindowedNotificationObserver::WindowedNotificationObserver( 201 int notification_type, 202 const ConditionTestCallbackWithoutSourceAndDetails& callback) 203 : seen_(false), 204 running_(false), 205 callback_(base::Bind(&IgnoreSourceAndDetails, callback)), 206 source_(NotificationService::AllSources()) { 207 registrar_.Add(this, notification_type, source_); 208} 209 210WindowedNotificationObserver::~WindowedNotificationObserver() {} 211 212void WindowedNotificationObserver::AddNotificationType( 213 int notification_type, 214 const NotificationSource& source) { 215 registrar_.Add(this, notification_type, source); 216} 217 218void WindowedNotificationObserver::Wait() { 219 if (seen_) 220 return; 221 222 running_ = true; 223 message_loop_runner_ = new MessageLoopRunner; 224 message_loop_runner_->Run(); 225 EXPECT_TRUE(seen_); 226} 227 228void WindowedNotificationObserver::Observe( 229 int type, 230 const NotificationSource& source, 231 const NotificationDetails& details) { 232 source_ = source; 233 details_ = details; 234 if (!callback_.is_null() && !callback_.Run(source, details)) 235 return; 236 237 seen_ = true; 238 if (!running_) 239 return; 240 241 message_loop_runner_->Quit(); 242 running_ = false; 243} 244 245} // namespace content 246