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/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 
     17 namespace content {
     18 
     19 namespace {
     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.
     30 static const int kNumQuitDeferrals = 10;
     31 
     32 static 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 
     43 void 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.
     50 class 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 
     64 void 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.
     73 bool IgnoreSourceAndDetails(
     74     const WindowedNotificationObserver::
     75         ConditionTestCallbackWithoutSourceAndDetails& callback,
     76     const NotificationSource& source,
     77     const NotificationDetails& details) {
     78   return callback.Run();
     79 }
     80 
     81 }  // namespace
     82 
     83 void RunMessageLoop() {
     84   base::RunLoop run_loop;
     85   RunThisRunLoop(&run_loop);
     86 }
     87 
     88 void 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 
    105 void RunAllPendingInMessageLoop() {
    106   base::MessageLoop::current()->PostTask(
    107       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
    108   RunMessageLoop();
    109 }
    110 
    111 void 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(&current_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 
    129 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
    130   return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
    131                     kNumQuitDeferrals);
    132 }
    133 
    134 scoped_ptr<base::Value> ExecuteScriptAndGetValue(
    135     RenderViewHost* render_view_host,
    136     const std::string& script) {
    137   ScriptCallback observer;
    138 
    139   render_view_host->ExecuteJavascriptInWebFrameCallbackResult(
    140       base::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 
    148 MessageLoopRunner::MessageLoopRunner()
    149     : loop_running_(false),
    150       quit_closure_called_(false) {
    151 }
    152 
    153 MessageLoopRunner::~MessageLoopRunner() {
    154 }
    155 
    156 void 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 
    167 base::Closure MessageLoopRunner::QuitClosure() {
    168   return base::Bind(&MessageLoopRunner::Quit, this);
    169 }
    170 
    171 void 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 
    181 WindowedNotificationObserver::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 
    190 WindowedNotificationObserver::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 
    200 WindowedNotificationObserver::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 
    210 WindowedNotificationObserver::~WindowedNotificationObserver() {}
    211 
    212 void WindowedNotificationObserver::AddNotificationType(
    213     int notification_type,
    214     const NotificationSource& source) {
    215   registrar_.Add(this, notification_type, source);
    216 }
    217 
    218 void 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 
    228 void 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