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