Home | History | Annotate | Download | only in test
      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_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 
     20 namespace content {
     21 
     22 namespace {
     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.
     33 static const int kNumQuitDeferrals = 10;
     34 
     35 static 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 
     46 void 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.
     53 class 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 
     67 void 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.
     76 bool IgnoreSourceAndDetails(
     77     const WindowedNotificationObserver::
     78         ConditionTestCallbackWithoutSourceAndDetails& callback,
     79     const NotificationSource& source,
     80     const NotificationDetails& details) {
     81   return callback.Run();
     82 }
     83 
     84 }  // namespace
     85 
     86 void RunMessageLoop() {
     87   base::RunLoop run_loop;
     88   RunThisRunLoop(&run_loop);
     89 }
     90 
     91 void 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 
    108 void RunAllPendingInMessageLoop() {
    109   base::MessageLoop::current()->PostTask(
    110       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
    111   RunMessageLoop();
    112 }
    113 
    114 void 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 
    132 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
    133   return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
    134                     kNumQuitDeferrals);
    135 }
    136 
    137 scoped_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 
    149 MessageLoopRunner::MessageLoopRunner()
    150     : loop_running_(false),
    151       quit_closure_called_(false) {
    152 }
    153 
    154 MessageLoopRunner::~MessageLoopRunner() {
    155 }
    156 
    157 void 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 
    168 base::Closure MessageLoopRunner::QuitClosure() {
    169   return base::Bind(&MessageLoopRunner::Quit, this);
    170 }
    171 
    172 void 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 
    182 WindowedNotificationObserver::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 
    191 WindowedNotificationObserver::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 
    201 WindowedNotificationObserver::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 
    211 WindowedNotificationObserver::~WindowedNotificationObserver() {}
    212 
    213 void WindowedNotificationObserver::AddNotificationType(
    214     int notification_type,
    215     const NotificationSource& source) {
    216   registrar_.Add(this, notification_type, source);
    217 }
    218 
    219 void 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 
    229 void 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 
    246 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
    247     : child_thread_count_(0) {
    248   RenderProcessHost::SetRunRendererInProcess(true);
    249   BrowserChildProcessObserver::Add(this);
    250 }
    251 
    252 InProcessUtilityThreadHelper::~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 
    263 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
    264     const ChildProcessData& data) {
    265   child_thread_count_++;
    266 }
    267 
    268 void 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