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 to 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 // Monitors if any task is processed by the message loop.
     74 class TaskObserver : public base::MessageLoop::TaskObserver {
     75  public:
     76   TaskObserver() : processed_(false) {}
     77   virtual ~TaskObserver() {}
     78 
     79   // MessageLoop::TaskObserver overrides.
     80   virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
     81   }
     82   virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
     83     processed_ = true;
     84   }
     85 
     86   // Returns true if any task was processed.
     87   bool processed() const { return processed_; }
     88 
     89  private:
     90   bool processed_;
     91   DISALLOW_COPY_AND_ASSIGN(TaskObserver);
     92 };
     93 
     94 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
     95 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
     96 // by ignoring the notification source and details.
     97 bool IgnoreSourceAndDetails(
     98     const WindowedNotificationObserver::
     99         ConditionTestCallbackWithoutSourceAndDetails& callback,
    100     const NotificationSource& source,
    101     const NotificationDetails& details) {
    102   return callback.Run();
    103 }
    104 
    105 }  // namespace
    106 
    107 void RunMessageLoop() {
    108   base::RunLoop run_loop;
    109   RunThisRunLoop(&run_loop);
    110 }
    111 
    112 void RunThisRunLoop(base::RunLoop* run_loop) {
    113   base::MessageLoop::ScopedNestableTaskAllower allow(
    114       base::MessageLoop::current());
    115 
    116   // If we're running inside a browser test, we might need to allow the test
    117   // launcher to do extra work before/after running a nested message loop.
    118   TestLauncherDelegate* delegate = NULL;
    119 #if !defined(OS_IOS)
    120   delegate = GetCurrentTestLauncherDelegate();
    121 #endif
    122   if (delegate)
    123     delegate->PreRunMessageLoop(run_loop);
    124   run_loop->Run();
    125   if (delegate)
    126     delegate->PostRunMessageLoop();
    127 }
    128 
    129 void RunAllPendingInMessageLoop() {
    130   base::MessageLoop::current()->PostTask(
    131       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
    132   RunMessageLoop();
    133 }
    134 
    135 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
    136   if (BrowserThread::CurrentlyOn(thread_id)) {
    137     RunAllPendingInMessageLoop();
    138     return;
    139   }
    140   BrowserThread::ID current_thread_id;
    141   if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
    142     NOTREACHED();
    143     return;
    144   }
    145 
    146   base::RunLoop run_loop;
    147   BrowserThread::PostTask(thread_id, FROM_HERE,
    148       base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
    149                  run_loop.QuitClosure()));
    150   RunThisRunLoop(&run_loop);
    151 }
    152 
    153 void RunAllBlockingPoolTasksUntilIdle() {
    154   while (true) {
    155     content::BrowserThread::GetBlockingPool()->FlushForTesting();
    156 
    157     TaskObserver task_observer;
    158     base::MessageLoop::current()->AddTaskObserver(&task_observer);
    159     base::RunLoop().RunUntilIdle();
    160     base::MessageLoop::current()->RemoveTaskObserver(&task_observer);
    161 
    162     if (!task_observer.processed())
    163       break;
    164   }
    165 }
    166 
    167 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
    168   return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
    169                     kNumQuitDeferrals);
    170 }
    171 
    172 scoped_ptr<base::Value> ExecuteScriptAndGetValue(
    173     RenderFrameHost* render_frame_host, const std::string& script) {
    174   ScriptCallback observer;
    175 
    176   render_frame_host->ExecuteJavaScript(
    177       base::UTF8ToUTF16(script),
    178       base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
    179   base::MessageLoop* loop = base::MessageLoop::current();
    180   loop->Run();
    181   return observer.result().Pass();
    182 }
    183 
    184 MessageLoopRunner::MessageLoopRunner()
    185     : loop_running_(false),
    186       quit_closure_called_(false) {
    187 }
    188 
    189 MessageLoopRunner::~MessageLoopRunner() {
    190 }
    191 
    192 void MessageLoopRunner::Run() {
    193   // Do not run the message loop if our quit closure has already been called.
    194   // This helps in scenarios where the closure has a chance to run before
    195   // we Run explicitly.
    196   if (quit_closure_called_)
    197     return;
    198 
    199   loop_running_ = true;
    200   RunThisRunLoop(&run_loop_);
    201 }
    202 
    203 base::Closure MessageLoopRunner::QuitClosure() {
    204   return base::Bind(&MessageLoopRunner::Quit, this);
    205 }
    206 
    207 void MessageLoopRunner::Quit() {
    208   quit_closure_called_ = true;
    209 
    210   // Only run the quit task if we are running the message loop.
    211   if (loop_running_) {
    212     GetQuitTaskForRunLoop(&run_loop_).Run();
    213     loop_running_ = false;
    214   }
    215 }
    216 
    217 WindowedNotificationObserver::WindowedNotificationObserver(
    218     int notification_type,
    219     const NotificationSource& source)
    220     : seen_(false),
    221       running_(false),
    222       source_(NotificationService::AllSources()) {
    223   AddNotificationType(notification_type, source);
    224 }
    225 
    226 WindowedNotificationObserver::WindowedNotificationObserver(
    227     int notification_type,
    228     const ConditionTestCallback& callback)
    229     : seen_(false),
    230       running_(false),
    231       callback_(callback),
    232       source_(NotificationService::AllSources()) {
    233   AddNotificationType(notification_type, source_);
    234 }
    235 
    236 WindowedNotificationObserver::WindowedNotificationObserver(
    237     int notification_type,
    238     const ConditionTestCallbackWithoutSourceAndDetails& callback)
    239     : seen_(false),
    240       running_(false),
    241       callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
    242       source_(NotificationService::AllSources()) {
    243   registrar_.Add(this, notification_type, source_);
    244 }
    245 
    246 WindowedNotificationObserver::~WindowedNotificationObserver() {}
    247 
    248 void WindowedNotificationObserver::AddNotificationType(
    249     int notification_type,
    250     const NotificationSource& source) {
    251   registrar_.Add(this, notification_type, source);
    252 }
    253 
    254 void WindowedNotificationObserver::Wait() {
    255   if (seen_)
    256     return;
    257 
    258   running_ = true;
    259   message_loop_runner_ = new MessageLoopRunner;
    260   message_loop_runner_->Run();
    261   EXPECT_TRUE(seen_);
    262 }
    263 
    264 void WindowedNotificationObserver::Observe(
    265     int type,
    266     const NotificationSource& source,
    267     const NotificationDetails& details) {
    268   source_ = source;
    269   details_ = details;
    270   if (!callback_.is_null() && !callback_.Run(source, details))
    271     return;
    272 
    273   seen_ = true;
    274   if (!running_)
    275     return;
    276 
    277   message_loop_runner_->Quit();
    278   running_ = false;
    279 }
    280 
    281 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
    282     : child_thread_count_(0) {
    283   RenderProcessHost::SetRunRendererInProcess(true);
    284   BrowserChildProcessObserver::Add(this);
    285 }
    286 
    287 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
    288   if (child_thread_count_) {
    289     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
    290     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
    291     runner_ = new MessageLoopRunner;
    292     runner_->Run();
    293   }
    294   BrowserChildProcessObserver::Remove(this);
    295   RenderProcessHost::SetRunRendererInProcess(false);
    296 }
    297 
    298 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
    299     const ChildProcessData& data) {
    300   child_thread_count_++;
    301 }
    302 
    303 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
    304     const ChildProcessData& data) {
    305   if (--child_thread_count_)
    306     return;
    307 
    308   if (runner_.get())
    309     runner_->Quit();
    310 }
    311 
    312 }  // namespace content
    313